Merge changes from topic "presubmit-am-a3e3a46016284fa6bec11af293474cfb" into sc-qpr1-dev am: ae4b713b2f

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

Change-Id: I2bf6582bd4fdbb647a3795a8e1311995a3e3f2e8
diff --git a/Android.bp b/Android.bp
index 45d022f..bab994a 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",
@@ -149,9 +152,13 @@
     ],
     srcs: [
         "src/**/*.java",
+        "src/**/*.kt",
         "src_shortcuts_overrides/**/*.java",
+        "src_shortcuts_overrides/**/*.kt",
         "src_ui_overrides/**/*.java",
+        "src_ui_overrides/**/*.kt",
         "ext_tests/src/**/*.java",
+        "ext_tests/src/**/*.kt",
     ],
     resource_dirs: [
         "ext_tests/res",
@@ -192,8 +199,12 @@
     resource_dirs: [
         "quickstep/res",
     ],
+    libs: [
+        "framework-statsd",
+    ],
     static_libs: [
         "Launcher3ResLib",
+        "lottie",
         "SystemUISharedLib",
         "SystemUI-statsd",
     ],
@@ -205,7 +216,10 @@
 // Source code used for test helpers
 filegroup {
     name: "launcher-src-ext-tests",
-    srcs: ["ext_tests/src/**/*.java"],
+    srcs: [
+        "ext_tests/src/**/*.java",
+        "ext_tests/src/**/*.kt",
+    ],
 }
 
 // Common source files used to build launcher
@@ -213,8 +227,11 @@
     name: "launcher-src-no-build-config",
     srcs: [
         "src/**/*.java",
+        "src/**/*.kt",
         "src_shortcuts_overrides/**/*.java",
+        "src_shortcuts_overrides/**/*.kt",
         "quickstep/src/**/*.java",
+        "quickstep/src/**/*.kt",
     ],
 }
 
@@ -224,20 +241,27 @@
     srcs: ["proguard.flags"],
 }
 
-
 // Library with all the dependencies for building Launcher Go
 android_library {
     name: "LauncherGoResLib",
     srcs: [
         "src/**/*.java",
+        "src/**/*.kt",
         "quickstep/src/**/*.java",
+        "quickstep/src/**/*.kt",
         "go/src/**/*.java",
+        "go/src/**/*.kt",
         "go/quickstep/src/**/*.java",
+        "go/quickstep/src/**/*.kt",
     ],
     resource_dirs: [
         "go/res",
         "go/quickstep/res",
     ],
+    // Note the ordering here is important when it comes to resource
+    // overriding. We want the most specific resource overrides defined
+    // in QuickstepResLib to take precendece, so it should be the final
+    // dependency. See b/205278434 for how this can go wrong.
     static_libs: [
         "Launcher3CommonDepsLib",
         "QuickstepResLib",
@@ -253,3 +277,30 @@
     },
 }
 
+// Build rule for Quickstep library
+android_library {
+    name: "Launcher3QuickStepLib",
+    srcs: [
+        ":launcher-src-no-build-config",
+    ],
+    resource_dirs: [],
+    libs: [
+        "framework-statsd",
+    ],
+    // Note the ordering here is important when it comes to resource
+    // overriding. We want the most specific resource overrides defined
+    // in QuickstepResLib to take precendece, so it should be the final
+    // dependency. See b/208647810 for how this can go wrong.
+    static_libs: [
+        "SystemUI-statsd",
+        "SystemUISharedLib",
+        "Launcher3CommonDepsLib",
+        "QuickstepResLib",
+    ],
+    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..ceaaf13 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)
@@ -141,7 +105,7 @@
   LOCAL_SDK_VERSION := system_current
   LOCAL_MIN_SDK_VERSION := 26
 endif
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
+LOCAL_STATIC_ANDROID_LIBRARIES := LauncherGoResLib
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
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..b838a51 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"
@@ -49,7 +50,7 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
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..683a4cf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,6 @@
     repositories {
         mavenCentral()
         google()
-        jcenter()
     }
     dependencies {
         classpath GRADLE_CLASS_PATH
@@ -164,6 +163,8 @@
     androidTestImplementation 'com.android.support.test:rules:1.0.0'
     androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
     androidTestImplementation "androidx.annotation:annotation:${ANDROID_X_VERSION}"
+
+    api 'com.airbnb.android:lottie:3.3.0'
 }
 
 protobuf {
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/AndroidManifest-launcher.xml b/go/AndroidManifest-launcher.xml
index 6a8f715..2223036 100644
--- a/go/AndroidManifest-launcher.xml
+++ b/go/AndroidManifest-launcher.xml
@@ -49,7 +49,7 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density|uiMode"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
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..d4eca2f 100644
--- a/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
@@ -35,6 +35,7 @@
  */
 public class GoOverviewActionsView extends OverviewActionsView<OverlayUICallbacksGo> {
 
+    @Nullable
     private ArrowTipView mArrowTipView;
 
     public GoOverviewActionsView(Context context) {
@@ -117,7 +118,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..c559988 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -59,6 +59,8 @@
     SettingsContainer settings_container = 9;
     PredictedHotseatContainer predicted_hotseat_container = 10;
     TaskSwitcherContainer task_switcher_container = 11;
+    TaskBarContainer task_bar_container = 12;
+    WallpapersContainer wallpapers_container = 13;
     ExtendedContainers extended_containers = 20;
   }
 }
@@ -100,6 +102,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 +153,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,9 +244,16 @@
   oneof ParentContainer {
     WorkspaceContainer workspace = 4;
     HotseatContainer hotseat = 5;
+    TaskBarContainer taskbar = 6;
   }
 }
 
+// Represents wallpapers container for quick switching.
+message WallpapersContainer {
+  // Number of wallpapers in the container.
+  optional int32 cardinality = 1;
+}
+
 // Represents state of EditText field before update.
 enum FromState {
   // Default value.
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-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 6808222..53910e3 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -49,7 +49,7 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 003f895..352cd3e 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -28,6 +28,7 @@
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
     <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS"/>
     <uses-permission android:name="android.permission.REMOVE_TASKS"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
     <uses-permission android:name="android.permission.STATUS_BAR"/>
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
@@ -66,7 +67,7 @@
              android:stateNotNeeded="true"
              android:theme="@style/LauncherTheme"
              android:screenOrientation="unspecified"
-             android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
+             android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
              android:resizeableActivity="true"
              android:resumeWhilePausing="true"
              android:taskAffinity=""/>
@@ -101,7 +102,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"/>
@@ -117,7 +117,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..fb014f7
--- /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="24dp" />
+            <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_screenshot.xml b/quickstep/res/drawable/ic_screenshot.xml
index d97eae1..9ee6c44 100644
--- a/quickstep/res/drawable/ic_screenshot.xml
+++ b/quickstep/res/drawable/ic_screenshot.xml
@@ -13,11 +13,20 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="20"
+    android:viewportHeight="20">
   <path
-      android:fillColor="#FF000000"
-      android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,21L7,21v-1h10v1zM17,18L7,18L7,6h10v12zM17,4L7,4L7,3h10v1zM9.5,8.5L12,8.5L12,7L8,7v4h1.5zM12,17h4v-4h-1.5v2.5L12,15.5z"/>
-</vector>
+      android:pathData="M5.8334,1.666H8.3334V3.3327H5.8334V6.666H4.1667V3.3327C4.1667,2.4122 4.9129,1.666 5.8334,1.666Z"
+      android:fillColor="#000000"/>
+  <path
+      android:pathData="M4.1667,13.3327V16.666C4.1667,17.5865 4.9129,18.3327 5.8334,18.3327H8.3334V16.666H5.8334V13.3327H4.1667Z"
+      android:fillColor="#000000"/>
+  <path
+      android:pathData="M14.1667,13.3327V16.666H11.6667V18.3327H14.1667C15.0872,18.3327 15.8334,17.5865 15.8334,16.666V13.3327H14.1667Z"
+      android:fillColor="#000000"/>
+  <path
+      android:pathData="M15.8334,6.666V3.3327C15.8334,2.4122 15.0872,1.666 14.1667,1.666H11.6667V3.3327H14.1667V6.666H15.8334Z"
+      android:fillColor="#000000"/>
+</vector>
\ No newline at end of file
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/task_menu_item_bg.xml b/quickstep/res/drawable/task_menu_item_bg.xml
index b6a8b90..16c13eb 100644
--- a/quickstep/res/drawable/task_menu_item_bg.xml
+++ b/quickstep/res/drawable/task_menu_item_bg.xml
@@ -15,7 +15,8 @@
      limitations under the License.
 -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="?android:attr/colorPrimary"/>
-    <corners android:radius="@dimen/task_menu_item_corner_radius"/>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <solid android:color="?androidprv:attr/colorSurface" />
+    <corners android:radius="@dimen/task_menu_item_corner_radius" />
 </shape>
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/drawable/taskbar_icon_click_feedback_roundrect.xml b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
index d6160de..534f241 100644
--- a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
+++ b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
@@ -16,7 +16,7 @@
   -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/taskbar_icon_selection_ripple">
+    android:color="@color/taskbar_nav_icon_selection_ripple">
     <item android:id="@android:id/mask">
         <shape android:shape="rectangle">
             <solid android:color="@android:color/white" />
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index e79e57e..9ad10dc 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -14,68 +14,111 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout
+    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"
-    android:paddingEnd="@dimen/allset_page_margin_horizontal"
-    android:layoutDirection="locale"
-    android:textDirection="locale">
+    android:id="@+id/root_view"
+    android:background="@color/all_set_page_background" >
 
-    <LinearLayout
+    <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_gravity="start"
-        android:gravity="start"
-        android:orientation="vertical">
+        android:layout_height="match_parent"
+        android:id="@+id/content_view"
+        android:fitsSystemWindows="true">
 
-        <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/title"
-            style="@style/TextAppearance.GestureTutorial.Feedback.Title"
+        <com.airbnb.lottie.LottieAnimationView
+            android:id="@+id/animated_background"
             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"/>
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:scaleType="centerCrop"
 
-        <TextView
-            android:id="@+id/subtitle"
-            style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+            app:lottie_rawRes="@raw/all_set_page_bg"
+            app:lottie_autoPlay="true"
+            app:lottie_loop="true" />
+
+        <androidx.constraintlayout.widget.ConstraintLayout
             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>
+            android:layout_height="match_parent"
+            android:layout_marginStart="@dimen/allset_page_margin_horizontal"
+            android:layout_marginEnd="@dimen/allset_page_margin_horizontal"
+            android:layoutDirection="locale"
+            android:textDirection="locale"
+            android:forceHasOverlappingRendering="false"
+            android:fitsSystemWindows="true" >
 
-    <TextView
-        android:id="@+id/navigation_settings"
-        style="@style/TextAppearance.GestureTutorial.LinkText"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_above="@+id/hint"
-        android:gravity="center"
-        android:layout_marginBottom="72dp"
-        android:minHeight="48dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:text="@string/allset_navigation_settings" />
+            <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/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"
-        android:text="@string/allset_hint"/>
-</RelativeLayout>
+            <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"
+                app:layout_constraintTop_toBottomOf="@id/icon"
+                app:layout_constraintStart_toStartOf="parent"
+                android:gravity="start"
+                android:text="@string/allset_title"/>
+
+            <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"/>
+
+            <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="wrap_content"
+                android:layout_height="wrap_content"
+                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"
+                style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+                android:textSize="14sp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
+                android:text="@string/allset_hint"/>
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index a43296f..bfeb82d 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -45,8 +45,7 @@
             android:layout_height="match_parent"
             android:clipChildren="false"
             android:clipToPadding="false"
-            android:outlineProvider="none"
-            android:theme="@style/HomeScreenElementTheme" />
+            android:outlineProvider="none" />
 
         <include
             android:id="@+id/overview_actions_view"
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..b0cc00b
--- /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="@dimen/gesture_tutorial_mock_taskbar_height"
+
+        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..e5cd9bc
--- /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="@dimen/gesture_tutorial_mock_taskbar_height"
+
+        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_taskbar.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml
new file mode 100644
index 0000000..ddfeeec
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.quickstep.interaction.AnimatedTaskbarView
+    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="@dimen/gesture_tutorial_mock_taskbar_height">
+
+    <View
+        android:id="@+id/taskbar_background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/gesture_tutorial_taskbar_color"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/icon_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+
+        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/taskbar_icon_1"
+            android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+            android:layout_marginStart="@dimen/gesture_tutorial_taskbar_padding_start_end"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_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_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/taskbar_icon_2"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/taskbar_icon_2"
+            android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_app_icon_2"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@id/taskbar_icon_1"
+            app:layout_constraintEnd_toStartOf="@id/taskbar_icon_3"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/taskbar_icon_3"
+            android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_app_icon_3"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@id/taskbar_icon_2"
+            app:layout_constraintEnd_toStartOf="@id/taskbar_icon_4"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/taskbar_icon_4"
+            android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_app_icon_1"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@id/taskbar_icon_3"
+            app:layout_constraintEnd_toStartOf="@id/taskbar_icon_5"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/taskbar_icon_5"
+            android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_app_icon_4"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@id/taskbar_icon_4"
+            app:layout_constraintEnd_toStartOf="@id/taskbar_icon_6"/>
+
+        <androidx.cardview.widget.CardView
+            android:id="@+id/taskbar_icon_6"
+            android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+            android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+            android:layout_marginEnd="@dimen/gesture_tutorial_taskbar_padding_start_end"
+
+            app:cardElevation="0dp"
+            app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+            app:cardBackgroundColor="@color/mock_app_icon_2"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@id/taskbar_icon_5"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</com.android.quickstep.interaction.AnimatedTaskbarView>
\ 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..08e6178 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"
@@ -61,8 +101,17 @@
         android:layout_height="match_parent"
         android:background="@drawable/gesture_tutorial_ripple"/>
 
+    <include
+        layout="@layout/gesture_tutorial_foldable_mock_taskbar"
+        android:id="@+id/gesture_tutorial_fake_taskbar_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/gesture_tutorial_mock_taskbar_height"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentEnd="true" />
+
     <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 +122,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 +136,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..0fda0bf 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"
@@ -39,6 +38,20 @@
             android:layout_height="wrap_content"
             android:drawableStart="@drawable/ic_screenshot"
             android:text="@string/action_screenshot"
+            android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
+        <Space
+            android:id="@+id/action_split_space"
+            android:layout_width="@dimen/overview_actions_button_spacing"
+            android:layout_height="1dp"
+            android:visibility="gone" />
+
+        <Button
+            android:id="@+id/action_split"
+            style="@style/OverviewActionButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/action_split"
             android:theme="@style/ThemeControlHighlightWorkspaceColor"
             android:visibility="gone" />
 
@@ -47,16 +60,6 @@
             android:layout_height="1dp"
             android:layout_weight="1" />
 
-        <Button
-            android:id="@+id/action_share"
-            style="@style/OverviewActionButton"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:drawableStart="@drawable/ic_share"
-            android:text="@string/action_share"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor"
-            android:visibility="gone" />
-
         <Space
             android:id="@+id/oav_three_button_space"
             android:layout_width="0dp"
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/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index f303f31..01d675f 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -25,13 +25,6 @@
         android:clipToPadding="false"
         android:visibility="invisible" />
 
-    <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/darker_gray"
-        android:visibility="gone" />
-
     <include
         android:id="@+id/overview_actions_view"
         layout="@layout/overview_actions_container" />
diff --git a/quickstep/res/layout/predicted_hotseat_edu.xml b/quickstep/res/layout/predicted_hotseat_edu.xml
index 1dab482..e4e3956 100644
--- a/quickstep/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/res/layout/predicted_hotseat_edu.xml
@@ -73,6 +73,7 @@
                 launcher:containerType="hotseat" />
 
             <LinearLayout
+                android:id="@+id/button_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:paddingLeft="@dimen/bottom_sheet_edu_padding"
diff --git a/quickstep/res/layout/rotate_suggestion.xml b/quickstep/res/layout/rotate_suggestion.xml
new file mode 100644
index 0000000..07cf0c8
--- /dev/null
+++ b/quickstep/res/layout/rotate_suggestion.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
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+    <com.android.systemui.shared.rotation.FloatingRotationButtonView
+        android:id="@+id/rotate_suggestion"
+        android:layout_width="@dimen/floating_rotation_button_diameter"
+        android:layout_height="@dimen/floating_rotation_button_diameter"
+        android:paddingStart="@dimen/navigation_key_padding"
+        android:paddingEnd="@dimen/navigation_key_padding"
+        android:layout_gravity="bottom|left"
+        android:scaleType="center"
+        android:visibility="invisible" />
+</FrameLayout>
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/task_menu_with_arrow.xml b/quickstep/res/layout/task_menu_with_arrow.xml
new file mode 100644
index 0000000..38573fd
--- /dev/null
+++ b/quickstep/res/layout/task_menu_with_arrow.xml
@@ -0,0 +1,33 @@
+<?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.quickstep.views.TaskMenuViewWithArrow
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:animateLayoutChanges="true"
+    android:background="@drawable/task_menu_bg"
+    android:orientation="vertical"
+    android:visibility="invisible">
+
+    <LinearLayout
+        android:id="@+id/menu_option_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:showDividers="middle" />
+
+</com.android.quickstep.views.TaskMenuViewWithArrow>
\ No newline at end of file
diff --git a/quickstep/res/layout/task_view_menu_option.xml b/quickstep/res/layout/task_view_menu_option.xml
index 5978b97..8a8fc36 100644
--- a/quickstep/res/layout/task_view_menu_option.xml
+++ b/quickstep/res/layout/task_view_menu_option.xml
@@ -18,7 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
+    android:orientation="horizontal"
     android:paddingTop="@dimen/task_card_menu_option_vertical_padding"
     android:paddingBottom="@dimen/task_card_menu_option_vertical_padding"
     android:background="@drawable/task_menu_item_bg"
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/res/drawable/personal_work_tabs_ripple.xml b/quickstep/res/layout/taskbar_contextual_button.xml
similarity index 66%
rename from res/drawable/personal_work_tabs_ripple.xml
rename to quickstep/res/layout/taskbar_contextual_button.xml
index 2e57b80..4ffb8d8 100644
--- a/res/drawable/personal_work_tabs_ripple.xml
+++ b/quickstep/res/layout/taskbar_contextual_button.xml
@@ -13,10 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <shape android:shape="rectangle">
-        <solid android:color="@android:color/transparent" />
-        <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-    </shape>
-</ripple>
\ No newline at end of file
+<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/res/drawable/personal_work_tabs_ripple.xml b/quickstep/res/layout/taskbar_nav_button.xml
similarity index 60%
copy from res/drawable/personal_work_tabs_ripple.xml
copy to quickstep/res/layout/taskbar_nav_button.xml
index 2e57b80..aea4885 100644
--- a/res/drawable/personal_work_tabs_ripple.xml
+++ b/quickstep/res/layout/taskbar_nav_button.xml
@@ -13,10 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <shape android:shape="rectangle">
-        <solid android:color="@android:color/transparent" />
-        <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-    </shape>
-</ripple>
\ No newline at end of file
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    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"
+    android:tint="@color/taskbar_nav_icon_light_color"
+    tools:ignore="UseAppTint" />
\ No newline at end of file
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 433ef86..a842d3c 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programgebruikinstellings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Vee alles uit"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Onlangse programme"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Taak is toegemaak"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Program steun nie verdeelde skerm nie."</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="accessibility_rotate_button" msgid="4771825231336502943">"Draai skerm"</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_switch_apps" msgid="6942863327845784813">"Gebruik die taakbalk om tussen programme te wissel"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Sleep na die kant om twee programme gelyktydig te gebruik"</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..ce14b54 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"የቅርብ ጊዜ መተግበሪያዎች"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ተግባር ተዘግቷል"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>፣ <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</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="accessibility_rotate_button" msgid="4771825231336502943">"ማያ ገጹን አዙር"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"የተግባር አሞሌ ትምህርት ይታያል"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"የተግባር አሞሌ ትምህርት ተዘግቷል"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"መተግበሪያዎችን ለመቀየር የተግባር አሞሌውን ይጠቀሙ"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"በአንድ ጊዜ ሁለት መተግበሪያዎችን ለመጠቀም ወደ ጎን ይጎትቱ"</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..df51dab 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"إعدادات استخدام التطبيق"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"محو الكل"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"التطبيقات المستخدمة مؤخرًا"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"تم إغلاق المهمة."</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"التطبيق لا يتيح تقسيم الشاشة."</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="accessibility_rotate_button" msgid="4771825231336502943">"تدوير الشاشة"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ظهرت لوحة تعليم استخدام شريط المهام."</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"تم إغلاق لوحة تعليم استخدام شريط المهام."</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"يمكنك استخدام شريط المهام للتبديل بين التطبيقات."</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"اسحبه إلى جانب الشاشة لاستخدام تطبيقين في آنٍ واحد."</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 110267f..62c472e 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"এপে ব্যৱহাৰ কৰা ডেটাৰ ছেটিং"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"আটাইবোৰ মচক"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"শেহতীয়া এপসমূহ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"কাৰ্য বন্ধ কৰা হ’ল"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"এপ্‌টোৱে বিভাজিত স্ক্ৰীন সমৰ্থন নকৰে।"</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="accessibility_rotate_button" msgid="4771825231336502943">"স্ক্ৰীনখন ঘূৰাওক"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"টাস্কবাৰৰ শিক্ষাৰ পেনেলটো প্ৰদর্শিত হৈছে"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"টাস্কবাৰৰ শিক্ষাৰ পেনেলটো বন্ধ হৈছে"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"এপ্‌ সলনি কৰিবলৈ টাস্কবাৰডাল ব্যৱহাৰ কৰক"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"এবাৰতে দুটা এপ্‌ ব্যৱহাৰ কৰিবলৈ কাষলৈ টানি আনি এৰক"</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..91d09f5 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tətbiq istifadə ayarları"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Hamısını silin"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Son tətbiqlər"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tapşırıq bağlanıb"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Tətbiq ekran bölünməsini dəstəkləmir."</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="accessibility_rotate_button" msgid="4771825231336502943">"Ekranı fırladı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_switch_apps" msgid="6942863327845784813">"Tətbiqləri keçirmək üçün tapşırıq panelindən istifadə edin"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Eyni anda iki tətbiqi istifadə etmək üçün yan tərəfə çəkin"</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..92fd21d 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Podešavanja korišćenja aplikacije"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Zadatak je zatvoren"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacija ne podržava 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotirajte ekran"</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_switch_apps" msgid="6942863327845784813">"Koristite traku zadataka da biste menjali aplikacije"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Prevucite na stranu da koristite dve aplikacije odjednom"</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..eae4ba1 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налады выкарыстання праграмы"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Ачысціць усё"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Нядаўнія праграмы"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Задача закрыта"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Праграма не падтрымлівае рэжым падзеленага экрана."</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="accessibility_rotate_button" msgid="4771825231336502943">"Павярнуць экран"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"З\'явілася панэль навучання на панэлі задач"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Панэль навучання на панэлі задач закрыта"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Выкарыстоўвайце панэль задач для пераключэння праграм"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Перацягніце ўбок, каб адначасова скарыстаць дзве праграмы"</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..fd9a3cd 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки за използването на приложенията"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Изчистване на всички"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Скорошни приложения"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Задачата е затворена"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Приложението не поддържа разделен екран."</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="accessibility_rotate_button" msgid="4771825231336502943">"Завъртане на екрана"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Показва се урокът за лентата на задачите"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Урокът за лентата на задачите бе затворен"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Използвайте лентата на задачите за превключване между прил."</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Плъзнете встрани, за да използвате едновременно 2 приложения"</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..799fe0b 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"অ্যাপ ব্যবহারের সেটিংস"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"সবকিছু খালি করুন"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"সম্প্রতি ব্যবহৃত অ্যাপ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"টাস্ক বন্ধ করা হয়েছে"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"স্প্লিট-স্ক্রিনে এই অ্যাপ কাজ করে না।"</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="accessibility_rotate_button" msgid="4771825231336502943">"স্ক্রিন ঘোরান"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"টাস্কবার এডুকেশন দেখানো হয়েছে"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"টাস্কবার এডুকেশন বন্ধ করা আছে"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"অ্যাপ পাল্টানোর জন্য টাস্কবার ব্যবহার করুন"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"একসাথে দুটি অ্যাপ ব্যবহার করতে পাশে টেনে আনুন"</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..3e4933f 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke korištenja aplikacije"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Zadatak je zatvoren"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacija ne podržava 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotiranje ekrana"</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_switch_apps" msgid="6942863327845784813">"Koristite programsku traku da promijenite aplikacije"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Prevucite u stranu da istovremeno koristite dvije aplikacije"</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..2e57be3 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuració d\'ús d\'aplicacions"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Esborra-ho tot"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacions recents"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tasca tancada"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>; <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"L\'aplicació no admet la pantalla dividida."</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="accessibility_rotate_button" msgid="4771825231336502943">"Gira la pantalla"</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_switch_apps" msgid="6942863327845784813">"Utilitza la barra de tasques per canviar d\'aplicació"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arrossega al costat per utilitzar dues aplicacions alhora"</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..272973f 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavení využití aplikací"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Vymazat vše"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Poslední aplikace"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Úkol byl zavřen"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikace nepodporuje režim rozdělené obrazovky."</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="accessibility_rotate_button" msgid="4771825231336502943">"Otočit obrazovku"</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_switch_apps" msgid="6942863327845784813">"Aplikace lze přepínat pomocí hlavního panelu"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Po přetažení na stranu lze používat dvě aplikace současně"</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..2747cc0 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Indstillinger for appforbrug"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Ryd alt"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Seneste apps"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Opgaven er lukket"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Appen understøtter ikke 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="accessibility_rotate_button" msgid="4771825231336502943">"Roter skærm"</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_switch_apps" msgid="6942863327845784813">"Skift mellem apps ved hjælp af proceslinjen"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Træk til siden for at bruge to apps samtidig"</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..2004849 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Einstellungen zur App-Nutzung"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Alle Apps schließen"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Kürzlich geöffnete Apps"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Aufgabe geschlossen"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"„Bildschirm teilen“ wird von der App nicht unterstützt."</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="accessibility_rotate_button" msgid="4771825231336502943">"Bildschirm drehen"</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_switch_apps" msgid="6942863327845784813">"Über die Taskleiste zwischen Apps wechseln"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Zur Seite ziehen, um zwei Apps gleichzeitig zu verwenden"</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..e1f709d 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ρυθμίσεις χρήσης εφαρμογής"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Διαγραφή όλων"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Πρόσφατες εφαρμογές"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Η εργασία έκλεισε"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</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="accessibility_rotate_button" msgid="4771825231336502943">"Περιστροφή οθόνης"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Η εκπαίδευση για τη γραμμή εργασιών εμφανίστηκε"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Η εκπαίδευση για τη γραμμή εργασιών έκλεισε"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Χρήση της γραμμής εργασιών για εναλλαγή εφαρμογών"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Σύρετε στο πλάι για ταυτόχρονη χρήση δύο εφαρμογών"</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..487ea72 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Task closed"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</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_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</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..487ea72 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Task closed"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</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_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</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..487ea72 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Task closed"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</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_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</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..487ea72 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Task closed"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"App does not support 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotate screen"</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_switch_apps" msgid="6942863327845784813">"Use the taskbar to switch apps"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Drag to the side to use two apps at once"</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..fa9e467 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‎App usage settings‎‏‎‎‏‎"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‏‏‎Clear all‎‏‎‎‏‎"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎Recent apps‎‏‎‎‏‎"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎Task Closed‎‏‎‎‏‎"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="REMAINING_TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‎‎App does not support split-screen.‎‏‎‎‏‎"</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="accessibility_rotate_button" msgid="4771825231336502943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‏‎Rotate screen‎‏‎‎‏‎"</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_switch_apps" msgid="6942863327845784813">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎Use the taskbar to switch apps‎‏‎‎‏‎"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎Drag to the side to use two apps at once‎‏‎‎‏‎"</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..e3f3cdf 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración de uso de la app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recientes"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Se cerró la tarea"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"La app no es compatible con la función de 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="accessibility_rotate_button" msgid="4771825231336502943">"Girar pantalla"</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_switch_apps" msgid="6942863327845784813">"Usa la barra de tareas para cambiar de app"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arrastra a un lado para usar dos apps a la vez"</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..d858e11 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ajustes de uso de la aplicación"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicaciones recientes"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tarea cerrada"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"La aplicación no admite 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="accessibility_rotate_button" msgid="4771825231336502943">"Girar la pantalla"</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_switch_apps" msgid="6942863327845784813">"Usa la barra de tareas para cambiar de aplicación"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arrastra hacia un lado para usar dos aplicaciones a la vez"</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..6752384 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Rakenduse kasutuse seaded"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Sule kõik"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Hiljutised rakendused"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Ülesanne suleti"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Rakendus ei toeta jagatud ekraani."</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="accessibility_rotate_button" msgid="4771825231336502943">"Pöörake ekraani"</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_switch_apps" msgid="6942863327845784813">"Kasutage rakenduste vahetamiseks tegumiriba"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Kahe rakenduse korraga kasutamiseks lohistage külje poole"</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..1858625 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Aplikazioen erabileraren ezarpenak"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Garbitu guztiak"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Azken aplikazioak"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Itxi da zeregina"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikazioak ez du onartzen pantaila zatitua."</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="accessibility_rotate_button" msgid="4771825231336502943">"Biratu pantaila"</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_switch_apps" msgid="6942863327845784813">"Erabili zereginen barra aplikazioz aldatzeko"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Bi aplikazio batera erabiltzeko, arrastatu hatza albo batera"</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..3ba4272 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"تنظیمات استفاده از برنامه"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"پاک کردن همه"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"برنامه‌های اخیر"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"تکلیف بسته شد"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"برنامه از صفحهٔ دونیمه پشتیبانی نمی‌کند."</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="accessibility_rotate_button" msgid="4771825231336502943">"چرخاندن صفحه"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"پانل آموزشی نوار وظیفه نمایان شد"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"پانل آموزشی نوار وظیفه بسته شد"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"برای جابه‌جایی بین برنامه‌ها، از نوار وظیفه استفاده کنید"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"برای استفاده هم‌زمان از دو برنامه، آن را به کنار بکشید"</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..9244c6b 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Sovelluksen käyttöasetukset"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Poista kaikki"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Viimeisimmät sovellukset"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tehtävä suljettu"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Sovellus ei tue jaetun näytön tilaa."</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="accessibility_rotate_button" msgid="4771825231336502943">"Käännä näyttö"</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_switch_apps" msgid="6942863327845784813">"Vaihda sovellusta tehtäväpalkin kautta"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Vetämällä sivuun voit käyttää kahta sovellusta samaan aikaan"</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..0ba184f 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres d\'utilisation de l\'application"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tâche fermée"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> : <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"L\'appli n\'est pas compatible avec l\'écran partagé."</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="accessibility_rotate_button" msgid="4771825231336502943">"Faire pivoter l\'écran"</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_switch_apps" msgid="6942863327845784813">"Utilisez la barre des tâches pour changer les applications"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Glissez sur le côté pour utiliser 2 applications à la fois"</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..a33a751 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres de consommation de l\'application"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tâche fermée"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Appli incompatible avec l\'é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="accessibility_rotate_button" msgid="4771825231336502943">"Faire pivoter l\'écran"</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_switch_apps" msgid="6942863327845784813">"Utilisez la barre des tâches pour changer d\'application"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Faites glisser sur côté pour utiliser deux applis à la fois"</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..234d611 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración do uso de aplicacións"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tarefa pechada"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"A app non admite a función de pantalla dividida."</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="accessibility_rotate_button" msgid="4771825231336502943">"Xira a pantalla"</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_switch_apps" msgid="6942863327845784813">"Usa a barra de ferramentas para cambiar de aplicación"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Para usar dúas aplicacións á vez, arrastra cara ao lado"</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..4b2e177 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ઍપ વપરાશનું સેટિંગ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"બધું સાફ કરો"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"તાજેતરની ઍપ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"કાર્ય બંધ કર્યું"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"ઍપ સ્ક્રીન-વિભાજનને સપોર્ટ કરતી નથી."</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="accessibility_rotate_button" msgid="4771825231336502943">"સ્ક્રીન ફેરવો"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ટાસ્કબારનું શિક્ષણ આપતી પૅનલ દેખાય છે"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ટાસ્કબારનું શિક્ષણ આપતી પૅનલ બંધ થઈ છે"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ઍપ સ્વિચ કરવા માટે, ટાસ્કબારનો ઉપયોગ કરો"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"એક જ સમયે બે ઍપનો ઉપયોગ કરવા માટે, ખેંચીને બાજુ પર લઈ જાઓ"</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..dbb6c71 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ऐप्लिकेशन इस्तेमाल की सेटिंग"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"सभी ऐप्लिकेशन बंद करें"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"टास्क बंद किया गया"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"यह ऐप्लिकेशन, स्प्लिट स्क्रीन पर काम नहीं करता है."</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="accessibility_rotate_button" msgid="4771825231336502943">"स्क्रीन घुमाएं"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबार ट्यूटोरियल दिखाया गया"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबार ट्यूटोरियल बंद किया गया"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ऐप्लिकेशन स्विच करने के लिए, टास्कबार का इस्तेमाल करें"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"एक साथ दो ऐप्लिकेशन इस्तेमाल करने के लिए, उन्हें किनारे की ओर खींचें और छोड़ें"</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..32876a0 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke upotrebe aplikacija"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Izbriši sve"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Zadatak je zatvoren"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacija ne podržava 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="accessibility_rotate_button" msgid="4771825231336502943">"Zakretanje zaslona"</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_switch_apps" msgid="6942863327845784813">"Upotrijebite program. traku da biste promijenili aplikaciju"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Povucite u stranu da biste istovremeno koristili dvije aplikacije"</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..b0e53ce 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Alkalmazáshasználati beállítások"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Összes törlése"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Legutóbbi alkalmazások"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"A feladat bezárult"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Az alkalmazás nem támogatja az osztott képernyőt."</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="accessibility_rotate_button" msgid="4771825231336502943">"Képernyő elforgatása"</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_switch_apps" msgid="6942863327845784813">"Az eszköztárral válthat az alkalmazások között"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Húzza oldalra, ha egyszerre két appot szeretne használni"</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..a2e498f 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Հավելվածի օգտագործման կարգավորումներ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Փակել բոլորը"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Վերջին օգտագործած հավելվածները"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Առաջադրանքը փակված է"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Հավելվածը չի աջակցում էկրանի տրոհումը։"</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="accessibility_rotate_button" msgid="4771825231336502943">"Պտտել էկրանը"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Խնդրագոտու «Կրթություն» վահանակը բացվեց"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Խնդրագոտու «Կրթություն» վահանակը փակվեց"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Օգտագործեք խնդրագոտին՝ մի հավելվածից մյուսին անցնելու համար"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Քաշեք մի կողմ՝ միաժամանակ երկու հավելված օգտագործելու համար"</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..47b4c57 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setelan penggunaan aplikasi"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Hapus semua"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikasi terbaru"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tugas Ditutup"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikasi tidak mendukung 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="accessibility_rotate_button" msgid="4771825231336502943">"Putar layar"</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_switch_apps" msgid="6942863327845784813">"Gunakan taskbar untuk beralih aplikasi"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Tarik ke samping untuk menggunakan dua aplikasi sekaligus"</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..22099e4 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Notkunarstillingar forrits"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Hreinsa allt"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nýleg forrit"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Verkefni lokað"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Forritið styður ekki að skjánum sé skipt."</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="accessibility_rotate_button" msgid="4771825231336502943">"Snúa skjánum"</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_switch_apps" msgid="6942863327845784813">"Notaðu verkefnastikuna til að skipta á milli forrita"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Dragðu til hliðar til að nota tvö forrit í einu"</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..0d91ccf 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Impostazioni di utilizzo delle app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Cancella tutto"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"App recenti"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Attività chiusa"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"L\'app non supporta la modalità 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="accessibility_rotate_button" msgid="4771825231336502943">"Ruota lo schermo"</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_switch_apps" msgid="6942863327845784813">"Usa la barra delle applicazioni per cambiare app"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Trascina di lato per usare due app contemporaneamente"</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..4feb9ee 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"הגדרות שימוש באפליקציה"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ניקוי הכול"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"אפליקציות אחרונות"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"המשימה סגורה"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"האפליקציה אינה תומכת במסך מפוצל."</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="accessibility_rotate_button" msgid="4771825231336502943">"סיבוב המסך"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"חלונית ההסברים על שורת המשימות מופיעה"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"חלונית ההסברים על שורת המשימות נסגרה"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"כדי לעבור בין אפליקציות, משתמשים בשורת המשימות"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"כדי להשתמש בשתי אפליקציות בו-זמנית, צריך לגרור לצד"</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..a88f621 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"アプリの使用状況の設定"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"すべてクリア"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使ったアプリ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"タスクを閉じました"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>、<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"アプリで分割画面がサポートされていません。"</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="accessibility_rotate_button" msgid="4771825231336502943">"画面を回転"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"タスクバーの説明を開きました"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"タスクバーの説明を閉じました"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"アプリを切り替えるには、タスクバーを使用します"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"2 個のアプリを同時に使用するには、横にドラッグします"</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..a5c411c 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"აპების გამოყენების პარამეტრები"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ყველას გასუფთავება"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ბოლოდროინდელი აპები"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ამოცანა დაიხურა"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</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="accessibility_rotate_button" msgid="4771825231336502943">"ეკრანის შეტრიალება"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ამოცანების ზოლის სასწავლო არე გამოჩნდა"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ამოცანების ზოლის სასწავლო არე დაიხურა"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"აპების გადასართავად გამოიყენეთ ამოცანათა ზოლი"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"გადაათრიეთ კიდეზე ორი აპის ერთდოულად გამოსაყენებლად"</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..38bfeb2 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Қолданбаны пайдалану параметрлері"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Барлығын өшіру"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Соңғы пайдаланылған қолданбалар"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Тапсырма жабылды."</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Қолданбада экранды бөлу мүмкін емес."</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="accessibility_rotate_button" msgid="4771825231336502943">"Экранды бұру"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Тапсырмалар тақтасы бойынша нұсқаулық ашылды."</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Тапсырмалар тақтасы бойынша нұсқаулық жабылды."</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Қолданбаларды ауыстыру үшін тапсырма тақтасын пайдаланыңыз."</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Екі қолданбаны бір уақытта пайдалану үшін шетке сүйреңіз."</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..9b8c997 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ការកំណត់​ការប្រើប្រាស់​កម្មវិធី"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"សម្អាត​ទាំងអស់"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"កម្មវិធី​ថ្មីៗ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"បានបិទ​កិច្ចការ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"កម្មវិធីមិនអាចប្រើមុខងារ​បំបែកអេក្រង់បានទេ។"</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="accessibility_rotate_button" msgid="4771825231336502943">"បង្វិលអេក្រង់"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ការបង្រៀនអំពីរបារកិច្ចការបានបង្ហាញ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ការបង្រៀនអំពីរបារកិច្ចការត្រូវបានបិទ"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ប្រើ​របារកិច្ចការ ដើម្បី​ប្ដូរកម្មវិធី"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"អូស​ទៅចំហៀង ដើម្បីប្រើ​កម្មវិធីពីរ​ក្នុងពេលតែមួយ"</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..a8b1182 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ಆ್ಯಪ್‌ ಬಳಕೆಯ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ಕಾರ್ಯವನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಆ್ಯಪ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</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="accessibility_rotate_button" msgid="4771825231336502943">"ಸ್ಕ್ರೀನ್ ತಿರುಗಿಸಿ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ಟಾಸ್ಕ್‌ಬಾರ್ ಶಿಕ್ಷಣ ಕಾಣಿಸಿಕೊಂಡಿದೆ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ಟಾಸ್ಕ್‌ಬಾರ್ ಶಿಕ್ಷಣ ಮುಚ್ಚಿದೆ"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಟಾಸ್ಕ್ ಬಾರ್ ಬಳಸಿ"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ಏಕಕಾಲದಲ್ಲಿ ಎರಡು ಆ್ಯಪ್‌ಗಳನ್ನು ಬಳಸಲು, ಬದಿಗೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</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..c46efa4 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"앱 사용 설정"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"모두 삭제"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"최근 앱"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"작업 종료됨"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"앱이 화면 분할을 지원하지 않습니다."</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="accessibility_rotate_button" msgid="4771825231336502943">"화면 회전"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"작업 표시줄 튜토리얼 패널 표시됨"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"작업 표시줄 튜토리얼 패널 닫힘"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"작업 표시줄을 사용하여 앱 전환"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"옆으로 드래그하여 한 번에 앱 두 개 사용"</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..bc39735 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Колдонмону пайдалануу жөндөөлөрү"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Баарын тазалоо"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Акыркы колдонмолор"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Тапшырма жабылды"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Колдонмодо экран бөлүнбөйт."</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="accessibility_rotate_button" msgid="4771825231336502943">"Экранды буруу"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Тапшырмалар тактасынын окутуу панели көрсөтүлдү"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Тапшырмалар тактасынын окутуу панели жабылды"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Тапшырмалар тактасы аркылуу башка колдонмого которула аласыз"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Эки колдонмону бир убакта пайдалануу үчүн капталга сүрүңүз"</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..c1ca1a3 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ການຕັ້ງຄ່າການນຳໃຊ້ແອັບ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ລຶບລ້າງທັງໝົດ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ແອັບຫຼ້າສຸດ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ປິດໜ້າວຽກແລ້ວ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"ແອັບບໍ່ຮອງຮັບການແບ່ງໜ້າຈໍ."</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="accessibility_rotate_button" msgid="4771825231336502943">"ໝຸນໜ້າຈໍ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ສະແດງການສຶກສາແຖບໜ້າວຽກແລ້ວ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ປິດການສຶກສາແຖບໜ້າວຽກແລ້ວ"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ໃຊ້ແຖບໜ້າວຽກເພື່ອສະຫຼັບແອັບ"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ລາກໄປທາງຂ້າງເພື່ອໃຊ້ສອງແອັບພ້ອມກັນ"</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..0b1301c 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programos naudojimo nustatymai"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Išvalyti viską"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Naujausios programos"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Užduotis uždaryta"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Programoje nepalaikomas išskaidyto ekrano režimas."</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="accessibility_rotate_button" msgid="4771825231336502943">"Pasukti ekraną"</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_switch_apps" msgid="6942863327845784813">"Naudokite užduočių juostą, kad gal. perjungti programas"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Nuvilkite į šoną, kad gal. vienu metu naudoti dvi programas"</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..c56cd5b 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Lietotņu izmantošanas iestatījumi"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Notīrīt visu"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Pēdējās izmantotās lietotnes"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Uzdevums ir aizvērts"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</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="accessibility_rotate_button" msgid="4771825231336502943">"Pagriezt ekrānu"</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_switch_apps" msgid="6942863327845784813">"Izmantojiet uzdevumjoslu, lai pārslēgtu lietotnes."</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Velciet uz malu, lai izmantotu divas lietotnes vienlaikus."</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..1afe079 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Поставки за користење на апликациите"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Избриши ги сите"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Неодамнешни апликации"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Задачата е затворена"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Апликацијата не поддржува поделен екран."</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="accessibility_rotate_button" msgid="4771825231336502943">"Ротирајте го екранот"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Се појави лентата за задачи за образование"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Затворена е лентата за задачи за образование"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Префрлувајте се меѓу апликации преку лентата за задачи"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Повлечете кон страната за да користите две апликации одеднаш"</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..eed4a7a 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ആപ്പ് ഉപയോഗ ക്രമീകരണം"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"എല്ലാം മായ്‌ക്കുക"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"സമീപകാല ആപ്പുകൾ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ടാസ്ക്ക് അടച്ചു"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</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="accessibility_rotate_button" msgid="4771825231336502943">"സ്‌ക്രീൻ റൊട്ടേറ്റ് ചെയ്യുക"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ടാസ്ക്ക്ബാർ വിവര പാനൽ ദൃശ്യമായി"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ടാസ്ക്ക്ബാർ വിവര പാനൽ അടച്ചു"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ആപ്പുകൾ മാറാൻ ടാസ്ക്ക്ബാർ ഉപയോഗിക്കുക"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"രണ്ട് ആപ്പുകൾ ഒരുമിച്ച് ഉപയോഗിക്കാൻ അരികിലേക്ക് വലിച്ചിടുക"</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..4e64649 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Апп ашиглалтын тохиргоо"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг устгах"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Саяхны аппууд"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Ажлыг хаасан"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Апп дэлгэцийг хуваах горимыг дэмждэггүй."</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="accessibility_rotate_button" msgid="4771825231336502943">"Дэлгэцийг эргүүлэх"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Боловсролын ажлын талбар гарч ирсэн"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Боловсролын ажлын талбарыг хаасан"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Аппуудыг сэлгэхийн тулд талбарыг ашиглана уу"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Хоёр аппыг зэрэг ашиглахын тулд хажуу тал руу чирнэ үү"</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..897c7f3 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अ‍ॅप वापर सेटिंग्ज"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"सर्व साफ करा"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"अलीकडील अ‍ॅप्स"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"टास्क बंद केली"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"अ‍ॅप हे स्प्लिट-स्क्रीनला सपोर्ट करत नाही."</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="accessibility_rotate_button" msgid="4771825231336502943">"स्क्रीन फिरवा"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबारशी संबंधित माहिती देणारे पॅनल उघडले आहे"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबारशी संबंधित माहिती देणारे पॅनल बंद केले आहे"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ॲप्स स्विच करण्यासाठी टास्कबार वापरा"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"दोन ॲप्स एकत्र वापरण्यासाठी, त्यांना बाजूला नेऊन ड्रॅग करा"</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..7944655 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tetapan penggunaan apl"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Kosongkan semua"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apl terbaharu"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tugas Ditutup"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Apl tidak menyokong 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="accessibility_rotate_button" msgid="4771825231336502943">"Putar skrin"</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_switch_apps" msgid="6942863327845784813">"Gunakan bar tugas untuk menukar apl"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Seret ke tepi untuk menggunakan dua apl serentak"</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 14c3576..57d5f31 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"အက်ပ်အသုံးပြုမှု ဆက်တင်များ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"အားလုံးရှင်းရန်"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"လတ်တလောသုံး အက်ပ်များ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"လုပ်ဆောင်စရာ ပိတ်ထားသည်"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>၊ <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"အက်ပ်တွင် မျက်နှာပြင် ခွဲ၍ပြသခြင်း သုံး၍မရပါ။"</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="accessibility_rotate_button" msgid="4771825231336502943">"ဖန်သားပြင်လှည့်ရန်"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ပညာရေး လုပ်ဆောင်စရာဘား ပြထားသည်"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ပညာရေး လုပ်ဆောင်စရာဘား ပိတ်ထားသည်"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"အက်ပ်များပြောင်းရန် လုပ်ဆောင်စရာဘားကို သုံးပါ"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"အက်ပ်နှစ်ခု တစ်ပြိုင်တည်းသုံးရန် တစ်ဖက်သို့ ဖိဆွဲပါ"</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..5c41210 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Innstillinger for appbruk"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Fjern alt"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nylige apper"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Oppgaven er lukket"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Appen støtter ikke 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotér skjermen"</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_switch_apps" msgid="6942863327845784813">"Bruk oppgavelinjen for å bytte app"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Dra til siden for å bruke to apper samtidig"</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..8f316e4 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"एपको उपयोगका सेटिङहरू"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"सबै मेटाउनुहोस्"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"हालसालैका एपहरू"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"कार्य बन्द गरियो"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"यो एपको स्क्रिन विभाजन गर्न मिल्दैन।"</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="accessibility_rotate_button" msgid="4771825231336502943">"स्क्रिन घुमाउनुहोस्"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"टास्कबार एजुकेसन देखिएको छ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"टास्कबार एजुकेसन बन्द गरिएको छ"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"एउटा एपबाट अर्को एपमा जान टास्कबार प्रयोग गर्नुहोस्"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"टास्कबार छेउतिर ड्र्याग गरेर एकै पटक दुई वटा एप चलाउनुहोस्"</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/res/drawable/personal_work_tabs_ripple.xml b/quickstep/res/values-night/colors.xml
similarity index 64%
copy from res/drawable/personal_work_tabs_ripple.xml
copy to quickstep/res/values-night/colors.xml
index 2e57b80..af6e064 100644
--- a/res/drawable/personal_work_tabs_ripple.xml
+++ b/quickstep/res/values-night/colors.xml
@@ -13,10 +13,15 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <shape android:shape="rectangle">
-        <solid android:color="@android:color/transparent" />
-        <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-    </shape>
-</ripple>
\ No newline at end of file
+<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>
+
+    <color name="all_set_page_background">#FF000000</color>
+
+</resources>
\ No newline at end of file
diff --git a/quickstep/res/values-night/styles.xml b/quickstep/res/values-night/styles.xml
index 1bd3f5d..e6b3450 100644
--- a/quickstep/res/values-night/styles.xml
+++ b/quickstep/res/values-night/styles.xml
@@ -21,7 +21,7 @@
         <item name="android:statusBarColor">@android:color/transparent</item>
         <item name="android:enforceNavigationBarContrast">false</item>
         <item name="android:windowLightStatusBar">false</item>
-        <item name="android:windowBackground">#FF000000</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
     </style>
 
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index a7084fb..61dacbb 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Instellingen voor app-gebruik"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Alles wissen"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recente apps"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Taak gesloten"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"App ondersteunt geen gesplitst scherm."</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="accessibility_rotate_button" msgid="4771825231336502943">"Scherm draaien"</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_switch_apps" msgid="6942863327845784813">"Gebruik de taakbalk om van app te wisselen"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Sleep naar de zijkant om 2 apps tegelijk te gebruiken"</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..38d7e1b 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ୍‍ ବ୍ୟବହାର ସେଟିଂସ୍‍"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ବର୍ତ୍ତମାନର ଆପ୍‌"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ଟାସ୍କ ବନ୍ଦ ହୋଇଯାଇଛି"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"ସ୍ପ୍ଲିଟ-ସ୍କ୍ରିନକୁ ଆପ ସମର୍ଥନ କରେ ନାହିଁ।"</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="accessibility_rotate_button" msgid="4771825231336502943">"ସ୍କ୍ରିନ ଘୂରାନ୍ତୁ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ଟାସ୍କବାର୍ ଶିକ୍ଷା ଦେଖାଯାଇଛି"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ଟାସ୍କବାର୍ ଶିକ୍ଷା ବନ୍ଦ ହୋଇଯାଇଛି"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ କରିବା ପାଇଁ ଟାସ୍କବାର ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ଥରକେ ଦୁଇଟି ଆପ ବ୍ୟବହାର କରିବା ପାଇଁ ପାର୍ଶ୍ୱକୁ ଡ୍ରାଗ କରନ୍ତୁ"</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..341eaeb 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ਐਪ ਵਰਤੋਂ ਦੀਆਂ ਸੈਟਿੰਗਾਂ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ਹਾਲੀਆ ਐਪਾਂ"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ਕਾਰਜ ਬੰਦ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</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="accessibility_rotate_button" msgid="4771825231336502943">"ਸਕ੍ਰੀਨ ਘੁਮਾਓ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ਟਾਸਕਵਾਰ ਸਿੱਖਿਆ ਪੈਨਲ ਦਿਖਾਇਆ ਗਿਆ"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ਟਾਸਕਵਾਰ ਸਿੱਖਿਆ ਪੈਨਲ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ਐਪਾਂ ਸਵਿੱਚ ਕਰਨ ਲਈ ਟਾਸਕਬਾਰ ਵਰਤੋ"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ਇੱਕ ਵਾਰ ਵਿੱਚ ਦੋ ਐਪਾਂ ਵਰਤਣ ਲਈ ਉਨ੍ਹਾਂ ਨੂੰ ਸਾਈਡ ਵੱਲ ਘਸੀਟੋ"</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..1b4617f 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ustawienia użycia aplikacji"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Wyczyść wszystko"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Ostatnie aplikacje"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Zadanie zamknięte"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacja nie obsługuje podzielonego ekranu."</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="accessibility_rotate_button" msgid="4771825231336502943">"Obróć ekran"</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_switch_apps" msgid="6942863327845784813">"Używaj paska zadań, aby przełączać aplikacje"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Przeciągnij w bok, aby używać 2 aplikacji jednocześnie"</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..2519d00 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Definições de utilização de aplicações"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tarefa fechada"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"A app não é compatível com 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="accessibility_rotate_button" msgid="4771825231336502943">"Rodar ecrã"</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_switch_apps" msgid="6942863327845784813">"Utilize a barra de ferramentas para alternar entre apps"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arraste para o lado para utilizar duas apps em simultâneo"</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..a8e6d6e 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configurações de uso do app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Tarefa encerrada"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"O app não tem suporte para a divisão de 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="accessibility_rotate_button" msgid="4771825231336502943">"Girar a tela"</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_switch_apps" msgid="6942863327845784813">"Use a barra de tarefas para alternar entre apps"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Arraste para o lado e use dois apps ao mesmo tempo"</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..062827b 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setări de utilizare a aplicației"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Ștergeți tot"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicații recente"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Activitatea s-a încheiat"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplicația nu acceptă 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotiți ecranul"</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_switch_apps" msgid="6942863327845784813">"Folosiți bara de activități ca să comutați între aplicații"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Trageți în lateral ca să folosiți două aplicații deodată"</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..105da96 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки использования приложения"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Очистить все"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавние приложения"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Задача закрыта"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>: <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Приложение не поддерживает разделение экрана."</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="accessibility_rotate_button" msgid="4771825231336502943">"Повернуть экран"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Обучение по работе с панелью задач показано"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Обучение по работе с панелью задач скрыто"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Используйте панель задач, чтобы переключать приложения."</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Перетащите в сторону, чтобы использовать два приложения сразу."</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..2cf7457 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"යෙදුම් භාවිත සැකසීම්"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"සියල්ල හිස් කරන්න"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"මෑත යෙදුම්"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"කාර්යය අවසන් කරන ලදි"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"යෙදුම බෙදුම් තිරය සඳහා සහාය නොදක්වයි."</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="accessibility_rotate_button" msgid="4771825231336502943">"තිරය කරකවන්න"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"කාර්ය තීරු අධ්‍යාපනය දිස් විය"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"කාර්ය තීරු අධ්‍යාපනය වසා ඇත"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"යෙදුම් මාරු කිරීමට කාර්ය තීරුව භාවිත කරන්න"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"එකවර යෙදුම් දෙකක් භාවිතා කිරීමට පැත්තට අදින්න"</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..46467ac 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavenia využívania aplikácie"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Vymazať všetko"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedávne aplikácie"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Úloha bola zavretá"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikácia nepodporuje rozdelenú obrazovku."</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="accessibility_rotate_button" msgid="4771825231336502943">"Otočiť obrazovku"</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_switch_apps" msgid="6942863327845784813">"Aplikácie je možné prepínať pomocou panela úloh"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Po presunutí na stranu je možné používať dve aplikácie naraz"</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..d9aade7 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavitve uporabe aplikacij"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Počisti vse"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Opravilo je zaprto"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacija ne podpira načina razdeljenega zaslona."</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="accessibility_rotate_button" msgid="4771825231336502943">"Sukanje zaslona"</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_switch_apps" msgid="6942863327845784813">"Za preklop aplikacij uporabite opravilno vrstico."</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Povlecite vstran za uporabo dveh aplikacij hkrati."</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..f973b06 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cilësimet e përdorimit të aplikacionit"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Pastroji të gjitha"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikacionet e fundit"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Detyra u mbyll"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Aplikacioni nuk mbështet 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="accessibility_rotate_button" msgid="4771825231336502943">"Rrotullo ekranin"</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_switch_apps" msgid="6942863327845784813">"Përdor shiritin e detyrave për të ndryshuar aplikacionet"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Zvarrit anash për të përdorur të dyja aplikacionet njëherësh"</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..9bbf89b 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Подешавања коришћења апликације"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Обриши све"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавне апликације"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Задатак је затворен"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Апликација не подржава подељени екран."</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="accessibility_rotate_button" msgid="4771825231336502943">"Ротирајте екран"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Едукативно окно из траке задатака се појавило"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Едукативно окно из траке задатака је затворено"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Користите траку задатака да бисте мењали апликације"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Превуците на страну да користите две апликације одједном"</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..0852fb7 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Inställningar för appanvändning"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Rensa alla"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Senaste apparna"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Uppgiften har stängts"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Appen har inte stöd för 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="accessibility_rotate_button" msgid="4771825231336502943">"Rotera skärmen"</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_switch_apps" msgid="6942863327845784813">"Använd aktivitetsfältet för att byta mellan appar"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Dra till sidan om du vill använda två appar samtidigt"</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..8655576 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mipangilio ya matumizi ya programu"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Ondoa zote"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Programu za hivi karibuni"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Jukumu Limefungwa"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Programu haiwezi kutumia 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="accessibility_rotate_button" msgid="4771825231336502943">"Zungusha skrini"</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_switch_apps" msgid="6942863327845784813">"Tumia upau wa shughuli kubadilisha programu"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Buruta pembeni ili utumie programu mbili kwa wakati mmoja"</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-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..5d9e059
--- /dev/null
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -0,0 +1,19 @@
+<?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.
+*/
+-->
+<resources>
+    <dimen name="navigation_key_padding">25dp</dimen>
+</resources>
diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml
new file mode 100644
index 0000000..2831a6f
--- /dev/null
+++ b/quickstep/res/values-sw720dp/dimens.xml
@@ -0,0 +1,21 @@
+<?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.
+*/
+-->
+<resources>
+    <dimen name="overview_grid_row_spacing">44dp</dimen>
+    <dimen name="overview_page_spacing_grid_portrait">44dp</dimen>
+    <dimen name="overview_page_spacing_grid_landscape">44dp</dimen>
+</resources>
diff --git a/quickstep/res/values-sw900dp/dimens.xml b/quickstep/res/values-sw900dp/dimens.xml
new file mode 100644
index 0000000..3efa5e3
--- /dev/null
+++ b/quickstep/res/values-sw900dp/dimens.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.
+*/
+-->
+<resources>
+    <!-- The maximum width of the navigation bar ripples. -->
+    <dimen name="key_button_ripple_max_width">76dp</dimen>
+
+    <!-- The padding around the navigation buttons -->
+    <dimen name="navigation_key_padding">0dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index e7cc31d..a6f8530 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ஆப்ஸ் உபயோக அமைப்புகள்"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"எல்லாம் அழி"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"சமீபத்திய ஆப்ஸ்"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"பணி முடிந்தது"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"திரைப் பிரிப்பு அம்சத்தை ஆப்ஸ் ஆதரிக்கவில்லை."</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="accessibility_rotate_button" msgid="4771825231336502943">"திரையைச் சுழற்றும்"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"பணிப்பட்டியை எவ்வாறு பயன்படுத்துவது என்பது பற்றிய பலகம் காட்டப்படுகிறது"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"பணிப்பட்டியை எவ்வாறு பயன்படுத்துவது என்பது பற்றிய பலகம் மூடப்பட்டது"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ஆப்ஸிற்கு இடையே மாற பணிப்பட்டியைப் பயன்படுத்தவும்"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ஒரே நேரத்தில் இரு ஆப்ஸை உபயோகிக்க பக்கவாட்டிற்கு இழுக்கவும்"</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..d6f1e48 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"యాప్ వినియోగ సెట్టింగ్‌లు"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"అన్నీ తీసివేయండి"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ఇటీవలి యాప్‌లు"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"టాస్క్ మూసివేయబడింది"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"యాప్‌లో స్ప్లిట్-స్క్రీన్ పని చేయదు."</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="accessibility_rotate_button" msgid="4771825231336502943">"స్క్రీన్‌ను తిప్పండి"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"టాస్క్‌బార్ శిక్షణకు సంబంధించిన ప్యానెల్ కనిపించింది"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"టాస్క్‌బార్ శిక్షణకు సంబంధించిన ప్యానెల్ మూసివేయబడింది"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"యాప్‌లను స్విచ్ చేయడానికి టాస్క్‌బార్‌ను ఉపయోగించండి"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ఒకేసారి రెండు యాప్‌లను ఉపయోగించడానికి పక్కకు లాగండి"</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..2caa536 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"การตั้งค่าการใช้แอป"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ล้างทั้งหมด"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"แอปล่าสุด"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ปิดงานแล้ว"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"แอปไม่รองรับการแบ่งหน้าจอ"</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="accessibility_rotate_button" msgid="4771825231336502943">"หมุนหน้าจอ"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"แถบงาน Education ปรากฎขึ้น"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ปิดแถบงาน Education แล้ว"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ใช้แถบงานเพื่อเปลี่ยนแอป"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ลากไปด้านข้างเพื่อใช้ 2 แอปพร้อมกัน"</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..1a23e6f 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mga setting ng paggamit ng app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"I-clear lahat"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Mga kamakailang app"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Isinara ang Gawain"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Hindi sinusuportahan ng app ang split-screen."</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="accessibility_rotate_button" msgid="4771825231336502943">"I-rotate ang screen"</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_switch_apps" msgid="6942863327845784813">"Gamitin ang taskbar para magpalipat-lipat sa mga app"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"I-drag sa gilid para makagamit ng dalawang app nang sabay"</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..51f60f1 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Uygulama kullanım ayarları"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Tümünü temizle"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Son uygulamalar"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Görev Kapatıldı"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Uygulama bölünmüş ekranı desteklemiyor."</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="accessibility_rotate_button" msgid="4771825231336502943">"Ekranı döndür"</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_switch_apps" msgid="6942863327845784813">"Görev çubuğundan uygulamalar arasında geçiş yapabilirsiniz"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Tek seferde iki uygulamayı kullanmak için yana sürükleyin"</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..f96c835 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налаштування використання додатка"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Очистити все"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Нещодавні додатки"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Завдання закрито"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Додаток не підтримує розділення екрана."</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="accessibility_rotate_button" msgid="4771825231336502943">"Обернути екран"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"Панель завдань Education відкрито"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"Панель завдань Education закрито"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"Переходьте між додатками за допомогою панелі завдань"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Перетягніть убік, щоб використовувати два додатки одночасно"</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..3760caa 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ایپ کے استعمال کی ترتیبات"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"سبھی کو صاف کریں"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"حالیہ ایپس"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"ٹاسک بند ہے"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>،<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</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="accessibility_rotate_button" msgid="4771825231336502943">"اسکرین کو گھمائیں"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"ٹاکس بار کا تعلیمی پینل ظاہر ہو گیا"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"ٹاسک بار کا تعلیمی پینل بند ہو گیا"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"ایپس کو سوئچ کرنے کیلئے ٹاسک بار کا استعمال کریں"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"ایک وقت میں دو ایپس استعمال کرنے کے لیے سائیڈ پر گھسیٹیں"</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..4e75ee2 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ilovadan foydalanish sozlamalari"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Hammasini tozalash"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Yaqinda ishlatilgan ilovalar"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Vazifalar yopildi"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Bu ilovada ekranni ikkiga ajratish ishlamaydi."</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="accessibility_rotate_button" msgid="4771825231336502943">"Ekranni burish"</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_switch_apps" msgid="6942863327845784813">"Ilovalarni vazifalar panelida almashtirish mumkin"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Bir vaqtda ikkita ilova ochish uchun birini yoniga torting"</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..2cfc475 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cài đặt mức sử dụng ứng dụng"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Xóa tất cả"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Ứng dụng gần đây"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Đã đóng tác vụ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Ứng dụng không hỗ trợ chia đôi màn hình."</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="accessibility_rotate_button" msgid="4771825231336502943">"Xoay màn hình"</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_switch_apps" msgid="6942863327845784813">"Dùng thanh tác vụ để chuyển đổi ứng dụng"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Kéo sang bên để dùng hai ứng dụng cùng một lúc"</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..58407da 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"应用使用设置"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近用过的应用"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"任务已关闭"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>(<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"应用不支持分屏。"</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="accessibility_rotate_button" msgid="4771825231336502943">"旋转屏幕"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"任务栏教程已显示"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"任务栏教程已关闭"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"使用任务栏切换应用"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"拖动到一侧,以便一次使用两个应用"</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..ed23e16 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"閂咗工作"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>,<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"應用程式不支援分割螢幕。"</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="accessibility_rotate_button" msgid="4771825231336502943">"旋轉螢幕"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"顯示咗工作列教學"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"閂咗工作列教學"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"使用工作列即可切換應用程式"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"拖曳至一側即可同時使用兩個應用程式"</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..469a959 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"工作已關閉"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"這個應用程式不支援分割畫面。"</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="accessibility_rotate_button" msgid="4771825231336502943">"旋轉螢幕"</string>
+    <string name="taskbar_edu_opened" msgid="3950252793551919129">"工作列教學課程已顯示"</string>
+    <string name="taskbar_edu_closed" msgid="126643734478892862">"工作列教學課程已關閉"</string>
+    <string name="taskbar_edu_switch_apps" msgid="6942863327845784813">"使用工作列即可切換應用程式"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"拖曳到一邊即可同時使用兩個應用程式"</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..2a97d53 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -25,11 +25,11 @@
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Izilungiselelo zokusetshenziswa kohlelo lokusebenza"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Sula konke"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Izinhlelo zokusebenza zakamuva"</string>
+    <string name="task_view_closed" msgid="9170038230110856166">"Umsebenzi Uvaliwe"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <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 +79,22 @@
     <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="toast_split_app_unsupported" msgid="3271526028981899666">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</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="accessibility_rotate_button" msgid="4771825231336502943">"Zungezisa isikrini"</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_switch_apps" msgid="6942863327845784813">"Sebenzisa ibha yomsebenzi ukushintsha ama-app"</string>
+    <string name="taskbar_edu_splitscreen" msgid="2663361731630346489">"Hudula ngaseceleni ukuze usebenzise ama-app amabili ngesikhathi esisodwa"</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..671a617 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,59 @@
     <color name="all_apps_prediction_row_separator_dark">#3cffffff</color>
 
     <!-- Taskbar -->
-    <color name="taskbar_background">#101010</color>
-    <color name="taskbar_icon_selection_ripple">#E0E0E0</color>
+    <color name="taskbar_background">@color/overview_scrim_dark</color>
+    <color name="taskbar_nav_icon_selection_ripple">#E0E0E0</color>
+    <color name="taskbar_nav_icon_light_color">#ffffff</color>
+    <!-- The dark navigation button color is only used in the rare cases that taskbar isn't drawing
+    its background and the underlying app has requested dark buttons. -->
+    <color name="taskbar_nav_icon_dark_color">#99000000</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 -->
+    <color name="gesture_tutorial_taskbar_color">#202124</color>
+
+    <!-- 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>
+
+    <color name="all_set_page_background">#FFFFFFFF</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..5ea94e9 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,35 @@
     <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">234dp</dimen>
+    <dimen name="task_menu_horizontal_padding">8dp</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>
+    <item name="overview_max_scale" format="float" type="dimen">0.7</item>
 
     <!-- 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_button_spacing">32dp</dimen>
+    <dimen name="overview_actions_button_spacing_grid">36dp</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_portrait">60dp</dimen>
+    <dimen name="overview_grid_side_margin_landscape">52dp</dimen>
+    <dimen name="overview_grid_row_spacing">36dp</dimen>
+    <dimen name="overview_page_spacing">16dp</dimen>
+    <dimen name="overview_page_spacing_grid_portrait">36dp</dimen>
+    <dimen name="overview_page_spacing_grid_landscape">38dp</dimen>
 
     <!-- These speeds are in dp/s -->
     <dimen name="max_task_dismiss_drag_velocity">2.25dp</dimen>
@@ -51,7 +63,6 @@
     <dimen name="default_task_dismiss_drag_velocity_grid">1dp</dimen>
     <dimen name="default_task_dismiss_drag_velocity_grid_focus_task">5dp</dimen>
 
-    <dimen name="recents_page_spacing">16dp</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 +81,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>
 
@@ -81,7 +94,7 @@
     <dimen name="task_menu_vertical_padding">8dp</dimen>
     <dimen name="task_card_margin">8dp</dimen>
     <dimen name="task_card_menu_shadow_height">3dp</dimen>
-    <dimen name="task_menu_option_start_margin">12dp</dimen>
+    <dimen name="task_menu_option_start_margin">16dp</dimen>
     <!-- Copied from framework resource:
        docked_stack_divider_thickness - 2 * docked_stack_divider_insets -->
     <dimen name="multi_window_task_divider_size">10dp</dimen>
@@ -108,17 +121,57 @@
     <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>
+    <dimen name="gesture_tutorial_mock_taskbar_height">80dp</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>
+
+    <!-- Gesture Tutorial mock taskbar -->
+    <dimen name="gesture_tutorial_taskbar_icon_size">44dp</dimen>
+    <dimen name="gesture_tutorial_taskbar_icon_corner_radius">100dp</dimen>
+    <dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</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>
+    <dimen name="allset_swipe_up_shift">10dp</dimen>
 
     <!-- All Apps Education tutorial -->
     <dimen name="swipe_edu_padding">8dp</dimen>
@@ -148,13 +201,34 @@
     <!-- Minimum distance to swipe to trigger accessibility gesture -->
     <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
 
+    <!-- The maximum width of the navigation bar ripples. -->
+    <dimen name="key_button_ripple_max_width">95dp</dimen>
+
+    <dimen name="rounded_corner_content_padding">0dp</dimen>
+
+    <dimen name="navigation_key_padding">0dp</dimen>
+
+    <!-- Floating rotation button -->
+    <dimen name="floating_rotation_button_diameter">40dp</dimen>
+    <dimen name="floating_rotation_button_min_margin">20dp</dimen>
+    <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
+    <dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</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_ime_size">48dp</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..6caed1c 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -39,6 +39,9 @@
     <!-- Accessibility title for the list of recent apps [CHAR_LIMIT=none] -->
     <string name="accessibility_recent_apps">Recent apps</string>
 
+    <!-- Accessibility confirmation for task closed -->
+    <string name="task_view_closed">Task Closed</string>
+
     <!-- Accessibility title for an app card in Recents for apps that have time limit set
      [CHAR_LIMIT=none] -->
     <string name="task_contents_description_with_remaining_time"><xliff:g id="task_description" example="GMail">%1$s</xliff:g>, <xliff:g id="remaining_time" example="7 minutes left today">%2$s</xliff:g></string>
@@ -53,8 +56,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 +192,12 @@
     <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>
+    <!-- Label for toast when app selected for split isn't supported. [CHAR_LIMIT=50] -->
+    <string name="toast_split_app_unsupported">App does not support split-screen.</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 +210,30 @@
     <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>
+
+    <!-- Accessibility label for the rotation suggestion button -->
+    <string name="accessibility_rotate_button">Rotate screen</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">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">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..2efe72e 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -115,7 +115,7 @@
         <item name="android:statusBarColor">@android:color/transparent</item>
         <item name="android:enforceNavigationBarContrast">false</item>
         <item name="android:windowLightStatusBar">true</item>
-        <item name="android:windowBackground">#FFFFFFFF</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
     </style>
 
     <!--
@@ -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">96dp</item>
+        <item name="android:minHeight">48dp</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..4269f2d 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;
@@ -53,9 +57,10 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
 import com.android.launcher3.taskbar.TaskbarManager;
-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 +70,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 +109,61 @@
 
     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 +299,64 @@
 
         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),
+                        getStateManager(), getDepthController());
+        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();
+    }
+
+    @Override
+    public void runOnBindToTouchInteractionService(Runnable r) {
+        mTISBindHelper.runOnBindToTouchInteractionService(r);
+    }
+
+    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
@@ -312,19 +371,15 @@
         out.add(getDepthController());
         out.add(new RecentsViewStateController(this));
         out.add(new BackButtonAlphaHandler(this));
-        out.add(getTaskbarStateHandler());
     }
 
     public DepthController getDepthController() {
         return mDepthController;
     }
 
-    public @Nullable LauncherTaskbarUIController getTaskbarUIController() {
-        return mTaskbarUIController;
-    }
-
-    public TaskbarStateHandler getTaskbarStateHandler() {
-        return mTaskbarStateHandler;
+    @Nullable
+    public UnfoldTransitionProgressProvider getUnfoldTransitionProgressProvider() {
+        return mUnfoldTransitionProgressProvider;
     }
 
     @Override
@@ -375,14 +430,6 @@
     }
 
     @Override
-    public float getNormalTaskbarScale() {
-        if (mTaskbarUIController != null) {
-            return mTaskbarUIController.getTaskbarScaleOnHome();
-        }
-        return super.getNormalTaskbarScale();
-    }
-
-    @Override
     public void onDragLayerHierarchyChanged() {
         onLauncherStateOrFocusChanged();
     }
@@ -436,8 +483,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 +552,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/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 5fc79f0..35151f1 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -17,11 +17,8 @@
 
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
 import android.os.Build;
 import android.os.CancellationSignal;
-import android.os.Handler;
 
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
@@ -78,11 +75,4 @@
         mRemoteAnimationProvider = null;
         super.unregister();
     }
-
-    @Override
-    public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
-            Context context, Handler handler, long duration) {
-        mRemoteAnimationProvider = animProvider;
-        super.registerAndStartActivity(intent, animProvider, context, handler, duration);
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index b557779..49b2cc5 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;
@@ -97,7 +111,9 @@
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskViewUtils;
 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;
@@ -159,6 +175,8 @@
     public static final int RECENTS_LAUNCH_DURATION = 336;
     private static final int LAUNCHER_RESUME_START_DELAY = 100;
     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
+    public static final int SPLIT_LAUNCH_DURATION = 370;
+    public static final int SPLIT_DIVIDER_ANIM_DURATION = 100;
 
     public static final int CONTENT_ALPHA_DURATION = 217;
     protected static final int CONTENT_SCALE_DURATION = 350;
@@ -269,7 +287,8 @@
         long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
                 - STATUS_BAR_TRANSITION_PRE_DELAY;
         RemoteAnimationAdapterCompat adapterCompat =
-                new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay);
+                new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay,
+                        mLauncher.getIApplicationThread());
         return new ActivityOptionsWrapper(
                 ActivityOptionsCompat.makeRemoteAnimation(adapterCompat), onEndCallback);
     }
@@ -348,7 +367,7 @@
         if (launcherClosing) {
             // Delay animation by a frame to avoid jank.
             Pair<AnimatorSet, Runnable> launcherContentAnimator =
-                    getLauncherContentAnimator(true /* isAppOpening */, startDelay);
+                    getLauncherContentAnimator(true /* isAppOpening */, startDelay, false);
             anim.play(launcherContentAnimator.first);
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -422,6 +441,10 @@
                         4 - rotationChange);
             }
         }
+        if (mDeviceProfile.isTaskbarPresentInApps) {
+            // Animate to above the taskbar.
+            bounds.bottom -= target.contentInsets.bottom;
+        }
         return bounds;
     }
 
@@ -441,9 +464,10 @@
      * @param isAppOpening True when this is called when an app is opening.
      *                     False when this is called when an app is closing.
      * @param startDelay   Start delay duration.
+     * @param skipAllAppsScale True if we want to avoid scaling All Apps
      */
     private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
-            int startDelay) {
+            int startDelay, boolean skipAllAppsScale) {
         AnimatorSet launcherAnimator = new AnimatorSet();
         Runnable endListener;
 
@@ -461,7 +485,6 @@
             final float startAlpha = appsView.getAlpha();
             final float startScale = SCALE_PROPERTY.get(appsView);
             appsView.setAlpha(alphas[0]);
-            SCALE_PROPERTY.set(appsView, scales[0]);
 
             ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
             alpha.setDuration(CONTENT_ALPHA_DURATION);
@@ -473,12 +496,16 @@
                     appsView.setLayerType(View.LAYER_TYPE_NONE, null);
                 }
             });
-            ObjectAnimator scale = ObjectAnimator.ofFloat(appsView, SCALE_PROPERTY, scales);
-            scale.setInterpolator(AGGRESSIVE_EASE);
-            scale.setDuration(CONTENT_SCALE_DURATION);
+
+            if (!skipAllAppsScale) {
+                SCALE_PROPERTY.set(appsView, scales[0]);
+                ObjectAnimator scale = ObjectAnimator.ofFloat(appsView, SCALE_PROPERTY, scales);
+                scale.setInterpolator(AGGRESSIVE_EASE);
+                scale.setDuration(CONTENT_SCALE_DURATION);
+                launcherAnimator.play(scale);
+            }
 
             launcherAnimator.play(alpha);
-            launcherAnimator.play(scale);
 
             endListener = () -> {
                 appsView.setAlpha(startAlpha);
@@ -506,7 +533,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 +549,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);
                 }
             }
@@ -611,10 +665,10 @@
         AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile,
                 windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1],
                 hasSplashScreen, floatingView.isDifferentFromAppIcon());
-        int left = (int) (prop.cropCenterXStart - prop.cropWidthStart / 2);
-        int top = (int) (prop.cropCenterYStart - prop.cropHeightStart / 2);
-        int right = (int) (left + prop.cropWidthStart);
-        int bottom = (int) (top + prop.cropHeightStart);
+        int left = prop.cropCenterXStart - prop.cropWidthStart / 2;
+        int top = prop.cropCenterYStart - prop.cropHeightStart / 2;
+        int right = left + prop.cropWidthStart;
+        int bottom = top + prop.cropHeightStart;
         // Set the crop here so we can calculate the corner radius below.
         crop.set(left, top, right, bottom);
 
@@ -633,6 +687,10 @@
                 if (v instanceof BubbleTextView) {
                     ((BubbleTextView) v).setStayPressed(false);
                 }
+                LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
+                if (taskbarController != null) {
+                    taskbarController.showEdu();
+                }
                 openingTargets.release();
             }
         });
@@ -641,7 +699,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 +862,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 +896,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 +1005,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;
@@ -1015,7 +1088,8 @@
                     new RemoteAnimationAdapterCompat(
                             new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
                                     false /* startAtFrontOfQueue */),
-                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */,
+                            mLauncher.getIApplicationThread()));
 
             if (KEYGUARD_ANIMATION.get()) {
                 mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
@@ -1025,7 +1099,8 @@
                                 new LauncherAnimationRunner(
                                         mHandler, mKeyguardGoingAwayRunner,
                                         true /* startAtFrontOfQueue */),
-                                CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+                                CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */,
+                                mLauncher.getIApplicationThread()));
             }
 
             new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
@@ -1043,8 +1118,8 @@
             mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
             mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
                     new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
-                            false /* startAtFrontOfQueue */));
-            mLauncherOpenTransition.addHomeOpenCheck();
+                            false /* startAtFrontOfQueue */), mLauncher.getIApplicationThread());
+            mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
             SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
         }
     }
@@ -1085,7 +1160,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 +1189,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 +1219,181 @@
     }
 
     /**
-     * Animator that controls the transformations of the windows the targets that are closing.
+     * Returns view on launcher that corresponds to the closing app in the list of app targets
      */
-    private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
+    private @Nullable View findLauncherView(RemoteAnimationTargetCompat[] appTargets) {
+        for (RemoteAnimationTargetCompat appTarget : appTargets) {
+            if (appTarget.mode == MODE_CLOSING) {
+                View launcherView = findLauncherView(appTarget);
+                if (launcherView != null) {
+                    return launcherView;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns view on launcher that corresponds to the {@param runningTaskTarget}.
+     */
+    private @Nullable View findLauncherView(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.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 launcherView, PointF velocityPxPerS) {
+        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 (launcherView instanceof LauncherAppWidgetHostView) {
+            Size windowSize = new Size(mDeviceProfile.availableWidthPx,
+                    mDeviceProfile.availableHeightPx);
+            int fallbackBackgroundColor =
+                    FloatingWidgetView.getDefaultBackgroundColor(mLauncher, runningTaskTarget);
+            floatingWidget = FloatingWidgetView.getFloatingWidgetView(mLauncher,
+                    (LauncherAppWidgetHostView) launcherView, targetRect, windowSize,
+                    mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
+                    isTransluscent, fallbackBackgroundColor);
+        } else if (launcherView != null) {
+            floatingIconView = getFloatingIconView(mLauncher, launcherView,
+                    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.
+        final int rotationChange = getRotationChange(targets);
+        Rect windowTargetBounds = getWindowTargetBounds(targets, rotationChange);
+        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,
+                    windowTargetBounds) {
+                @Override
+                public void onUpdate(RectF currentRectF, float progress) {
+                    finalFloatingIconView.update(1f, 255 /* fgAlpha */, currentRectF, progress,
+                            windowAlphaThreshold, getCornerRadius(progress), false);
+
+                    super.onUpdate(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,
+                    windowTargetBounds) {
+                @Override
+                public void onUpdate(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(currentRectF, progress);
+                }
+            };
+            anim.addOnUpdateListener(runner);
+        }
+
+        // Use a fixed velocity to start the animation.
+        animation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                anim.start(mLauncher, velocityPxPerS);
+            }
+        });
+    }
+
+    /**
+     * 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 +1402,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 +1532,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 +1559,39 @@
 
             if (anim == null) {
                 anim = new AnimatorSet();
-                anim.play(mFromUnlock
-                        ? getUnlockWindowAnimator(appTargets, wallpaperTargets)
-                        : getClosingWindowAnimators(appTargets, wallpaperTargets));
+
+                final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
+                        || launcherIsATargetWithMode(appTargets, MODE_OPENING);
+
+                View launcherView = findLauncherView(appTargets);
+                boolean playFallBackAnimation = (launcherView == null
+                        && launcherIsForceInvisibleOrOpening)
+                        || mLauncher.getWorkspace().isOverlayShown();
+
+                boolean playWorkspaceReveal = true;
+                boolean skipAllAppsScale = false;
+                if (mFromUnlock) {
+                    anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
+                } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
+                        && !playFallBackAnimation) {
+                    // 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);
+                    getClosingWindowAnimators(anim, appTargets, launcherView, velocity);
+                    if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
+                        anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
+                                true /* animateOverviewScrim */, launcherView).getAnimators());
+                        // We play StaggeredWorkspaceAnim as a part of the closing window animation.
+                        playWorkspaceReveal = false;
+                    } else {
+                        // Skip scaling all apps, otherwise FloatingIconView will get wrong
+                        // layout bounds.
+                        skipAllAppsScale = true;
+                    }
+                } 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
@@ -1316,8 +1601,7 @@
                 // targets list because it is already visible). In that case, we force
                 // invisibility on touch down, and only reset it after the animation to home
                 // is initialized.
-                if (launcherIsATargetWithMode(appTargets, MODE_OPENING)
-                        || mLauncher.isForceInvisible()) {
+                if (launcherIsForceInvisibleOrOpening) {
                     addCujInstrumentation(
                             anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
                     // Only register the content animation for cancellation when state changes
@@ -1325,7 +1609,8 @@
 
                     if (mLauncher.isInState(LauncherState.ALL_APPS)) {
                         Pair<AnimatorSet, Runnable> contentAnimator =
-                                getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY);
+                                getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
+                                        skipAllAppsScale);
                         anim.play(contentAnimator.first);
                         anim.addListener(new AnimatorListenerAdapter() {
                             @Override
@@ -1334,7 +1619,9 @@
                             }
                         });
                     } else {
-                        anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+                        if (playWorkspaceReveal) {
+                            anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+                        }
                     }
                 }
             }
@@ -1483,4 +1770,101 @@
             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;
+        private final Rect mWindowTargetBounds = new Rect();
+
+        private final Rect mTmpRect = new Rect();
+
+        SpringAnimRunner(RemoteAnimationTargetCompat[] appTargets, RectF targetRect,
+                Rect windowTargetBounds) {
+            mAppTargets = appTargets;
+            mStartRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
+            mEndRadius = Math.max(1, targetRect.width()) / 2f;
+            mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer);
+            mWindowTargetBounds.set(windowTargetBounds);
+        }
+
+        public float getCornerRadius(float progress) {
+            return Utilities.mapRange(progress, mStartRadius, mEndRadius);
+        }
+
+        @Override
+        public void onUpdate(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) {
+                    currentRectF.round(mCurrentRect);
+
+                    // Scale the target window to match the currentRectF.
+                    final float scale;
+
+                    // We need to infer the crop (we crop the window to match the currentRectF).
+                    if (mWindowTargetBounds.height() > mWindowTargetBounds.width()) {
+                        scale = Math.min(1f, currentRectF.width() / mWindowTargetBounds.width());
+
+                        int unscaledHeight = (int) (mCurrentRect.height() * (1f / scale));
+                        int croppedHeight = mWindowTargetBounds.height() - unscaledHeight;
+                        mTmpRect.set(0, 0, mWindowTargetBounds.width(),
+                                mWindowTargetBounds.height() - croppedHeight);
+                    } else {
+                        scale = Math.min(1f, currentRectF.height() / mWindowTargetBounds.height());
+
+                        int unscaledWidth = (int) (mCurrentRect.width() * (1f / scale));
+                        int croppedWidth = mWindowTargetBounds.width() - unscaledWidth;
+                        mTmpRect.set(0, 0, mWindowTargetBounds.width() - croppedWidth,
+                                mWindowTargetBounds.height());
+                    }
+
+                    // Match size and position of currentRect.
+                    mMatrix.setScale(scale, scale);
+                    mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
+
+                    builder.withMatrix(mMatrix)
+                            .withWindowCrop(mTmpRect)
+                            .withAlpha(getWindowAlpha(progress))
+                            .withCornerRadius(getCornerRadius(progress) / scale);
+                } 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..119ae90 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -28,17 +28,20 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.views.AbstractSlideInView;
 
@@ -77,6 +80,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() {
@@ -84,8 +92,9 @@
         mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
         mSampleHotseat = findViewById(R.id.sample_prediction);
 
+        Context context = getContext();
         DeviceProfile grid = mActivityContext.getDeviceProfile();
-        Rect padding = grid.getHotseatLayoutPadding();
+        Rect padding = grid.getHotseatLayoutPadding(context);
 
         mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
         mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1);
@@ -97,6 +106,15 @@
         mDismissBtn = findViewById(R.id.no_thanks);
         mDismissBtn.setOnClickListener(this::onDismiss);
 
+        LinearLayout buttonContainer = findViewById(R.id.button_container);
+        int adjustedMarginEnd = ApiWrapper.getHotseatEndOffset(context)
+                - buttonContainer.getPaddingEnd();
+        if (InvariantDeviceProfile.INSTANCE.get(context)
+                .getDeviceProfile(context).isTaskbarPresent && adjustedMarginEnd > 0) {
+            ((LinearLayout.LayoutParams) buttonContainer.getLayoutParams()).setMarginEnd(
+                    adjustedMarginEnd);
+        }
+
         // update ui to reflect which migration method is going to be used
         if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
             ((TextView) findViewById(R.id.hotseat_edu_content)).setText(
@@ -200,9 +218,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/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
index 080633a..56945ba 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
@@ -16,33 +16,25 @@
 package com.android.launcher3.hybridhotseat;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo;
+import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
+import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation;
 
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
-import android.app.prediction.AppTargetId;
-import android.content.ComponentName;
 import android.content.Context;
 import android.os.Bundle;
 
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Workspace;
 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.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.shortcuts.ShortcutKey;
 
 import java.util.ArrayList;
-import java.util.Locale;
 
 /**
  * Model helper for app predictions in workspace
  */
 public class HotseatPredictionModel {
-    private static final String APP_LOCATION_HOTSEAT = "hotseat";
-    private static final String APP_LOCATION_WORKSPACE = "workspace";
-
     private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
     private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items";
 
@@ -54,15 +46,15 @@
         ArrayList<AppTargetEvent> events = new ArrayList<>();
         ArrayList<ItemInfo> workspaceItems = dataModel.getAllWorkspaceItems();
         for (ItemInfo item : workspaceItems) {
-            AppTarget target = getAppTargetFromInfo(context, item);
-            if (target != null && !isTrackedForPrediction(item)) continue;
-            events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
+            AppTarget target = getAppTargetFromItemInfo(context, item);
+            if (target != null && !isTrackedForHotseatPrediction(item)) continue;
+            events.add(wrapAppTargetWithItemLocation(target, AppTargetEvent.ACTION_PIN, item));
         }
         ArrayList<AppTarget> currentTargets = new ArrayList<>();
         FixedContainerItems hotseatItems = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
         if (hotseatItems != null) {
             for (ItemInfo itemInfo : hotseatItems.items) {
-                AppTarget target = getAppTargetFromInfo(context, itemInfo);
+                AppTarget target = getAppTargetFromItemInfo(context, itemInfo);
                 if (target != null) currentTargets.add(target);
             }
         }
@@ -70,56 +62,4 @@
         bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
         return bundle;
     }
-
-    /**
-     * Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null
-     * if item is not supported prediction
-     */
-    public static AppTarget getAppTargetFromInfo(Context context, ItemInfo info) {
-        if (info == null) return null;
-        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
-                && info instanceof LauncherAppWidgetInfo
-                && ((LauncherAppWidgetInfo) info).providerName != null) {
-            ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
-            return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
-                    cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                && info.getTargetComponent() != null) {
-            ComponentName cn = info.getTargetComponent();
-            return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
-                    cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
-                && info instanceof WorkspaceItemInfo) {
-            ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
-            //TODO: switch to using full shortcut info
-            return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
-                    shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
-            return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
-                    context.getPackageName(), info.user).build();
-        }
-        return null;
-    }
-
-    /**
-     * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
-     * location using {@link ItemInfo}
-     */
-    public static AppTargetEvent wrapAppTargetWithLocation(
-            AppTarget target, int action, ItemInfo info) {
-        String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
-                info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
-                        ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
-                info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
-        return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
-    }
-
-    /**
-     * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
-     */
-    public static boolean isTrackedForPrediction(ItemInfo info) {
-        return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
-                info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
-                        && info.screenId == Workspace.FIRST_SCREEN_ID);
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index eed493d..7c29c5b 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -22,6 +22,7 @@
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
@@ -30,10 +31,13 @@
 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;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
+import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
+import static com.android.launcher3.model.PredictionHelper.isTrackedForWidgetPrediction;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.annotation.TargetApi;
@@ -61,7 +65,6 @@
 import com.android.launcher3.logger.LauncherAtom.HotseatContainer;
 import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer;
 import com.android.launcher3.logging.StatsLogManager.EventEnum;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
@@ -140,6 +143,9 @@
             if (isTrackedForHotseatPrediction(mLastDragItem)) {
                 sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
             }
+            if (isTrackedForWidgetPrediction(atomInfo)) {
+                sendEvent(atomInfo, ACTION_PIN, CONTAINER_WIDGETS_PREDICTION);
+            }
             mLastDragItem = null;
         } else if (event == LAUNCHER_ITEM_DROP_FOLDER_CREATED) {
             if (isTrackedForHotseatPrediction(atomInfo)) {
@@ -157,10 +163,18 @@
             if (mLastDragItem != null && isTrackedForHotseatPrediction(mLastDragItem)) {
                 sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
             }
+            if (mLastDragItem != null && isTrackedForWidgetPrediction(mLastDragItem)) {
+                sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_WIDGETS_PREDICTION);
+            }
         } else if (event == LAUNCHER_HOTSEAT_PREDICTION_PINNED) {
             if (isTrackedForHotseatPrediction(atomInfo)) {
                 sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
             }
+        } else if (event == LAUNCHER_ONRESUME) {
+            AppTarget target = new AppTarget.Builder(new AppTargetId("launcher:launcher"),
+                    mContext.getPackageName(), Process.myUserHandle())
+                    .build();
+            sendEvent(target, atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
         }
     }
 
@@ -257,9 +271,6 @@
             case ALL_APPS_CONTAINER: {
                 return "all-apps";
             }
-            case SEARCH_RESULT_CONTAINER: {
-                return "search-results";
-            }
             case PREDICTED_HOTSEAT_CONTAINER: {
                 return "predictions/hotseat";
             }
@@ -279,6 +290,16 @@
                 }
                 return "folder";
             }
+            case SEARCH_RESULT_CONTAINER:
+                return "search-results";
+            case EXTENDED_CONTAINERS: {
+                switch(ci.getExtendedContainers().getContainerCase()) {
+                    case DEVICE_SEARCH_RESULT_CONTAINER:
+                    case CORRECTED_DEVICE_SEARCH_RESULT_CONTAINER:
+                        return "search-results";
+                }
+            }
+            default: // fall out
         }
         return "";
     }
@@ -296,19 +317,4 @@
         return TextUtils.isEmpty(componentNameString)
                 ? null : ComponentName.unflattenFromString(componentNameString);
     }
-
-    /**
-     * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
-     */
-    private static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) {
-        ContainerInfo ci = info.getContainerInfo();
-        switch (ci.getContainerCase()) {
-            case HOTSEAT:
-                return true;
-            case WORKSPACE:
-                return ci.getWorkspace().getPageIndex() == 0;
-            default:
-                return false;
-        }
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/model/PredictionHelper.java b/quickstep/src/com/android/launcher3/model/PredictionHelper.java
new file mode 100644
index 0000000..738dd83
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/PredictionHelper.java
@@ -0,0 +1,130 @@
+/*
+ * 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.logger.LauncherAtom.ContainerInfo.ContainerCase.WORKSPACE;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.logger.LauncherAtom;
+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.shortcuts.ShortcutKey;
+
+import java.util.Locale;
+
+/** Helper class with methods for converting launcher items to form usable by predictors */
+public final class PredictionHelper {
+    private static final String APP_LOCATION_HOTSEAT = "hotseat";
+    private static final String APP_LOCATION_WORKSPACE = "workspace";
+
+    /**
+     * Creates and returns an {@link AppTarget} object for an {@link ItemInfo}. Returns null
+     * if item type is not supported in predictions
+     */
+    @Nullable
+    public static AppTarget getAppTargetFromItemInfo(Context context, ItemInfo info) {
+        if (info == null) return null;
+        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+                && info instanceof LauncherAppWidgetInfo
+                && ((LauncherAppWidgetInfo) info).providerName != null) {
+            ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
+            return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
+                    cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                && info.getTargetComponent() != null) {
+            ComponentName cn = info.getTargetComponent();
+            return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
+                    cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                && info instanceof WorkspaceItemInfo) {
+            ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
+            //TODO: switch to using full shortcut info
+            return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
+                    shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+            return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
+                    context.getPackageName(), info.user).build();
+        }
+        return null;
+    }
+
+    /**
+     * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
+     * location using {@link ItemInfo}
+     */
+    public static AppTargetEvent wrapAppTargetWithItemLocation(
+            AppTarget target, int action, ItemInfo info) {
+        String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
+                info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
+                        ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
+                info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
+        return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
+    }
+
+    /**
+     * Helper method to determine if {@link ItemInfo} should be tracked and reported to hotseat
+     * predictors
+     */
+    public static boolean isTrackedForHotseatPrediction(ItemInfo info) {
+        return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
+                info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
+                        && info.screenId == Workspace.FIRST_SCREEN_ID);
+    }
+
+    /**
+     * Helper method to determine if {@link LauncherAtom.ItemInfo} should be tracked and reported to
+     * hotseat predictors
+     */
+    public static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) {
+        LauncherAtom.ContainerInfo ci = info.getContainerInfo();
+        switch (ci.getContainerCase()) {
+            case HOTSEAT:
+                return true;
+            case WORKSPACE:
+                return ci.getWorkspace().getPageIndex() == 0;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Helper method to determine if {@link ItemInfo} should be tracked and reported to widget
+     * predictors
+     */
+    public static boolean isTrackedForWidgetPrediction(ItemInfo info) {
+        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+                && info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP;
+    }
+
+    /**
+     * Helper method to determine if {@link LauncherAtom.ItemInfo} should be tracked and reported
+     * to widget predictors
+     */
+    public static boolean isTrackedForWidgetPrediction(LauncherAtom.ItemInfo info) {
+        return info.getItemCase() == LauncherAtom.ItemInfo.ItemCase.WIDGET
+                && info.getContainerInfo().getContainerCase() == WORKSPACE;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index a9c2a5e..4d06956 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -25,7 +25,13 @@
 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.model.PredictionHelper.getAppTargetFromItemInfo;
+import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import static java.util.stream.Collectors.toCollection;
+
+import android.app.StatsManager;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionManager;
 import android.app.prediction.AppPredictor;
@@ -37,15 +43,17 @@
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
 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,11 +62,13 @@
 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.SettingsChangeLogger;
 import com.android.quickstep.logging.StatsLogCompatManager;
+import com.android.systemui.shared.system.SysUiStatsLog;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -68,10 +78,11 @@
 /**
  * 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";
+    private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets";
     private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
 
     private static final boolean IS_DEBUG = false;
@@ -86,15 +97,18 @@
 
     private final InvariantDeviceProfile mIDP;
     private final AppEventProducer mAppEventProducer;
+    private final StatsManager mStatsManager;
+    private final Context mContext;
 
     protected boolean mActive = false;
 
     public QuickstepModelDelegate(Context context) {
+        mContext = context;
         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
@@ -150,17 +164,82 @@
             }
             InstanceId instanceId = new InstanceIdSequence().newInstanceId();
             for (ItemInfo info : itemsIdMap) {
-                FolderInfo parent = info.container > 0
-                        ? (FolderInfo) itemsIdMap.get(info.container) : null;
+                FolderInfo parent = getContainer(info, itemsIdMap);
                 StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId);
             }
             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 = getContainer(info, itemsIdMap);
+                            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);
+                        SettingsChangeLogger.INSTANCE.get(mContext).logSnapshot(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);
+        }
+    }
+
+    private static FolderInfo getContainer(ItemInfo info, IntSparseArrayMap<ItemInfo> itemsIdMap) {
+        if (info.container > 0) {
+            ItemInfo containerInfo = itemsIdMap.get(info.container);
+
+            if (!(containerInfo instanceof FolderInfo)) {
+                Log.e(TAG, String.format(
+                        "Item info: %s found with invalid container: %s",
+                        info,
+                        containerInfo));
+            } else {
+                return (FolderInfo) containerInfo;
+            }
+        }
+        return null;
+    }
+
     @Override
     public void validateData() {
         super.validateData();
@@ -177,9 +256,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() {
@@ -217,6 +297,7 @@
         registerWidgetsPredictor(apm.createAppPredictionSession(
                 new AppPredictionContext.Builder(context)
                         .setUiSurface("widgets")
+                        .setExtras(getBundleForWidgetsOnWorkspace(context, mDataModel))
                         .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
                         .build()));
     }
@@ -224,7 +305,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 +320,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,19 +331,44 @@
         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;
+        PredictorState state;
+        switch(client) {
+            case CONTAINER_PREDICTION:
+                state = mAllAppsState;
+                break;
+            case CONTAINER_WIDGETS_PREDICTION:
+                state = mWidgetsRecommendationState;
+                break;
+            case CONTAINER_HOTSEAT_PREDICTION:
+            default:
+                state = mHotseatState;
+                break;
+        }
         if (state.predictor != null) {
             state.predictor.notifyAppTargetEvent(event);
+            Log.d(TAG, "notifyAppTargetEvent action=" + event.getAction()
+                    + " launchLocation=" + event.getLaunchLocation());
         }
     }
 
+    private Bundle getBundleForWidgetsOnWorkspace(Context context, BgDataModel dataModel) {
+        Bundle bundle = new Bundle();
+        ArrayList<AppTargetEvent> widgetEvents =
+                dataModel.getAllWorkspaceItems().stream()
+                        .filter(PredictionHelper::isTrackedForWidgetPrediction)
+                        .map(item -> {
+                            AppTarget target = getAppTargetFromItemInfo(context, item);
+                            if (target == null) return null;
+                            return wrapAppTargetWithItemLocation(
+                                    target, AppTargetEvent.ACTION_PIN, item);
+                        })
+                        .filter(Objects::nonNull)
+                        .collect(toCollection(ArrayList::new));
+        bundle.putParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, widgetEvents);
+        return bundle;
+    }
+
     static class PredictorState {
 
         public final FixedContainerItems items;
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/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 3d891e8..4be83dc 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -83,7 +83,7 @@
             }
         } else {
             Map<ComponentKey, WidgetItem> widgetItems =
-                    allWidgets.values().stream().flatMap(List::stream)
+                    allWidgets.values().stream().flatMap(List::stream).distinct()
                             .collect(Collectors.toMap(widget -> (ComponentKey) widget,
                                     widget -> widget));
             for (AppTarget app : mTargets) {
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index bb58f45..3242d42 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -44,6 +44,7 @@
 import com.android.systemui.shared.system.BlurUtils;
 import com.android.systemui.shared.system.WallpaperManagerCompat;
 
+import java.io.PrintWriter;
 import java.util.function.Consumer;
 
 /**
@@ -96,7 +97,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,13 +139,26 @@
      */
     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;
 
+    // Hints that there is potentially content behind Launcher and that we shouldn't optimize by
+    // marking the launcher surface as opaque.  Only used in certain Launcher states.
+    private boolean mHasContentBehindLauncher;
+
     private View.OnAttachStateChangeListener mOnAttachListener;
 
     public DepthController(Launcher l) {
@@ -184,6 +202,10 @@
         mLauncher.getScrimView().addOpaquenessListener(mOpaquenessListener);
     }
 
+    public void setHasContentBehindLauncher(boolean hasContentBehindLauncher) {
+        mHasContentBehindLauncher = hasContentBehindLauncher;
+    }
+
     /**
      * Sets if the underlying activity is started or not
      */
@@ -198,20 +220,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 +249,8 @@
             setDepth(toDepth);
         } else if (toState == LauncherState.OVERVIEW) {
             dispatchTransactionSurface(mDepth);
+        } else if (toState == LauncherState.BACKGROUND_APP) {
+            mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
         }
     }
 
@@ -263,9 +289,8 @@
         if (Float.compare(mDepth, depthF) == 0) {
             return;
         }
-        if (dispatchTransactionSurface(depthF)) {
-            mDepth = depthF;
-        }
+        dispatchTransactionSurface(depthF);
+        mDepth = depthF;
     }
 
     public void onOverlayScrollChanged(float progress) {
@@ -292,16 +317,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 hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
+            boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg;
 
-            int blur = opaque || isOverview || !mCrossWindowBlursEnabled
-                    || mBlurDisabledForAppLaunch ? 0 : (int) (depth * mMaxBlurRadius);
+            mCurrentBlur = !mCrossWindowBlursEnabled || mBlurDisabledForAppLaunch || hasOpaqueBg
+                    ? 0 : (int) (depth * mMaxBlurRadius);
             SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
-                    .setBackgroundBlurRadius(mSurface, blur)
-                    .setOpaque(mSurface, opaque);
+                    .setBackgroundBlurRadius(mSurface, mCurrentBlur)
+                    .setOpaque(mSurface, isSurfaceOpaque);
+
+            // 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();
@@ -328,4 +362,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..f1e6747
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.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 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);
+                }
+            };
+
+    public FallbackTaskbarUIController(RecentsActivity recentsActivity) {
+        mRecentsActivity = recentsActivity;
+    }
+
+    @Override
+    protected void init(TaskbarControllers taskbarControllers) {
+        super.init(taskbarControllers);
+
+        mRecentsActivity.setTaskbarUIController(this);
+        mRecentsActivity.getStateManager().addStateListener(mStateListener);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.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..5e8db69 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,13 +15,18 @@
  */
 package com.android.launcher3.taskbar;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.view.MotionEvent;
+import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED;
+import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
 
-import androidx.annotation.Nullable;
+import android.animation.Animator;
+import android.annotation.ColorInt;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.TaskTransitionSpec;
+import android.view.WindowManagerGlobal;
+
+import androidx.annotation.NonNull;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.DeviceProfile;
@@ -29,188 +34,116 @@
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-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.util.OnboardingPrefs;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.RecentsAnimationCallbacks;
 
+import java.util.Arrays;
+import java.util.Set;
+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 static final String TAG = "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 DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener =
+            this::onStashedInAppChanged;
 
-    private @Nullable Animator mAnimator;
-    private boolean mIsAnimatingToLauncher;
+    // Initialized in init.
+    private AnimatedFloat mTaskbarOverrideBackgroundAlpha;
+    private TaskbarKeyguardController mKeyguardController;
+    private final TaskbarLauncherStateController
+            mTaskbarLauncherStateController = new TaskbarLauncherStateController();
 
-    public LauncherTaskbarUIController(
-            BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
-        mContext = context;
-        mTaskbarDragLayer = context.getDragLayer();
-        mTaskbarView = mTaskbarDragLayer.findViewById(R.id.taskbar_view);
+    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) {
+        super.init(taskbarControllers);
+
+        mTaskbarLauncherStateController.init(mControllers, mLauncher);
+        mTaskbarOverrideBackgroundAlpha = mControllers.taskbarDragLayerController
+                .getOverrideBackgroundAlpha();
+
         mLauncher.setTaskbarUIController(this);
+        mKeyguardController = taskbarControllers.taskbarKeyguardController;
+
+        onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */);
+
+        onStashedInAppChanged(mLauncher.getDeviceProfile());
+        mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
+        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);
-        mLauncher.getHotseat().setIconsAlpha(1f);
+        super.onDestroy();
+        onLauncherResumedOrPaused(false);
+        mTaskbarLauncherStateController.onDestroy();
+
+        mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
         mLauncher.setTaskbarUIController(null);
+        mLauncher.removeOnDeviceProfileChangeListener(mProfileChangeListener);
+        updateTaskTransitionSpec(true);
     }
 
     @Override
     protected boolean isTaskbarTouchable() {
-        return !mIsAnimatingToLauncher;
-    }
-
-    private TaskbarAnimationControllerCallbacks createTaskbarAnimationControllerCallbacks() {
-        return new TaskbarAnimationControllerCallbacks() {
-            @Override
-            public void updateTaskbarBackgroundAlpha(float alpha) {
-                mTaskbarDragLayer.setTaskbarBackgroundAlpha(alpha);
-            }
-
-            @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);
-            }
-        };
+        return !mTaskbarLauncherStateController.isAnimatingToLauncher();
     }
 
     /**
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
     public void onLauncherResumedOrPaused(boolean isResumed) {
-        long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
-        if (mAnimator != null) {
-            mAnimator.cancel();
-        }
-        if (isResumed) {
-            mAnimator = createAnimToLauncher(null, duration);
-        } else {
-            mAnimator = createAnimToApp(duration);
-        }
-        mAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimator = null;
+        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();
             }
-        });
-        mAnimator.start();
+        }
+
+        mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
+        mTaskbarLauncherStateController.applyState(
+                fromInit ? 0 : QuickstepTransitionManager.CONTENT_ALPHA_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);
-        }
-
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mIsAnimatingToLauncher = true;
-                mTaskbarView.setHolesAllowedInLayout(true);
-                mTaskbarView.updateHotseatItemsVisibility();
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mIsAnimatingToLauncher = false;
-                setTaskbarViewVisible(false);
-            }
-        });
-
-        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);
-            }
-        });
-        return anim.buildAnim();
-    }
-
-    @Override
-    protected void onImeVisible(TaskbarDragLayer containerView, boolean isVisible) {
-        mTaskbarAnimationController.animateToVisibilityForIme(isVisible ? 0 : 1);
-    }
-
-    /**
-     * Should be called when one or more items in the Hotseat have changed.
-     */
-    public void onHotseatUpdated() {
-        mHotseatController.onHotseatUpdated();
+    public Animator createAnimToLauncher(@NonNull LauncherState toState,
+            @NonNull RecentsAnimationCallbacks callbacks, long duration) {
+        return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration);
     }
 
     /**
@@ -218,58 +151,99 @@
      * @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)
+                || mControllers.navbarButtonsViewController.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;
+    protected void onStashedInAppChanged() {
+        onStashedInAppChanged(mLauncher.getDeviceProfile());
+    }
 
-        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);
+    private void onStashedInAppChanged(DeviceProfile deviceProfile) {
+        boolean taskbarStashedInApps = mControllers.taskbarStashController.isStashedInApp();
+        deviceProfile.isTaskbarPresentInApps = !taskbarStashedInApps;
+        updateTaskTransitionSpec(taskbarStashedInApps);
+    }
+
+    private void updateTaskTransitionSpec(boolean taskbarIsHidden) {
+        try {
+            if (taskbarIsHidden) {
+                // Clear custom task transition settings when the taskbar is stashed
+                WindowManagerGlobal.getWindowManagerService().clearTaskTransitionSpec();
+            } else {
+                // Adjust task transition spec to account for taskbar being visible
+                @ColorInt int taskAnimationBackgroundColor =
+                        mLauncher.getColor(R.color.taskbar_background);
+
+                TaskTransitionSpec customTaskAnimationSpec = new TaskTransitionSpec(
+                        taskAnimationBackgroundColor,
+                        Set.of(ITYPE_EXTRA_NAVIGATION_BAR)
+                );
+                WindowManagerGlobal.getWindowManagerService()
+                        .setTaskTransitionSpec(customTaskAnimationSpec);
+            }
+        } catch (RemoteException e) {
+            // This shouldn't happen but if it does task animations won't look good until the
+            // taskbar stashing state is changed.
+            Log.e(TAG, "Failed to update task transition spec to account for new taskbar state",
+                    e);
+        }
     }
 
     /**
-     * Returns the ratio of the taskbar icon size on home vs in an app.
+     * Sets whether the background behind the taskbar/nav bar should be hidden.
      */
-    public float getTaskbarScaleOnHome() {
-        DeviceProfile inAppDp = mContext.getDeviceProfile();
-        DeviceProfile onHomeDp = mLauncher.getDeviceProfile();
-        return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
+    public void forceHideBackground(boolean forceHide) {
+        mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1);
     }
 
-    void setTaskbarViewVisible(boolean isVisible) {
-        mTaskbarView.setIconsVisibility(isVisible);
-        mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
+    @Override
+    public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
+        return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps());
     }
 
     /**
-     * Contains methods that TaskbarAnimationController can call to interface with
-     * TaskbarController.
+     * Starts the taskbar education flow, if the user hasn't seen it yet.
      */
-    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 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);
+    }
+
+    @Override
+    public void setSystemGestureInProgress(boolean inProgress) {
+        super.setSystemGestureInProgress(inProgress);
+        // Launcher's ScrimView will draw the background throughout the gesture. But once the
+        // gesture ends, start drawing taskbar's background again since launcher might stop drawing.
+        forceHideBackground(inProgress);
     }
 }
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..50637a1
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -0,0 +1,612 @@
+/*
+ * 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_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_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_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.annotation.DrawableRes;
+import android.annotation.IdRes;
+import android.annotation.LayoutRes;
+import android.content.pm.ActivityInfo.Config;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+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.MotionEvent;
+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.util.MultiValueAlpha;
+import com.android.quickstep.AnimatedFloat;
+import com.android.systemui.shared.rotation.FloatingRotationButton;
+import com.android.systemui.shared.rotation.RotationButton;
+import com.android.systemui.shared.rotation.RotationButtonController;
+
+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 FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
+    private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 11;
+
+    private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
+
+    private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
+    private final ArrayList<ImageView> 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 int mLightIconColor;
+    private final int mDarkIconColor;
+
+    private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
+            this::updateNavButtonTranslationY);
+    private final AnimatedFloat mTaskbarNavButtonTranslationYForIme = new AnimatedFloat(
+            this::updateNavButtonTranslationY);
+    // Only applies to mTaskbarNavButtonTranslationY
+    private final AnimatedFloat mNavButtonTranslationYMultiplier = new AnimatedFloat(
+            this::updateNavButtonTranslationY);
+    private final AnimatedFloat mTaskbarNavButtonDarkIntensity = new AnimatedFloat(
+            this::updateNavButtonDarkIntensity);
+    private final AnimatedFloat mNavButtonDarkIntensityMultiplier = new AnimatedFloat(
+            this::updateNavButtonDarkIntensity);
+    private final RotationButtonListener mRotationButtonListener = new RotationButtonListener();
+
+    private final Rect mFloatingRotationButtonBounds = new Rect();
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+    private View mA11yButton;
+    private int mSysuiStateFlags;
+    private View mBackButton;
+    private FloatingRotationButton mFloatingRotationButton;
+
+    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);
+
+        mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color);
+        mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color);
+    }
+
+    /**
+     * Initializes the controller
+     */
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+        mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
+        mNavButtonTranslationYMultiplier.value = 1;
+
+        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
+                        && (flags & FLAG_SCREEN_PINNING_ACTIVE) == 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();
+        boolean alwaysShowButtons = isThreeButtonNav || isInSetup;
+
+        // Make sure to remove nav bar buttons translation when notification shade is expanded or
+        // IME is showing (add separate translation for IME).
+        int flagsToRemoveTranslation = FLAG_NOTIFICATION_SHADE_EXPANDED | FLAG_IME_VISIBLE;
+        mPropertyHolders.add(new StatePropertyHolder(mNavButtonTranslationYMultiplier,
+                flags -> (flags & flagsToRemoveTranslation) != 0, AnimatedFloat.VALUE,
+                0, 1));
+        // Center nav buttons in new height for IME.
+        float transForIme = (mContext.getDeviceProfile().taskbarSize
+                - mContext.getTaskbarHeightForIme()) / 2f;
+        // For gesture nav, nav buttons only show for IME anyway so keep them translated down.
+        float defaultButtonTransY = alwaysShowButtons ? 0 : transForIme;
+        mPropertyHolders.add(new StatePropertyHolder(mTaskbarNavButtonTranslationYForIme,
+                flags -> (flags & FLAG_IME_VISIBLE) != 0, AnimatedFloat.VALUE, transForIme,
+                defaultButtonTransY));
+
+        if (alwaysShowButtons) {
+            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();
+
+                // TODO(b/210906568) Dark intensity is currently not propagated during setup, so set
+                //  it based on dark theme for now.
+                int mode = mContext.getResources().getConfiguration().uiMode
+                        & Configuration.UI_MODE_NIGHT_MASK;
+                boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
+                mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
+            }
+
+            // Animate taskbar background when any of these flags are enabled
+            int flagsToShowBg = FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE
+                    | FLAG_NOTIFICATION_SHADE_EXPANDED;
+            mPropertyHolders.add(new StatePropertyHolder(
+                    mControllers.taskbarDragLayerController.getNavbarBackgroundAlpha(),
+                    flags -> (flags & flagsToShowBg) != 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, null);
+        } else {
+            mFloatingRotationButton = new FloatingRotationButton(mContext,
+                    R.string.accessibility_rotate_button,
+                    R.layout.rotate_suggestion,
+                    R.id.rotate_suggestion,
+                    R.dimen.floating_rotation_button_min_margin,
+                    R.dimen.rounded_corner_content_padding,
+                    R.dimen.floating_rotation_button_taskbar_left_margin,
+                    R.dimen.floating_rotation_button_taskbar_bottom_margin,
+                    R.dimen.floating_rotation_button_diameter,
+                    R.dimen.key_button_ripple_max_width);
+            mControllers.rotationButtonController.setRotationButton(mFloatingRotationButton,
+                    mRotationButtonListener);
+
+            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 -> {
+                    // Show only if not disabled, and if not on the keyguard or otherwise only when
+                    // the bouncer or a lockscreen app is showing above the keyguard
+                    boolean showingOnKeyguard = (flags & FLAG_KEYGUARD_VISIBLE) == 0 ||
+                            (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0 ||
+                            (flags & FLAG_KEYGUARD_OCCLUDED) != 0;
+                    return (flags & FLAG_DISABLE_BACK) == 0
+                            && ((flags & FLAG_KEYGUARD_VISIBLE) == 0 || showingOnKeyguard);
+                }));
+        boolean isRtl = Utilities.isRtl(mContext.getResources());
+        mPropertyHolders.add(new StatePropertyHolder(
+                mBackButton, flags -> (flags & FLAG_IME_VISIBLE) != 0, View.ROTATION,
+                isRtl ? 90 : -90, 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));
+    }
+
+    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;
+        int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+                | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+        boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0;
+        boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 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);
+        updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
+        updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive);
+
+        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, boolean skipAnim) {
+        if (systemUiStateFlags == mSysuiStateFlags) {
+            return;
+        }
+        parseSystemUiFlags(systemUiStateFlags);
+        applyState();
+        if (skipAnim) {
+            mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
+        }
+    }
+
+    /**
+     * 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;
+    }
+
+    /** Use to set the dark intensity for the all nav+contextual buttons */
+    public AnimatedFloat getTaskbarNavButtonDarkIntensity() {
+        return mTaskbarNavButtonDarkIntensity;
+    }
+
+    /** Use to determine whether to use the dark intensity requested by the underlying app */
+    public AnimatedFloat getNavButtonDarkIntensityMultiplier() {
+        return mNavButtonDarkIntensityMultiplier;
+    }
+
+    /**
+     * 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() {
+        float normalTranslationY = mTaskbarNavButtonTranslationY.value
+                * mNavButtonTranslationYMultiplier.value;
+        float otherTranslationY = mTaskbarNavButtonTranslationYForIme.value;
+        mNavButtonsView.setTranslationY(normalTranslationY + otherTranslationY);
+    }
+
+    private void updateNavButtonDarkIntensity() {
+        float darkIntensity = mTaskbarNavButtonDarkIntensity.value
+                * mNavButtonDarkIntensityMultiplier.value;
+        int iconColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightIconColor,
+                mDarkIconColor);
+        for (ImageView button : mAllButtons) {
+            button.setImageTintList(ColorStateList.valueOf(iconColor));
+        }
+    }
+
+    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));
+        buttonView.setOnLongClickListener(view ->
+                navButtonController.onButtonLongClick(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 boolean isEventOverAnyItem(MotionEvent ev) {
+        return mFloatingRotationButtonBounds.contains((int) ev.getX(), (int) ev.getY());
+    }
+
+    public void onConfigurationChanged(@Config int configChanges) {
+        if (mFloatingRotationButton != null) {
+            mFloatingRotationButton.onConfigurationChanged(configChanges);
+        }
+    }
+
+    public void onDestroy() {
+        mPropertyHolders.clear();
+        mControllers.rotationButtonController.unregisterListeners();
+        if (mFloatingRotationButton != null) {
+            mFloatingRotationButton.hide();
+        }
+    }
+
+    private class RotationButtonListener implements RotationButton.RotationButtonUpdatesCallback {
+        @Override
+        public void onVisibilityChanged(boolean isVisible) {
+            if (isVisible) {
+                mFloatingRotationButton.getCurrentView()
+                        .getBoundsOnScreen(mFloatingRotationButtonBounds);
+            } else {
+                mFloatingRotationButtonBounds.setEmpty();
+            }
+        }
+    }
+
+    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);
+            mButton.setContentDescription(mButton.getResources()
+                    .getString(R.string.accessibility_rotate_button));
+            mImageDrawable.setCallback(mButton);
+        }
+
+        @Override
+        public View getCurrentView() {
+            return mButton;
+        }
+
+        @Override
+        public boolean show() {
+            mButton.setVisibility(View.VISIBLE);
+            mState |= FLAG_ROTATION_BUTTON_VISIBLE;
+            applyState();
+            return true;
+        }
+
+        @Override
+        public boolean hide() {
+            mButton.setVisibility(View.GONE);
+            mState &= ~FLAG_ROTATION_BUTTON_VISIBLE;
+            applyState();
+            return true;
+        }
+
+        @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..22ca63f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -0,0 +1,203 @@
+/*
+ * 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.ValueAnimator;
+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 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;
+
+    // When the reveal animation is cancelled, we can assume it's about to create a new animation,
+    // which should start off at the same point the cancelled one left off.
+    private float mStartProgressForNextRevealAnim;
+    private boolean mWasLastRevealAnimReversed;
+
+    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 Animator createRevealAnimToIsStashed(boolean isStashed) {
+        final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
+                mStashedHandleRadius, mStashedHandleRadius,
+                mControllers.taskbarViewController.getIconLayoutBounds(), mStashedHandleBounds);
+
+        boolean isReversed = !isStashed;
+        boolean changingDirection = mWasLastRevealAnimReversed != isReversed;
+        mWasLastRevealAnimReversed = isReversed;
+        if (changingDirection) {
+            mStartProgressForNextRevealAnim = 1f - mStartProgressForNextRevealAnim;
+        }
+
+        ValueAnimator revealAnim = handleRevealProvider.createRevealAnimator(mStashedHandleView,
+                isReversed, mStartProgressForNextRevealAnim);
+        revealAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mStartProgressForNextRevealAnim = ((ValueAnimator) animation).getAnimatedFraction();
+            }
+        });
+        return revealAnim;
+    }
+
+    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..3b6d5fb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -17,29 +17,37 @@
 
 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;
 
+import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo.Config;
 import android.content.pm.LauncherApps;
+import android.content.res.Resources;
+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 +55,27 @@
 
 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.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.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.rotation.RotationButtonController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
 
 /**
  * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
@@ -85,62 +90,98 @@
 
     private static final String WINDOW_TITLE = "Taskbar";
 
-    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 DeviceProfile mDeviceProfile;
 
     private final WindowManager mWindowManager;
+    private final @Nullable RoundedCorner mLeftCorner, mRightCorner;
+    private final int mTaskbarHeightForIme;
     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;
+    // The flag to know if the window is excluded from magnification region computation.
+    private boolean mIsExcludeFromMagnificationRegion = 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());
+        mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
+                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
 
-        mOnTaskbarIconLongClickListener =
-                new TaskbarDragController(this)::startSystemDragOnLongClick;
-        mOnTaskbarIconClickListener = this::onTaskbarIconClicked;
+        final Resources resources = getResources();
+        updateIconSize(resources);
 
-        float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
-        float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
-        mDeviceProfile.updateIconSize(iconScale, getResources());
+        mTaskbarHeightForIme = resources.getDimensionPixelSize(R.dimen.taskbar_ime_size);
 
         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,
+                        c.getColor(R.color.taskbar_nav_icon_light_color),
+                        c.getColor(R.color.taskbar_nav_icon_dark_color),
+                        R.drawable.ic_sysbar_rotate_button_ccw_start_0,
+                        R.drawable.ic_sysbar_rotate_button_ccw_start_90,
+                        R.drawable.ic_sysbar_rotate_button_cw_start_0,
+                        R.drawable.ic_sysbar_rotate_button_cw_start_90,
+                        () -> getDisplay().getRotation()),
+                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),
+                new TaskbarAutohideSuspendController(this),
+                new TaskbarPopupController());
     }
 
-    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 +189,53 @@
         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() - mTaskbarHeightForIme, 0, 0);
 
-        mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener);
+        // Initialize controllers after all are constructed.
+        mControllers.init(sharedState);
+        updateSysuiStateFlags(sharedState.sysuiStateFlags, true /* fromInit */);
+
         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);
+    /** Updates the Device profile instance to the latest representation of the screen. */
+    public void updateDeviceProfile(DeviceProfile dp) {
+        mDeviceProfile = dp;
+        updateIconSize(getResources());
     }
 
-    public boolean canShowNavButtons() {
-        return ENABLE_THREE_BUTTON_TASKBAR && mNavMode == Mode.THREE_BUTTONS;
+    private void updateIconSize(Resources resources) {
+        float taskbarIconSize = resources.getDimension(R.dimen.taskbar_icon_size);
+        mDeviceProfile.updateIconSize(1, resources);
+        float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
+        mDeviceProfile.updateIconSize(iconScale, resources);
+    }
+
+    public void onConfigurationChanged(@Config int configChanges) {
+        mControllers.onConfigurationChanged(configChanges);
+    }
+
+    public boolean isThreeButtonNav() {
+        return 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 +255,223 @@
 
     @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;
+    }
+
+    @Override
+    public View.OnClickListener getItemOnClickListener() {
+        return this::onTaskbarIconClicked;
+    }
+
+    /**
+     * 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, boolean fromInit) {
+        mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags,
+                fromInit);
+        mControllers.taskbarViewController.setImeIsVisible(
+                mControllers.navbarButtonsViewController.isImeVisible());
+        int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+                | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+        onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit);
+        mControllers.taskbarViewController.setRecentsButtonDisabled(
+                mControllers.navbarButtonsViewController.isRecentsDisabled());
+        mControllers.stashedHandleViewController.setIsHomeButtonDisabled(
+                mControllers.navbarButtonsViewController.isHomeDisabled());
+        mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
+        mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags, fromInit);
+        mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
+                fromInit);
+        mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
     }
 
     /**
-     * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
+     * Hides the taskbar icons and background when the notication shade is expanded.
      */
-    public void setImeIsVisible(boolean isImeVisible) {
-        mIconController.setImeIsVisible(isImeVisible);
+    private void onNotificationShadeExpandChanged(boolean isExpanded, boolean skipAnim) {
+        float alpha = isExpanded ? 0 : 1;
+        AnimatorSet anim = new AnimatorSet();
+        anim.play(mControllers.taskbarViewController.getTaskbarIconAlpha().getProperty(
+                TaskbarViewController.ALPHA_INDEX_NOTIFICATION_EXPANDED).animateToValue(alpha));
+        if (!isThreeButtonNav()) {
+            anim.play(mControllers.taskbarDragLayerController.getNotificationShadeBgTaskbar()
+                    .animateToValue(alpha));
+        }
+        anim.start();
+        if (skipAnim) {
+            anim.end();
+        }
     }
 
-    /**
-     * 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 onRotationProposal(int rotation, boolean isValid) {
+        mControllers.rotationButtonController.onRotationProposal(rotation, isValid);
+    }
+
+    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);
+    }
+
+    public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+        if (!isUserSetupComplete()) {
+            return;
+        }
+        mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
+                .updateValue(darkIntensity);
     }
 
     /**
      * 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) {
+        mControllers.taskbarAutohideSuspendController.updateFlag(
+                TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN, 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) {
+            height = mDeviceProfile.heightPx;
+        } else {
+            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;
+        mWindowLayoutParams.providedInternalImeInsets =
+                Insets.of(0, height - mTaskbarHeightForIme, 0, 0);
+        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());
+    }
+
+    /**
+     * Returns the bottom insets taskbar provides to the IME when IME is visible.
+     */
+    public int getTaskbarHeightForIme() {
+        return mTaskbarHeightForIme;
     }
 
     protected void onTaskbarIconClicked(View view) {
@@ -260,10 +487,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 +499,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 +525,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 +540,44 @@
         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();
+    }
+
+    /**
+     * 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);
+    }
+
+    protected boolean isUserSetupComplete() {
+        return mIsUserSetupComplete;
+    }
+
+    /**
+     * Called when we determine the touchable region.
+     *
+     * @param exclude {@code true} then the magnification region computation will omit the window.
+     */
+    public void excludeFromMagnificationRegion(boolean exclude) {
+        if (mIsExcludeFromMagnificationRegion == exclude) {
+            return;
         }
 
-        @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;
+        mIsExcludeFromMagnificationRegion = exclude;
+        if (exclude) {
+            mWindowLayoutParams.privateFlags |=
+                    WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+        } else {
+            mWindowLayoutParams.privateFlags &=
+                    ~WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
         }
-
-        @Override
-        protected void exitDrag() {
-        }
-
-        @Override
-        protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
-            return null;
-        }
+        mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
     }
 }
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/TaskbarAutohideSuspendController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
new file mode 100644
index 0000000..e42f83d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendController.java
@@ -0,0 +1,63 @@
+/*
+ * 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.IntDef;
+
+import com.android.quickstep.SystemUiProxy;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Normally Taskbar will auto-hide when entering immersive (fullscreen) apps. This controller allows
+ * us to suspend that behavior in certain cases (e.g. opening a Folder or dragging an icon).
+ */
+public class TaskbarAutohideSuspendController {
+
+    public static final int FLAG_AUTOHIDE_SUSPEND_FULLSCREEN = 1 << 0;
+    public static final int FLAG_AUTOHIDE_SUSPEND_DRAGGING = 1 << 1;
+    @IntDef(flag = true, value = {
+            FLAG_AUTOHIDE_SUSPEND_FULLSCREEN,
+            FLAG_AUTOHIDE_SUSPEND_DRAGGING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AutohideSuspendFlag {}
+
+    private final SystemUiProxy mSystemUiProxy;
+
+    private @AutohideSuspendFlag int mAutohideSuspendFlags = 0;
+
+    public TaskbarAutohideSuspendController(TaskbarActivityContext activity) {
+        mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
+    }
+
+    public void onDestroy() {
+        mSystemUiProxy.notifyTaskbarAutohideSuspend(false);
+    }
+
+    /**
+     * Adds or removes the given flag, then notifies system UI proxy whether to suspend auto-hide.
+     */
+    public void updateFlag(@AutohideSuspendFlag int flag, boolean enabled) {
+        if (enabled) {
+            mAutohideSuspendFlags |= flag;
+        } else {
+            mAutohideSuspendFlags &= ~flag;
+        }
+        mSystemUiProxy.notifyTaskbarAutohideSuspend(mAutohideSuspendFlags != 0);
+    }
+}
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..c43fbf9
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -0,0 +1,144 @@
+/*
+ * 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.pm.ActivityInfo.Config;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.rotation.RotationButtonController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 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;
+    public final TaskbarAutohideSuspendController taskbarAutohideSuspendController;
+    public final TaskbarPopupController taskbarPopupController;
+
+    /** Do not store this controller, as it may change at runtime. */
+    @NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
+
+    private boolean mAreAllControllersInitialized;
+    private final List<Runnable> mPostInitCallbacks = new ArrayList<>();
+
+    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,
+            TaskbarAutohideSuspendController taskbarAutoHideSuspendController,
+            TaskbarPopupController taskbarPopupController) {
+        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;
+        this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController;
+        this.taskbarPopupController = taskbarPopupController;
+    }
+
+    /**
+     * 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) {
+        mAreAllControllersInitialized = false;
+
+        taskbarDragController.init(this);
+        navbarButtonsViewController.init(this);
+        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);
+
+        mAreAllControllersInitialized = true;
+        for (Runnable postInitCallback : mPostInitCallbacks) {
+            postInitCallback.run();
+        }
+        mPostInitCallbacks.clear();
+    }
+
+    public void onConfigurationChanged(@Config int configChanges) {
+        navbarButtonsViewController.onConfigurationChanged(configChanges);
+    }
+
+    /**
+     * Cleans up all controllers.
+     */
+    public void onDestroy() {
+        navbarButtonsViewController.onDestroy();
+        uiController.onDestroy();
+        rotationButtonController.onDestroy();
+        taskbarDragLayerController.onDestroy();
+        taskbarKeyguardController.onDestroy();
+        taskbarUnfoldAnimationController.onDestroy();
+        taskbarViewController.onDestroy();
+        stashedHandleViewController.onDestroy();
+        taskbarAutohideSuspendController.onDestroy();
+    }
+
+    /**
+     * If all controllers are already initialized, runs the given callback immediately. Otherwise,
+     * queues it to run after calling init() on all controllers. This should likely be used in any
+     * case where one controller is telling another controller to do something inside init().
+     */
+    public void runAfterInit(Runnable callback) {
+        if (mAreAllControllersInitialized) {
+            callback.run();
+        } else {
+            mPostInitCallbacks.add(callback);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index ee44927..21d7411 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -15,25 +15,42 @@
  */
 package com.android.launcher3.taskbar;
 
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-
 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.config.FeatureFlags;
+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.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ClipDescriptionCompat;
 import com.android.systemui.shared.system.LauncherAppsCompat;
@@ -41,52 +58,213 @@
 /**
  * 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();
+    // Initialized in init.
+    TaskbarControllers mControllers;
+
+    // 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);
     }
 
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
     /**
      * Attempts to start a system drag and drop operation for the given View, using its tag to
      * 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);
+        btv.post(() -> {
+            startInternalDrag(btv);
+            btv.getIcon().setIsDisabled(true);
+            mControllers.taskbarAutohideSuspendController.updateFlag(
+                    TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, true);
+        });
+        return true;
+    }
+
+    private void startInternalDrag(BubbleTextView btv) {
+        float iconScale = btv.getIcon().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;
+            }
+        };
+        if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
+            PopupContainerWithArrow<TaskbarActivityContext> popupContainer =
+                    mControllers.taskbarPopupController.showForIcon(btv);
+            if (popupContainer != null) {
+                dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
+            }
+        }
+
+        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 offsetX = (mDragIconSize - mDragObject.dragView.getDragRegionWidth()) / 2;
+                int offsetY = (mDragIconSize - mDragObject.dragView.getDragRegionHeight()) / 2;
+                shadowTouchPoint.set(mRegistrationX + offsetX, mRegistrationY + offsetY);
             }
 
             @Override
             public void onDrawShadow(Canvas canvas) {
                 canvas.save();
-                float scale = (float) mDragIconSize / btv.getIconSize();
+                float scale = mDragObject.dragView.getScaleX();
                 canvas.scale(scale, scale);
-                btv.getIcon().draw(canvas);
+                mDragObject.dragView.draw(canvas);
                 canvas.restore();
             }
         };
 
-        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 +293,97 @@
         }
 
         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()) {
+            ((BubbleTextView) mDragObject.originalView).getIcon().setIsDisabled(false);
+            mControllers.taskbarAutohideSuspendController.updateFlag(
+                    TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, false);
+        }
+    }
+
+    @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..a918016
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -0,0 +1,209 @@
+/*
+ * 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);
+    private final AnimatedFloat mNotificationShadeBgTaskbar = new AnimatedFloat(
+            this::updateBackgroundAlpha);
+    private final AnimatedFloat mImeBgTaskbar = 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;
+    private AnimatedFloat mNavButtonDarkIntensityMultiplier;
+
+    private float mLastSetBackgroundAlpha;
+
+    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());
+
+        mNavButtonDarkIntensityMultiplier = mControllers.navbarButtonsViewController
+                .getNavButtonDarkIntensityMultiplier();
+
+        mBgTaskbar.value = 1;
+        mKeyguardBgTaskbar.value = 1;
+        mNotificationShadeBgTaskbar.value = 1;
+        mImeBgTaskbar.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 getNotificationShadeBgTaskbar() {
+        return mNotificationShadeBgTaskbar;
+    }
+
+    public AnimatedFloat getImeBgTaskbar() {
+        return mImeBgTaskbar;
+    }
+
+    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
+                * mNotificationShadeBgTaskbar.value * mImeBgTaskbar.value;
+        mLastSetBackgroundAlpha = mBgOverride.value * Math.max(bgNavbar, bgTaskbar);
+        mTaskbarDragLayer.setTaskbarBackgroundAlpha(mLastSetBackgroundAlpha);
+
+        updateNavBarDarkIntensityMultiplier();
+    }
+
+    private void updateBackgroundOffset() {
+        mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
+
+        updateNavBarDarkIntensityMultiplier();
+    }
+
+    private void updateNavBarDarkIntensityMultiplier() {
+        // Zero out the app-requested dark intensity when we're drawing our own background.
+        float effectiveBgAlpha = mLastSetBackgroundAlpha * (1 - mBgOffset.value);
+        mNavButtonDarkIntensityMultiplier.updateValue(1 - effectiveBgAlpha);
+    }
+
+    /**
+     * 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);
+            boolean insetsIsTouchableRegion = true;
+
+            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_REGION);
+            } 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);
+                insetsIsTouchableRegion = false;
+            } else {
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            }
+            mActivity.excludeFromMagnificationRegion(insetsIsTouchableRegion);
+        }
+
+        /**
+         * Called to update the {@link InsetsInfo#contentInsets}. This is reported to apps but our
+         * internal launcher will ignore these insets.
+         */
+        public void updateContentInsets(Rect outContentInsets) {
+            int contentHeight = mControllers.taskbarStashController
+                    .getContentHeightToReportToApps();
+            outContentInsets.top = mTaskbarDragLayer.getHeight() - contentHeight;
+        }
+
+        /**
+         * 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/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
new file mode 100644
index 0000000..be5ab55
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -0,0 +1,419 @@
+/*
+ * 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 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 androidx.annotation.NonNull;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.util.HashMap;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate
+ * the task bar accordingly.
+ */
+ public class TaskbarLauncherStateController {
+
+    public static final int FLAG_RESUMED = 1 << 0;
+    public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1;
+    public static final int FLAG_TRANSITION_STATE_RUNNING = 1 << 2;
+
+    /** Equivalent to an int with all 1s for binary operation purposes */
+    private static final int FLAGS_ALL = ~0;
+
+    private final AnimatedFloat mIconAlignmentForResumedState =
+            new AnimatedFloat(this::onIconAlignmentRatioChangedForAppAndHomeTransition);
+    private final AnimatedFloat mIconAlignmentForGestureState =
+            new AnimatedFloat(this::onIconAlignmentRatioChangedForAppAndHomeTransition);
+    private final AnimatedFloat mIconAlignmentForLauncherState =
+            new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition);
+
+    private TaskbarControllers mControllers;
+    private AnimatedFloat mTaskbarBackgroundAlpha;
+    private MultiValueAlpha.AlphaProperty mIconAlphaForHome;
+    private BaseQuickstepLauncher mLauncher;
+
+    private Integer mPrevState;
+    private int mState;
+    private LauncherState mLauncherState = LauncherState.NORMAL;
+
+    private boolean mIsAnimatingToLauncherViaGesture;
+    private boolean mIsAnimatingToLauncherViaResume;
+
+    private final StateManager.StateListener<LauncherState> mStateListener =
+            new StateManager.StateListener<LauncherState>() {
+
+                @Override
+                public void onStateTransitionStart(LauncherState toState) {
+                    if (toState != mLauncherState) {
+                        // Treat FLAG_TRANSITION_STATE_RUNNING as a changed flag even if a previous
+                        // state transition was already running, so we update the new target.
+                        mPrevState &= ~FLAG_TRANSITION_STATE_RUNNING;
+                        mLauncherState = toState;
+                    }
+                    updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, true);
+                    applyState();
+                }
+
+                @Override
+                public void onStateTransitionComplete(LauncherState finalState) {
+                    mLauncherState = finalState;
+                    updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
+                    applyState();
+                }
+            };
+
+    public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) {
+        mControllers = controllers;
+        mLauncher = launcher;
+
+        mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
+                .getTaskbarBackgroundAlpha();
+        MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
+        mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
+        mIconAlphaForHome.setConsumer(
+                (Consumer<Float>) alpha -> mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1));
+
+        mIconAlignmentForResumedState.finishAnimation();
+        onIconAlignmentRatioChangedForAppAndHomeTransition();
+
+        mLauncher.getStateManager().addStateListener(mStateListener);
+
+        // Initialize to the current launcher state
+        updateStateForFlag(FLAG_RESUMED, launcher.hasBeenResumed());
+        mLauncherState = launcher.getStateManager().getState();
+        applyState(0);
+    }
+
+    public void onDestroy() {
+        mIconAlignmentForResumedState.finishAnimation();
+        mIconAlignmentForGestureState.finishAnimation();
+        mIconAlignmentForLauncherState.finishAnimation();
+
+        mIconAlphaForHome.setConsumer(null);
+        mLauncher.getHotseat().setIconsAlpha(1f);
+        mLauncher.getStateManager().removeStateListener(mStateListener);
+    }
+
+    public Animator createAnimToLauncher(@NonNull LauncherState toState,
+            @NonNull RecentsAnimationCallbacks callbacks, long duration) {
+        // If going to overview, stash the task bar
+        // If going home, align the icons to hotseat
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        // Update stashed flags first to ensure goingToUnstashedLauncherState() returns correctly.
+        TaskbarStashController stashController = mControllers.taskbarStashController;
+        stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE,
+                toState.isTaskbarStashed(mLauncher));
+        stashController.updateStateForFlag(FLAG_IN_APP, false);
+
+        updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true);
+        animatorSet.play(stashController.applyStateWithoutStart(duration));
+        animatorSet.play(applyState(duration, false));
+
+        TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks);
+        callbacks.addListener(listener);
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        recentsView.setTaskLaunchListener(() -> {
+            listener.endGestureStateOverride(true);
+            callbacks.removeListener(listener);
+        });
+        return animatorSet;
+    }
+
+    public boolean isAnimatingToLauncher() {
+        return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
+    }
+
+    /**
+     * Updates the proper flag to change the state of the task bar.
+     *
+     * 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
+     */
+    public void updateStateForFlag(int flag, boolean enabled) {
+        if (enabled) {
+            mState |= flag;
+        } else {
+            mState &= ~flag;
+        }
+    }
+
+    private boolean hasAnyFlag(int flagMask) {
+        return hasAnyFlag(mState, flagMask);
+    }
+
+    private boolean hasAnyFlag(int flags, int flagMask) {
+        return (flags & flagMask) != 0;
+    }
+
+    public void applyState() {
+        applyState(TASKBAR_STASH_DURATION);
+    }
+
+    public void applyState(long duration) {
+        applyState(duration, true);
+    }
+
+    public Animator applyState(boolean start) {
+        return applyState(TASKBAR_STASH_DURATION, start);
+    }
+
+    public Animator applyState(long duration, boolean start) {
+        Animator animator = null;
+        if (mPrevState == null || mPrevState != mState) {
+            // If this is our initial state, treat all flags as changed.
+            int changedFlags = mPrevState == null ? FLAGS_ALL : mPrevState ^ mState;
+            mPrevState = mState;
+            animator = onStateChangeApplied(changedFlags, duration, start);
+        }
+        return animator;
+    }
+
+    private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) {
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        // Add the state animation first to ensure FLAG_IN_STASHED_LAUNCHER_STATE is set and we can
+        // determine whether goingToUnstashedLauncherStateChanged.
+        boolean wasGoingToUnstashedLauncherState = goingToUnstashedLauncherState();
+        if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_RUNNING)) {
+            boolean committed = !hasAnyFlag(FLAG_TRANSITION_STATE_RUNNING);
+            playStateTransitionAnim(animatorSet, duration, committed);
+
+            if (committed && mLauncherState == LauncherState.QUICK_SWITCH) {
+                // We're about to be paused, set immediately to ensure seamless handoff.
+                updateStateForFlag(FLAG_RESUMED, false);
+                applyState(0 /* duration */);
+            }
+        }
+        boolean goingToUnstashedLauncherStateChanged = wasGoingToUnstashedLauncherState
+                != goingToUnstashedLauncherState();
+
+        boolean launcherStateChangedDuringAnimToResumeAlignment =
+                mIconAlignmentForResumedState.isAnimating() && goingToUnstashedLauncherStateChanged;
+        if (hasAnyFlag(changedFlags, FLAG_RESUMED)
+                || launcherStateChangedDuringAnimToResumeAlignment) {
+            boolean isResumed = isResumed();
+            float toAlignmentForResumedState = isResumed && goingToUnstashedLauncherState() ? 1 : 0;
+            // If we're already animating to the value, just leave it be instead of restarting it.
+            if (!mIconAlignmentForResumedState.isAnimatingToValue(toAlignmentForResumedState)) {
+                ObjectAnimator resumeAlignAnim = mIconAlignmentForResumedState
+                        .animateToValue(toAlignmentForResumedState)
+                        .setDuration(duration);
+
+                resumeAlignAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mIsAnimatingToLauncherViaResume = false;
+                    }
+
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        mIsAnimatingToLauncherViaResume = isResumed;
+
+                        TaskbarStashController stashController =
+                                mControllers.taskbarStashController;
+                        stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
+                        stashController.applyState(duration);
+                    }
+                });
+                animatorSet.play(resumeAlignAnim);
+            }
+        }
+
+
+        boolean launcherStateChangedDuringAnimToGestureAlignment =
+                mIconAlignmentForGestureState.isAnimating() && goingToUnstashedLauncherStateChanged;
+        if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)
+                || launcherStateChangedDuringAnimToGestureAlignment) {
+            boolean isRecentsAnimationRunning = isRecentsAnimationRunning();
+            float toAlignmentForGestureState = isRecentsAnimationRunning
+                    && goingToUnstashedLauncherState() ? 1 : 0;
+            // If we're already animating to the value, just leave it be instead of restarting it.
+            if (!mIconAlignmentForGestureState.isAnimatingToValue(toAlignmentForGestureState)) {
+                Animator gestureAlignAnim = mIconAlignmentForGestureState
+                        .animateToValue(toAlignmentForGestureState);
+                if (isRecentsAnimationRunning) {
+                    gestureAlignAnim.setDuration(duration);
+                }
+                gestureAlignAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mIsAnimatingToLauncherViaGesture = false;
+                    }
+
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning();
+                    }
+                });
+                animatorSet.play(gestureAlignAnim);
+            }
+        }
+
+        if (hasAnyFlag(changedFlags, FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING)) {
+            boolean goingToLauncher = hasAnyFlag(FLAG_RESUMED | FLAG_RECENTS_ANIMATION_RUNNING);
+            animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(goingToLauncher ? 0 : 1)
+                    .setDuration(duration));
+        }
+
+        if (start) {
+            animatorSet.start();
+        }
+        return animatorSet;
+    }
+
+    /** Returns whether we're going to a state where taskbar icons should align with launcher. */
+    private boolean goingToUnstashedLauncherState() {
+        return !mControllers.taskbarStashController.isInStashedLauncherState();
+    }
+
+    private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
+            boolean committed) {
+        boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
+        float toAlignment = mLauncherState.isTaskbarAlignedWithHotseat(mLauncher) ? 1 : 0;
+
+        TaskbarStashController controller = mControllers.taskbarStashController;
+        controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, isInStashedState);
+        Animator stashAnimator = controller.applyStateWithoutStart(duration);
+        if (stashAnimator != null) {
+            stashAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (isInStashedState && committed) {
+                        // Reset hotseat alpha to default
+                        mLauncher.getHotseat().setIconsAlpha(1);
+                    }
+                }
+
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    if (mLauncher.getHotseat().getIconsAlpha() > 0) {
+                        mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha());
+                    }
+                }
+            });
+            animatorSet.play(stashAnimator);
+        }
+
+        // If we're already animating to the value, just leave it be instead of restarting it.
+        if (!mIconAlignmentForLauncherState.isAnimatingToValue(toAlignment)) {
+            animatorSet.play(mIconAlignmentForLauncherState.animateToValue(toAlignment)
+                    .setDuration(duration));
+        }
+    }
+
+    private boolean isResumed() {
+        return (mState & FLAG_RESUMED) != 0;
+    }
+
+    private boolean isRecentsAnimationRunning() {
+        return (mState & FLAG_RECENTS_ANIMATION_RUNNING) != 0;
+    }
+
+    private void onIconAlignmentRatioChangedForStateTransition() {
+        if (!isResumed()) {
+            return;
+        }
+        onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioForLauncherState);
+    }
+
+    private void onIconAlignmentRatioChangedForAppAndHomeTransition() {
+        onIconAlignmentRatioChanged(this::getCurrentIconAlignmentRatioBetweenAppAndHome);
+    }
+
+    private void onIconAlignmentRatioChanged(Supplier<Float> alignmentSupplier) {
+        if (mControllers == null) {
+            return;
+        }
+        float alignment = alignmentSupplier.get();
+        mControllers.taskbarViewController.setLauncherIconAlignment(
+                alignment, mLauncher.getDeviceProfile());
+
+        // Switch taskbar and hotseat in last frame
+        setTaskbarViewVisible(alignment < 1);
+    }
+
+    private float getCurrentIconAlignmentRatioBetweenAppAndHome() {
+        return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value);
+    }
+
+    private float getCurrentIconAlignmentRatioForLauncherState() {
+        return mIconAlignmentForLauncherState.value;
+    }
+
+    private void setTaskbarViewVisible(boolean isVisible) {
+        mIconAlphaForHome.setValue(isVisible ? 1 : 0);
+    }
+
+    private final class TaskBarRecentsAnimationListener implements
+            RecentsAnimationCallbacks.RecentsAnimationListener {
+        private final RecentsAnimationCallbacks mCallbacks;
+
+        TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) {
+            mCallbacks = callbacks;
+        }
+
+        @Override
+        public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
+            endGestureStateOverride(true);
+        }
+
+        @Override
+        public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+            endGestureStateOverride(!controller.getFinishTargetIsLauncher());
+        }
+
+        private void endGestureStateOverride(boolean finishedToApp) {
+            mCallbacks.removeListener(this);
+
+            // Update the resumed state immediately to ensure a seamless handoff
+            boolean launcherResumed = !finishedToApp;
+            updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false);
+            updateStateForFlag(FLAG_RESUMED, launcherResumed);
+            applyState();
+            // Set this last because applyState() might also animate it.
+            mIconAlignmentForResumedState.cancelAnimation();
+            mIconAlignmentForResumedState.updateValue(launcherResumed ? 1 : 0);
+
+            TaskbarStashController controller = mControllers.taskbarStashController;
+            controller.updateStateForFlag(FLAG_IN_APP, finishedToApp);
+            controller.applyState();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index d026bfb..bec717d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -16,43 +16,71 @@
 package com.android.launcher3.taskbar;
 
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
 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.os.Handler;
+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;
@@ -64,11 +92,49 @@
         mSysUINavigationMode = SysUINavigationMode.INSTANCE.get(service);
         Display display =
                 service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
-        mContext = service.createWindowContext(display, TYPE_APPLICATION_OVERLAY, null);
-        mNavButtonController = new TaskbarNavButtonController(service);
+        mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
+        mNavButtonController = new TaskbarNavButtonController(service,
+                SystemUiProxy.INSTANCE.get(mContext), new Handler());
+        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 | ActivityInfo.CONFIG_UI_MODE;
+                if ((configDiff & configsRequiringRecreate) != 0) {
+                    // Color has changed, recreate taskbar to reload background color & icons.
+                    recreateTaskbar();
+                } else {
+                    // Config change might be handled without re-creating the taskbar
+                    if (mTaskbarActivityContext != null) {
+                        DeviceProfile dp = mUserUnlocked
+                                ? LauncherAppState.getIDP(mContext).getDeviceProfile(mContext)
+                                : null;
+
+                        if (dp != null && dp.isTaskbarPresent) {
+                            mTaskbarActivityContext.updateDeviceProfile(dp.copy(mContext));
+                        }
+                        mTaskbarActivityContext.onConfigurationChanged(configDiff);
+                    }
+                }
+                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 +166,120 @@
     }
 
     /**
-     * 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, false /* fromInit */);
         }
     }
 
     /**
-     * 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);
+        }
+    }
+
+    public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onNavButtonsDarkIntensityChanged(darkIntensity);
         }
     }
 
@@ -165,5 +290,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..37a9b5e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -0,0 +1,199 @@
+/*
+ * 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.ComponentKey;
+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.HashMap;
+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);
+
+        final boolean finalIsHotseatEmpty = isHotseatEmpty;
+        mControllers.runAfterInit(() -> {
+            mControllers.taskbarStashController.updateStateForFlag(
+                    TaskbarStashController.FLAG_STASHED_IN_APP_EMPTY, finalIsHotseatEmpty);
+            mControllers.taskbarStashController.applyState();
+        });
+    }
+
+    @Override
+    public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
+        mControllers.taskbarPopupController.setDeepShortcutMap(deepShortcutMapCopy);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 3b5afad..d233365 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -16,13 +16,18 @@
 
 package com.android.launcher3.taskbar;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 
-import android.content.Intent;
-import android.view.inputmethod.InputMethodManager;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+
+import android.os.Bundle;
+import android.os.Handler;
 
 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;
@@ -34,17 +39,23 @@
  * Controller for 3 button mode in the taskbar.
  * Handles all the functionality of the various buttons, making/routing the right calls into
  * launcher or sysui/system.
- *
- * TODO: Create callbacks to hook into UI layer since state will change for more context buttons/
- *       assistant invocation.
  */
 public class TaskbarNavButtonController {
+
+    /** Allow some time in between the long press for back and recents. */
+    static final int SCREEN_PIN_LONG_PRESS_THRESHOLD = 200;
+    static final int SCREEN_PIN_LONG_PRESS_RESET = SCREEN_PIN_LONG_PRESS_THRESHOLD + 100;
+
+    private long mLastScreenPinLongPress;
+    private boolean mScreenPinned;
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
             BUTTON_BACK,
             BUTTON_HOME,
             BUTTON_RECENTS,
-            BUTTON_IME_SWITCH
+            BUTTON_IME_SWITCH,
+            BUTTON_A11Y,
     })
 
     public @interface TaskbarButton {}
@@ -53,11 +64,22 @@
     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;
+
+    private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS;
+    private int mLongPressedButtons = 0;
 
     private final TouchInteractionService mService;
+    private final SystemUiProxy mSystemUiProxy;
+    private final Handler mHandler;
 
-    public TaskbarNavButtonController(TouchInteractionService service) {
+    private final Runnable mResetLongPress = this::resetScreenUnpin;
+
+    public TaskbarNavButtonController(TouchInteractionService service,
+            SystemUiProxy systemUiProxy, Handler handler) {
         mService = service;
+        mSystemUiProxy = systemUiProxy;
+        mHandler = handler;
     }
 
     public void onButtonClick(@TaskbarButton int buttonType) {
@@ -69,32 +91,115 @@
                 navigateHome();
                 break;
             case BUTTON_RECENTS:
-                navigateToOverview();;
+                navigateToOverview();
                 break;
             case BUTTON_IME_SWITCH:
                 showIMESwitcher();
                 break;
+            case BUTTON_A11Y:
+                notifyA11yClick(false /* longClick */);
+                break;
         }
     }
 
+    public boolean onButtonLongClick(@TaskbarButton int buttonType) {
+        switch (buttonType) {
+            case BUTTON_HOME:
+                startAssistant();
+                return true;
+            case BUTTON_A11Y:
+                notifyA11yClick(true /* longClick */);
+                return true;
+            case BUTTON_BACK:
+            case BUTTON_RECENTS:
+                mLongPressedButtons |= buttonType;
+                return determineScreenUnpin();
+            case BUTTON_IME_SWITCH:
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Checks if the user has long pressed back and recents buttons
+     * "together" (within {@link #SCREEN_PIN_LONG_PRESS_THRESHOLD})ms
+     * If so, then requests the system to turn off screen pinning.
+     *
+     * @return true if the long press is a valid user action in attempting to unpin an app
+     *         Will always return {@code false} when screen pinning is not active.
+     *         NOTE: Returning true does not mean that screen pinning has stopped
+     */
+    private boolean determineScreenUnpin() {
+        long timeNow = System.currentTimeMillis();
+        if (!mScreenPinned) {
+            return false;
+        }
+
+        if (mLastScreenPinLongPress == 0) {
+            // First button long press registered, just mark time and wait for second button press
+            mLastScreenPinLongPress = System.currentTimeMillis();
+            mHandler.postDelayed(mResetLongPress, SCREEN_PIN_LONG_PRESS_RESET);
+            return true;
+        }
+
+        if ((timeNow - mLastScreenPinLongPress) > SCREEN_PIN_LONG_PRESS_THRESHOLD) {
+            // Too long in-between presses, reset the clock
+            resetScreenUnpin();
+            return false;
+        }
+
+        if ((mLongPressedButtons & SCREEN_UNPIN_COMBO) == SCREEN_UNPIN_COMBO) {
+            // Hooray! They did it (finally...)
+            mSystemUiProxy.stopScreenPinning();
+            mHandler.removeCallbacks(mResetLongPress);
+            resetScreenUnpin();
+        }
+        return true;
+    }
+
+    private void resetScreenUnpin() {
+        mLongPressedButtons = 0;
+        mLastScreenPinLongPress = 0;
+    }
+
+    public void updateSysuiFlags(int sysuiFlags) {
+        mScreenPinned = (sysuiFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
+    }
+
     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);
+        if (mScreenPinned) {
+            return;
+        }
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
+        mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE);
     }
 
     private void executeBack() {
-        SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
+        mSystemUiProxy.onBackPressed();
     }
 
     private void showIMESwitcher() {
-        mService.getSystemService(InputMethodManager.class)
-                .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
-                        DEFAULT_DISPLAY);
+        mSystemUiProxy.onImeSwitcherPressed();
+    }
+
+    private void notifyA11yClick(boolean longClick) {
+        if (longClick) {
+            mSystemUiProxy.notifyAccessibilityButtonLongClicked();
+        } else {
+            mSystemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
+        }
+    }
+
+    private void startAssistant() {
+        if (mScreenPinned) {
+            return;
+        }
+        Bundle args = new Bundle();
+        args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+        mSystemUiProxy.startAssistant(args);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
new file mode 100644
index 0000000..952f597
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.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 androidx.annotation.NonNull;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.views.ActivityContext;
+
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Implements interfaces required to show and allow interacting with a PopupContainerWithArrow.
+ */
+public class TaskbarPopupController {
+
+    private static final SystemShortcut.Factory<TaskbarActivityContext>
+            APP_INFO = SystemShortcut.AppInfo::new;
+
+    private final PopupDataProvider mPopupDataProvider;
+
+    public TaskbarPopupController() {
+        // TODO (b/198438631): add notifications dots change listener
+        mPopupDataProvider = new PopupDataProvider(packageUserKey -> {});
+    }
+
+    @NonNull
+    public PopupDataProvider getPopupDataProvider() {
+        return mPopupDataProvider;
+    }
+
+    public void setDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
+        mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
+    }
+
+    /**
+     * Shows the notifications and deep shortcuts associated with a Taskbar {@param icon}.
+     * @return the container if shown or null.
+     */
+    public PopupContainerWithArrow<TaskbarActivityContext> showForIcon(BubbleTextView icon) {
+        TaskbarActivityContext context = ActivityContext.lookupContext(icon.getContext());
+        if (PopupContainerWithArrow.getOpen(context) != null) {
+            // There is already an items container open, so don't open this one.
+            icon.clearFocus();
+            return null;
+        }
+        ItemInfo item = (ItemInfo) icon.getTag();
+        if (!PopupContainerWithArrow.canShow(icon, item)) {
+            return null;
+        }
+
+        final PopupContainerWithArrow<TaskbarActivityContext> container =
+                (PopupContainerWithArrow) context.getLayoutInflater().inflate(
+                        R.layout.popup_container, context.getDragLayer(), false);
+        // TODO (b/198438631): configure for taskbar/context
+
+        container.populateAndShow(icon,
+                mPopupDataProvider.getShortcutCountForItem(item),
+                mPopupDataProvider.getNotificationKeysForItem(item),
+                // TODO (b/198438631): add support for INSTALL shortcut factory
+                Stream.of(APP_INFO)
+                        .map(s -> s.getShortcut(context, item))
+                        .filter(Objects::nonNull)
+                        .collect(Collectors.toList()));
+        container.requestFocus();
+        return container;
+    }
+}
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..4b4ee44
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -0,0 +1,97 @@
+/*
+ * 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, boolean skipAnim) {
+        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, skipAnim);
+    }
+
+    private void showScrim(boolean showScrim, float alpha, boolean skipAnim) {
+        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();
+        if (skipAnim) {
+            anim.end();
+        }
+    }
+
+    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..7f75850
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -0,0 +1,592 @@
+/*
+ * 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_IME_SHOWING;
+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_STASHED_IN_APP_IME = 1 << 5; // IME is visible
+    public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 6;
+
+    // If we're in an app and any of these flags are enabled, taskbar should be stashed.
+    private 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
+            | FLAG_STASHED_IN_APP_IME;
+
+    // If any of these flags are enabled, inset apps by our stashed height instead of our unstashed
+    // height. This way the reported insets are consistent even during transitions out of the app.
+    // Currently any flag that causes us to stash in an app is included, except for IME since that
+    // covers the underlying app anyway and thus the app shouldn't change insets.
+    private static final int FLAGS_REPORT_STASHED_INSETS_TO_APP = FLAGS_STASHED_IN_APP
+            & ~FLAG_STASHED_IN_APP_IME;
+
+    /**
+     * How long to stash/unstash when manually invoked via long press.
+     */
+    public static final long TASKBAR_STASH_DURATION = 300;
+
+    /**
+     * How long to stash/unstash when keyboard is appearing/disappearing.
+     */
+    private static final long TASKBAR_STASH_DURATION_FOR_IME = 80;
+
+    /**
+     * 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;
+    private final SystemUiProxy mSystemUiProxy;
+
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+    // Taskbar background properties.
+    private AnimatedFloat mTaskbarBackgroundOffset;
+    private AnimatedFloat mTaskbarImeBgAlpha;
+    // 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;
+    private boolean mIsSystemGestureInProgress;
+    private boolean mIsImeShowing;
+
+    // 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);
+        mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
+        mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
+    }
+
+    public void init(TaskbarControllers controllers, TaskbarSharedState sharedState) {
+        mControllers = controllers;
+
+        TaskbarDragLayerController dragLayerController = controllers.taskbarDragLayerController;
+        mTaskbarBackgroundOffset = dragLayerController.getTaskbarBackgroundOffset();
+        mTaskbarImeBgAlpha = dragLayerController.getImeBgTaskbar();
+
+        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);
+        boolean isInSetup = !mActivity.isUserSetupComplete() || sharedState.setupUIVisible;
+        updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+        // TODO(b/204384193): Temporarily disable SUW specific logic
+        // updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
+        if (isInSetup) {
+            // Update the in-app state to ensure isStashed() reflects right state during SUW
+            updateStateForFlag(FLAG_IN_APP, true);
+        }
+        applyState();
+
+        notifyStashChange(/* 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);
+    }
+
+    /**
+     * Returns whether the taskbar should be stashed in the current LauncherState.
+     */
+    public boolean isInStashedLauncherState() {
+        return hasAnyFlag(FLAG_IN_STASHED_LAUNCHER_STATE) && supportsVisualStashing();
+    }
+
+    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;
+    }
+
+    /**
+     * Returns the height that taskbar will inset when inside apps.
+     */
+    public int getContentHeightToReportToApps() {
+        if (hasAnyFlag(FLAGS_REPORT_STASHED_INSETS_TO_APP)) {
+            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
+     * @param startDelay how many milliseconds to delay the animation after starting it.
+     */
+    private void createAnimToIsStashed(boolean isStashed, long duration, long startDelay) {
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+        mAnimator = new AnimatorSet();
+
+        if (!supportsVisualStashing()) {
+            // Just hide/show the icons and background instead of stashing into a handle.
+            mAnimator.play(mIconAlphaForStash.animateToValue(isStashed ? 0 : 1)
+                    .setDuration(duration));
+            mAnimator.play(mTaskbarImeBgAlpha.animateToValue(
+                    hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration));
+            mAnimator.setStartDelay(startDelay);
+            mAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mAnimator = null;
+                }
+            });
+            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)
+            );
+        }
+
+        fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
+                .createRevealAnimToIsStashed(isStashed));
+        // 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.setStartDelay(startDelay);
+        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 void applyState(long duration, long startDelay) {
+        mStatePropertyHolder.setState(mState, duration, startDelay, true);
+    }
+
+    public Animator applyStateWithoutStart() {
+        return applyStateWithoutStart(TASKBAR_STASH_DURATION);
+    }
+
+    public Animator applyStateWithoutStart(long duration) {
+        return mStatePropertyHolder.setState(mState, duration, false);
+    }
+
+    /**
+     * Should be called when a system gesture starts and settles, so we can defer updating
+     * FLAG_STASHED_IN_APP_IME until after the gesture transition completes.
+     */
+    public void setSystemGestureInProgress(boolean inProgress) {
+        mIsSystemGestureInProgress = inProgress;
+        // Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
+        if (!mIsSystemGestureInProgress) {
+            updateStateForFlag(FLAG_STASHED_IN_APP_IME, mIsImeShowing);
+            applyState(TASKBAR_STASH_DURATION_FOR_IME, getTaskbarStashStartDelayForIme());
+        }
+    }
+
+    /**
+     * When hiding the IME, delay the unstash animation to align with the end of the transition.
+     */
+    private long getTaskbarStashStartDelayForIme() {
+        if (mIsImeShowing) {
+            // Only delay when IME is exiting, not entering.
+            return 0;
+        }
+        // This duration is based on input_method_extract_exit.xml.
+        long imeExitDuration = mControllers.taskbarActivityContext.getResources()
+                .getInteger(android.R.integer.config_shortAnimTime);
+        return imeExitDuration - TASKBAR_STASH_DURATION_FOR_IME;
+    }
+
+    /** Called when some system ui state has changed. (See SYSUI_STATE_... in QuickstepContract) */
+    public void updateStateForSysuiFlags(int systemUiStateFlags, boolean skipAnim) {
+        long animDuration = TASKBAR_STASH_DURATION;
+        long startDelay = 0;
+
+        updateStateForFlag(FLAG_STASHED_IN_APP_PINNED,
+                hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
+
+        // Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
+        mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
+        if (!mIsSystemGestureInProgress) {
+            updateStateForFlag(FLAG_STASHED_IN_APP_IME, mIsImeShowing);
+            animDuration = TASKBAR_STASH_DURATION_FOR_IME;
+            startDelay = getTaskbarStashStartDelayForIme();
+        }
+
+        applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
+    }
+
+    /**
+     * 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)) {
+            notifyStashChange(/* 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 void notifyStashChange(boolean visible, boolean stashed) {
+        mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
+        mControllers.rotationButtonController.onTaskbarStateChange(visible, stashed);
+    }
+
+    private class StatePropertyHolder {
+        private final IntPredicate mStashCondition;
+
+        private boolean mIsStashed;
+        private int mPrevFlags;
+
+        StatePropertyHolder(IntPredicate stashCondition) {
+            mStashCondition = stashCondition;
+        }
+
+        /**
+         * @see #setState(int, long, long, boolean) with a default startDelay = 0.
+         */
+        public Animator setState(int flags, long duration, boolean start) {
+            return setState(flags, duration, 0 /* startDelay */, start);
+        }
+
+        /**
+         * Applies the latest state, potentially calling onStateChangeApplied() and creating a new
+         * animation (stored in mAnimator) which is started if {@param start} is true.
+         * @param flags The latest flags to apply (see the top of this file).
+         * @param duration The length of the animation.
+         * @param startDelay How long to delay the animation after calling start().
+         * @param start Whether to start mAnimator immediately.
+         * @return mAnimator if mIsStashed changed, else null.
+         */
+        public Animator setState(int flags, long duration, long startDelay, boolean start) {
+            int changedFlags = mPrevFlags ^ flags;
+            if (mPrevFlags != flags) {
+                onStateChangeApplied(changedFlags);
+                mPrevFlags = flags;
+            }
+            boolean isStashed = mStashCondition.test(flags);
+            if (mIsStashed != isStashed) {
+                mIsStashed = isStashed;
+
+                // This sets mAnimator.
+                createAnimToIsStashed(mIsStashed, duration, startDelay);
+                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
deleted file mode 100644
index a701aae..0000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
+++ /dev/null
@@ -1,75 +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 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;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.quickstep.AnimatedFloat;
-
-/**
- * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
- * isn't present (i.e. {@link #setAnimationController} is never called).
- */
-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;
-
-    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);
-    }
-
-    @Override
-    public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
-            PendingAnimation animation) {
-        setState(toState, animation);
-    }
-
-    private void setState(LauncherState toState, PropertySetter setter) {
-        if (mAnimationController == null) {
-            return;
-        }
-
-        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);
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 50adead..abad906 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -15,6 +15,15 @@
  */
 package com.android.launcher3.taskbar;
 
+import android.view.View;
+
+import androidx.annotation.CallSuper;
+
+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,20 +31,40 @@
 
     public static final TaskbarUIController DEFAULT = new TaskbarUIController();
 
-    /**
-     * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
-     */
-    public void alignRealHotseatWithTaskbar() { }
+    // Initialized in init.
+    protected TaskbarControllers mControllers;
 
-    protected void onCreate() { }
+    @CallSuper
+    protected void init(TaskbarControllers taskbarControllers) {
+        mControllers = taskbarControllers;
+    }
 
-    protected void onDestroy() { }
+    @CallSuper
+    protected void onDestroy() {
+        mControllers = null;
+    }
 
     protected boolean isTaskbarTouchable() {
         return true;
     }
 
-    protected void onImeVisible(TaskbarDragLayer container, boolean isVisible) {
-        container.updateImeBarVisibilityAlpha(isVisible ? 1 : 0);
+    protected void onStashedInAppChanged() { }
+
+    public Stream<ItemInfoWithIcon> getAppIconsForEdu() {
+        return Stream.empty();
+    }
+
+    public void onTaskbarIconLaunched(WorkspaceItemInfo item) { }
+
+    public View getRootView() {
+        return mControllers.taskbarActivityContext.getDragLayer();
+    }
+
+    /**
+     * Called when swiping from the bottom nav region in fully gestural mode.
+     * @param inProgress True if the animation started, false if we just settled on an end target.
+     */
+    public void setSystemGestureInProgress(boolean inProgress) {
+        mControllers.taskbarStashController.setSystemGestureInProgress(inProgress);
     }
 }
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..445a23b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -0,0 +1,335 @@
+/*
+ * 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_KEYGUARD = 1;
+    public static final int ALPHA_INDEX_STASH = 2;
+    public static final int ALPHA_INDEX_RECENTS_DISABLED = 3;
+    public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4;
+    private static final int NUM_ALPHA_CHANNELS = 5;
+
+    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 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.getItemOnClickListener();
+        }
+
+        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/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..0eaea83 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -17,6 +17,8 @@
 package com.android.launcher3.uioverrides;
 
 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;
@@ -27,8 +29,6 @@
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
-import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
 import android.util.FloatProperty;
@@ -40,7 +40,6 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -97,13 +96,6 @@
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
-        PagedOrientationHandler orientationHandler =
-                ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
-        FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset(
-                TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
-                mLauncher.getDeviceProfile());
-        setter.setFloat(mRecentsView, taskViewsFloat,
-                toState.getSplitSelectTranslation(mLauncher), LINEAR);
 
         setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
                 config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
@@ -112,8 +104,9 @@
                 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);
     }
 
     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..32ce1c4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -18,20 +18,22 @@
 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.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
 
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.util.FloatProperty;
+import android.util.Pair;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
@@ -39,6 +41,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.quickstep.views.ClearAllButton;
 import com.android.quickstep.views.LauncherRecentsView;
@@ -65,6 +68,11 @@
         }
         setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig(), state);
         mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
+        // In Overview, we may be layering app surfaces behind Launcher, so we need to notify
+        // DepthController to prevent optimizations which might occlude the layers behind
+        mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
+
+        handleSplitSelectionState(state, null);
     }
 
     @Override
@@ -80,14 +88,12 @@
             builder.addListener(
                     AnimatorListeners.forSuccessCallback(mRecentsView::resetTaskVisuals));
         }
+        // In Overview, we may be layering app surfaces behind Launcher, so we need to notify
+        // DepthController to prevent optimizations which might occlude the layers behind
+        builder.addListener(AnimatorListeners.forSuccessCallback(() ->
+                mLauncher.getDepthController().setHasContentBehindLauncher(toState.overviewUi)));
 
-        // Create or dismiss split screen select animations
-        LauncherState currentState = mLauncher.getStateManager().getState();
-        if (isSplitSelectionState(toState) && !isSplitSelectionState(currentState)) {
-            builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
-        } else if (!isSplitSelectionState(toState) && isSplitSelectionState(currentState)) {
-            builder.add(mRecentsView.cancelSplitSelect(true).buildAnim());
-        }
+        handleSplitSelectionState(toState, builder);
 
         setAlphas(builder, config, toState);
         builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
@@ -95,10 +101,52 @@
     }
 
     /**
-     * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+     * Create or dismiss split screen select animations.
+     * @param builder if null then this will run the split select animations right away, otherwise
+     *                will add animations to builder.
      */
-    private boolean isSplitSelectionState(@NonNull LauncherState toState) {
-        return toState == OVERVIEW_SPLIT_SELECT;
+    private void handleSplitSelectionState(@NonNull LauncherState toState,
+            @Nullable PendingAnimation builder) {
+        LauncherState currentState = mLauncher.getStateManager().getState();
+        boolean animate = builder != null;
+        PagedOrientationHandler orientationHandler =
+                ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
+        Pair<FloatProperty, FloatProperty> taskViewsFloat =
+                orientationHandler.getSplitSelectTaskOffset(
+                        TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+                        mLauncher.getDeviceProfile());
+
+        if (isSplitSelectionState(currentState, toState)) {
+            // Animation to "dismiss" selected taskView
+            PendingAnimation splitSelectInitAnimation =
+                    mRecentsView.createSplitSelectInitAnimation();
+            // Add properties to shift remaining taskViews to get out of placeholder view
+            splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.first,
+                    toState.getSplitSelectTranslation(mLauncher), LINEAR);
+            splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
+
+            if (!animate && isSplitSelectionState(currentState, toState)) {
+                splitSelectInitAnimation.buildAnim().start();
+            } else if (animate &&
+                    isSplitSelectionState(currentState, toState)) {
+                builder.add(splitSelectInitAnimation.buildAnim());
+            }
+        }
+
+        if (isSplitSelectionState(currentState, toState)) {
+            mRecentsView.applySplitPrimaryScrollOffset();
+        } else {
+            mRecentsView.resetSplitPrimaryScrollOffset();
+        }
+    }
+
+    /**
+     * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+     *          and {@param fromState} is not {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+     */
+    private boolean isSplitSelectionState(@NonNull LauncherState fromState,
+            @NonNull LauncherState toState) {
+        return fromState != OVERVIEW_SPLIT_SELECT && toState == OVERVIEW_SPLIT_SELECT;
     }
 
     private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
@@ -106,16 +154,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..8f89d30 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(Launcher launcher) {
+        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..b733007 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;
@@ -73,8 +74,7 @@
         return super.getVisibleElements(launcher)
                 & ~OVERVIEW_ACTIONS
                 & ~CLEAR_ALL_BUTTON
-                & ~VERTICAL_SWIPE_INDICATOR
-                | TASKBAR;
+                & ~VERTICAL_SWIPE_INDICATOR;
     }
 
     @Override
@@ -89,6 +89,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..08d0a80 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -21,14 +21,11 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.SystemProperties;
-import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 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;
@@ -67,10 +64,7 @@
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
         RecentsView recentsView = launcher.getOverviewPanel();
-        Workspace workspace = launcher.getWorkspace();
-        View workspacePage = workspace.getPageAt(workspace.getCurrentPage());
-        float workspacePageWidth = workspacePage != null && workspacePage.getWidth() != 0
-                ? workspacePage.getWidth() : launcher.getDeviceProfile().availableWidthPx;
+        float workspacePageWidth = launcher.getDeviceProfile().getWorkspaceWidth();
         recentsView.getTaskSize(sTempRect);
         float scale = (float) sTempRect.width() / workspacePageWidth;
         float parallaxFactor = 0.5f;
@@ -83,16 +77,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 +92,18 @@
     }
 
     @Override
+    public boolean isTaskbarStashed(Launcher launcher) {
+        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/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index d36e76b..969abc2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.util.Themes;
@@ -42,6 +43,10 @@
 
     @Override
     public int getWorkspaceScrimColor(Launcher launcher) {
+        DeviceProfile dp = launcher.getDeviceProfile();
+        if (dp.isTaskbarPresentInApps) {
+            return launcher.getColor(R.color.taskbar_background);
+        }
         return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
     }
 
@@ -53,6 +58,16 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return TASKBAR;
+        return NONE;
+    }
+
+    @Override
+    public boolean isTaskbarStashed(Launcher launcher) {
+        return !launcher.getDeviceProfile().isTaskbarPresentInApps;
+    }
+
+    @Override
+    public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
+        return false;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 0e2fbbc..75cf5cb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -17,6 +17,7 @@
 
 import static android.view.View.VISIBLE;
 
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -44,6 +45,10 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_OPAQUE_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_VISIBLE_THRESHOLD;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 
 import android.animation.ValueAnimator;
@@ -52,6 +57,7 @@
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.quickstep.SysUINavigationMode;
@@ -171,6 +177,13 @@
                 mHintToNormalDuration = (int) va.getDuration();
             }
             config.duration = Math.max(config.duration, mHintToNormalDuration);
+        } else if (fromState == ALL_APPS && toState == NORMAL) {
+            config.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
+                    1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
+                    1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
+            config.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(DEACCEL,
+                    1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
+                    1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index 6968494..e79d56b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -17,8 +17,6 @@
 package com.android.launcher3.uioverrides.states;
 
 import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -31,11 +29,6 @@
     }
 
     @Override
-    public void onBackPressed(Launcher launcher) {
-        launcher.getStateManager().goToState(OVERVIEW);
-    }
-
-    @Override
     public int getVisibleElements(Launcher launcher) {
         return SPLIT_PLACHOLDER_VIEW;
     }
@@ -43,13 +36,6 @@
     @Override
     public float getSplitSelectTranslation(Launcher launcher) {
         RecentsView recentsView = launcher.getOverviewPanel();
-        int splitPosition = recentsView.getSplitPlaceholder().getSplitController()
-                .getActiveSplitPositionOption().mStagePosition;
-        if (!recentsView.shouldShiftThumbnailsForSplitSelect(splitPosition)) {
-            return 0f;
-        }
-        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
-        int direction = orientationHandler.getSplitTranslationDirectionFactor(splitPosition);
-        return launcher.getResources().getDimension(R.dimen.split_placeholder_size) * direction;
+        return recentsView.getSplitSelectTranslation();
     }
 }
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..f6148a7 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
@@ -235,8 +236,10 @@
         //   - RecentsView fade (if it's empty)
         PendingAnimation xAnim = new PendingAnimation((long) (mXRange * 2));
         xAnim.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1], LINEAR);
+        // Use QuickSwitchState instead of OverviewState to determine scrim color,
+        // since we need to take potential taskbar into account.
         xAnim.setViewBackgroundColor(mLauncher.getScrimView(),
-                toState.getWorkspaceScrimColor(mLauncher), LINEAR);
+                QUICK_SWITCH.getWorkspaceScrimColor(mLauncher), LINEAR);
         if (mRecentsView.getTaskViewCount() == 0) {
             xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
         }
@@ -309,6 +312,11 @@
                 }
             });
             overviewAnim.start();
+
+            // Create an empty state transition so StateListeners get onStateTransitionStart().
+            mLauncher.getStateManager().createAnimationToNewWorkspace(
+                    OVERVIEW, config.duration, StateAnimationConfig.SKIP_ALL_ANIMATIONS)
+                    .dispatchOnStart();
             return;
         }
 
@@ -383,6 +391,7 @@
             config.animFlags = SKIP_ALL_ANIMATIONS;
             updateNonOverviewAnim(targetState, config);
             nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
+            mNonOverviewAnim.dispatchOnStart();
 
             new WorkspaceRevealAnim(mLauncher, false /* animateOverviewScrim */).start();
         } else {
@@ -398,6 +407,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/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 3c83d25..59ade49 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -52,22 +52,22 @@
     /**
      * The progress at which all apps content will be fully visible.
      */
-    protected static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
+    public static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
 
     /**
      * Minimum clamping progress for fading in all apps content
      */
-    protected static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
+    public static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
 
     /**
      * Minimum clamping progress for fading in all apps scrim
      */
-    protected static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f;
+    public static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f;
 
     /**
      * Maximum clamping progress for opaque all apps scrim
      */
-    protected static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
+    public static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
 
     private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
 
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 9748123..2f52c9d 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;
@@ -55,6 +55,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
@@ -73,6 +74,7 @@
 import android.view.WindowInsets;
 import android.view.animation.Interpolator;
 import android.widget.Toast;
+import android.window.PictureInPictureSurfaceTransaction;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -89,16 +91,18 @@
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.tracing.InputConsumerProto;
 import com.android.launcher3.tracing.SwipeHandlerProto;
+import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
 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 +110,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;
@@ -119,6 +124,8 @@
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.function.Consumer;
 
 /**
@@ -149,6 +156,17 @@
     protected MultiStateCallback mStateCallback;
     protected boolean mCanceled;
     private boolean mRecentsViewScrollLinked = false;
+    private final ActivityLifecycleCallbacksAdapter mLifecycleCallbacks =
+            new ActivityLifecycleCallbacksAdapter() {
+                @Override
+                public void onActivityDestroyed(Activity activity) {
+                    if (mActivity != activity) {
+                        return;
+                    }
+                    mRecentsView = null;
+                    mActivity = null;
+                }
+            };
 
     private static int getFlagForIndex(int index, String name) {
         if (DEBUG_STATES) {
@@ -162,45 +180,51 @@
             getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
     protected static final int STATE_LAUNCHER_STARTED =
             getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
-    protected static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+    protected static final int STATE_LAUNCHER_DRAWN =
+            getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+    // Called when the Launcher has connected to the touch interaction service (and the taskbar
+    // ui controller is initialized)
+    protected static final int STATE_LAUNCHER_BIND_TO_SERVICE =
+            getFlagForIndex(3, "STATE_LAUNCHER_BIND_TO_SERVICE");
 
     // Internal initialization states
     private static final int STATE_APP_CONTROLLER_RECEIVED =
-            getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED");
+            getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
 
     // Interaction finish states
     private static final int STATE_SCALED_CONTROLLER_HOME =
-            getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME");
+            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_HOME");
     private static final int STATE_SCALED_CONTROLLER_RECENTS =
-            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS");
+            getFlagForIndex(6, "STATE_SCALED_CONTROLLER_RECENTS");
 
     protected static final int STATE_HANDLER_INVALIDATED =
-            getFlagForIndex(6, "STATE_HANDLER_INVALIDATED");
+            getFlagForIndex(7, "STATE_HANDLER_INVALIDATED");
     private static final int STATE_GESTURE_STARTED =
-            getFlagForIndex(7, "STATE_GESTURE_STARTED");
+            getFlagForIndex(8, "STATE_GESTURE_STARTED");
     private static final int STATE_GESTURE_CANCELLED =
-            getFlagForIndex(8, "STATE_GESTURE_CANCELLED");
+            getFlagForIndex(9, "STATE_GESTURE_CANCELLED");
     private static final int STATE_GESTURE_COMPLETED =
-            getFlagForIndex(9, "STATE_GESTURE_COMPLETED");
+            getFlagForIndex(10, "STATE_GESTURE_COMPLETED");
 
     private static final int STATE_CAPTURE_SCREENSHOT =
-            getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT");
+            getFlagForIndex(11, "STATE_CAPTURE_SCREENSHOT");
     protected static final int STATE_SCREENSHOT_CAPTURED =
-            getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED");
+            getFlagForIndex(12, "STATE_SCREENSHOT_CAPTURED");
     private static final int STATE_SCREENSHOT_VIEW_SHOWN =
-            getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN");
+            getFlagForIndex(13, "STATE_SCREENSHOT_VIEW_SHOWN");
 
     private static final int STATE_RESUME_LAST_TASK =
-            getFlagForIndex(13, "STATE_RESUME_LAST_TASK");
+            getFlagForIndex(14, "STATE_RESUME_LAST_TASK");
     private static final int STATE_START_NEW_TASK =
-            getFlagForIndex(14, "STATE_START_NEW_TASK");
+            getFlagForIndex(15, "STATE_START_NEW_TASK");
     private static final int STATE_CURRENT_TASK_FINISHED =
-            getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED");
+            getFlagForIndex(16, "STATE_CURRENT_TASK_FINISHED");
     private static final int STATE_FINISH_WITH_NO_END =
-            getFlagForIndex(16, "STATE_FINISH_WITH_NO_END");
+            getFlagForIndex(17, "STATE_FINISH_WITH_NO_END");
 
     private static final int LAUNCHER_UI_STATES =
-            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
+            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED |
+                    STATE_LAUNCHER_BIND_TO_SERVICE;
 
     public static final long MAX_SWIPE_DURATION = 350;
     public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS;
@@ -212,6 +236,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,9 +246,11 @@
     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;
+    // Current running divider animation
+    private ValueAnimator mDividerAnimator;
     private boolean mIsMotionPaused;
     private boolean mHasMotionEverBeenPaused;
 
@@ -251,22 +279,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();
@@ -384,9 +423,10 @@
         // Set up a entire animation lifecycle callback to notify the current recents view when
         // the animation is canceled
         mGestureState.runOnceAtState(STATE_RECENTS_ANIMATION_CANCELED, () -> {
-                ThumbnailData snapshot = mGestureState.consumeRecentsAnimationCanceledSnapshot();
-                if (snapshot != null) {
-                    mRecentsView.switchToScreenshot(snapshot, () -> {
+                HashMap<Integer, ThumbnailData> snapshots =
+                        mGestureState.consumeRecentsAnimationCanceledSnapshot();
+                if (snapshots != null) {
+                    mRecentsView.switchToScreenshot(snapshots, () -> {
                         if (mRecentsAnimationController != null) {
                             mRecentsAnimationController.cleanupScreenshot();
                         }
@@ -397,7 +437,9 @@
 
         setupRecentsViewUi();
         linkRecentsViewScroll();
+        activity.runOnBindToTouchInteractionService(this::onLauncherBindToService);
 
+        mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks);
         return true;
     }
 
@@ -419,7 +461,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.
@@ -476,6 +519,11 @@
         mStateCallback.setState(STATE_LAUNCHER_STARTED);
     }
 
+    private void onLauncherBindToService() {
+        mStateCallback.setState(STATE_LAUNCHER_BIND_TO_SERVICE);
+        flushOnRecentsAnimationAndLauncherBound();
+    }
+
     private void onLauncherPresentAndGestureStarted() {
         // Re-setup the recents UI when gesture starts, as the state could have been changed during
         // that time by a previous window transition.
@@ -516,7 +564,26 @@
     }
 
     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];
+                // Order matters here, we want first indexed RunningTaskInfo to be leftTop task
+                for (ActivityManager.RunningTaskInfo rti : mGestureState.getRunningTasks()) {
+                    if (taskId == rti.taskId) {
+                        runningTasks[i] = rti;
+                        break;
+                    }
+
+                }
+            }
+        } else {
+            runningTasks = new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()};
+        }
+        mRecentsView.onGestureAnimationStart(runningTasks);
     }
 
     private void launcherFrameDrawn() {
@@ -543,7 +610,7 @@
             @Override
             public void onMotionPauseDetected() {
                 mHasMotionEverBeenPaused = true;
-                maybeUpdateRecentsAttachedState();
+                maybeUpdateRecentsAttachedState(true/* animate */, true/* moveFocusedTask */);
                 performHapticFeedback();
             }
 
@@ -554,18 +621,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;
         }
@@ -584,6 +657,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
@@ -591,15 +670,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();
         }
     }
 
@@ -635,13 +714,24 @@
     public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
         WindowInsets result = view.onApplyWindowInsets(windowInsets);
         buildAnimationController();
+        // Reapply the current shift to ensure it takes new insets into account, e.g. when long
+        // pressing to stash taskbar without moving the finger.
+        updateFinalShift();
         return result;
     }
 
     private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) {
+        boolean isFirstCreation = mLauncherTransitionController == null;
         mLauncherTransitionController = anim;
-        mLauncherTransitionController.getNormalController().dispatchOnStart();
-        updateLauncherTransitionProgress();
+        if (isFirstCreation) {
+            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() {
@@ -663,7 +753,7 @@
         }
 
         updateSysUiFlags(mCurrentShift.value);
-        applyWindowTransform();
+        applyScrollAndTransform();
 
         updateLauncherTransitionProgress();
     }
@@ -673,7 +763,8 @@
                 || !canCreateNewOrUpdateExistingLauncherTransitionController()) {
             return;
         }
-        mLauncherTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
+        mLauncherTransitionController.setProgress(
+                Math.max(mCurrentShift.value, getScaleProgressDueToScroll()), mDragLengthFactor);
     }
 
     /**
@@ -708,24 +799,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 {
@@ -735,16 +827,14 @@
             dp.updateInsets(targets.homeContentInsets);
             dp.updateIsSeascape(mContext);
             initTransitionEndpoints(dp);
-            mTaskViewSimulator.getOrientationState().setMultiWindowMode(dp.isMultiWindowMode);
+            orientationState.setMultiWindowMode(dp.isMultiWindowMode);
         }
 
         // Notify when the animation starts
-        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
-            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
-                action.run();
-            }
-            mRecentsAnimationStartCallbacks.clear();
-        }
+        flushOnRecentsAnimationAndLauncherBound();
+
+        // Start hiding the divider
+        setDividerShown(false, false /* immediate */);
 
         // Only add the callback to enable the input consumer after we actually have the controller
         mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
@@ -755,11 +845,15 @@
     }
 
     @Override
-    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+    public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
         ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
         mActivityInitListener.unregister();
         mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
 
+        if (mRecentsAnimationTargets != null) {
+            setDividerShown(true, false /* immediate */);
+        }
+
         // Defer clearing the controller and the targets until after we've updated the state
         mRecentsAnimationController = null;
         mRecentsAnimationTargets = null;
@@ -849,9 +943,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) {
@@ -865,6 +967,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);
@@ -873,11 +978,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
@@ -887,19 +994,28 @@
                 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);
+                }
+                if (mRecentsAnimationTargets != null) {
+                    setDividerShown(true, true /* immediate */);
+                }
                 break;
         }
         ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget);
     }
 
     /** @return Whether this was the task we were waiting to appear, and thus handled it. */
-    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
         if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
             return false;
         }
-        if (mStateCallback.hasStates(STATE_START_NEW_TASK)
-                && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
+        boolean hasStartedTaskBefore = Arrays.stream(appearedTaskTarget).anyMatch(
+                targetCompat -> targetCompat.taskId == mGestureState.getLastStartedTaskId());
+        if (mStateCallback.hasStates(STATE_START_NEW_TASK) && hasStartedTaskBefore) {
             reset();
             return true;
         }
@@ -1025,9 +1141,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.
@@ -1096,7 +1209,7 @@
     @UiThread
     private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
             GestureEndTarget target, PointF velocityPxPerMs) {
-        runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration,
+        runOnRecentsAnimationAndLauncherBound(() -> animateToProgressInternal(start, end, duration,
                 interpolator, target, velocityPxPerMs));
     }
 
@@ -1133,8 +1246,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();
             }
         }
@@ -1150,21 +1270,25 @@
             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;
+            mIsSwipingPipToHome = !mIsSwipeForStagedSplit
+                    && homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
+            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) {
@@ -1178,14 +1302,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();
@@ -1227,24 +1359,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);
@@ -1273,7 +1423,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();
@@ -1303,7 +1453,7 @@
                 mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
             }
         });
-        setupWindowAnimation(swipePipToHomeAnimator);
+        setupWindowAnimation(new RectFSpringAnim[]{swipePipToHomeAnimator});
         return swipePipToHomeAnimator;
     }
 
@@ -1330,19 +1480,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((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) {
@@ -1354,7 +1504,7 @@
             }
         });
         if (mRecentsAnimationTargets != null) {
-            mRecentsAnimationTargets.addReleaseCheck(anim);
+            mRecentsAnimationTargets.addReleaseCheck(anims[0]);
         }
     }
 
@@ -1422,6 +1572,9 @@
 
     private void reset() {
         mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+        if (mActivity != null) {
+            mActivity.unregisterActivityLifecycleCallbacks(mLifecycleCallbacks);
+        }
     }
 
     /**
@@ -1499,6 +1652,10 @@
         boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
         mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget());
 
+        if (mRecentsAnimationTargets != null) {
+            setDividerShown(true, true /* immediate */);
+        }
+
         // Leave the pending invisible flag, as it may be used by wallpaper open animation.
         if (mActivity != null) {
             mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
@@ -1587,7 +1744,7 @@
             // If there are no targets or the animation not started, then there is nothing to finish
             mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
         } else {
-            maybeFinishSwipePipToHome();
+            maybeFinishSwipeToHome();
             finishRecentsControllerToHome(
                     () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
         }
@@ -1596,11 +1753,12 @@
     }
 
     /**
-     * Resets the {@link #mIsSwipingPipToHome} and notifies SysUI that transition is finished
-     * if applicable. This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
+     * Notifies SysUI that transition is finished if applicable and also pass leash transactions
+     * from Launcher to WM.
+     * This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
      */
-    private void maybeFinishSwipePipToHome() {
-        if (mIsSwipingPipToHome && mSwipePipToHomeAnimator != null) {
+    private void maybeFinishSwipeToHome() {
+        if (mIsSwipingPipToHome && mSwipePipToHomeAnimators[0] != null) {
             SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
                     mSwipePipToHomeAnimator.getComponentName(),
                     mSwipePipToHomeAnimator.getDestinationBounds(),
@@ -1610,6 +1768,18 @@
                     mSwipePipToHomeAnimator.getFinishTransaction(),
                     mSwipePipToHomeAnimator.getContentOverlay());
             mIsSwipingPipToHome = false;
+        } else if (mIsSwipeForStagedSplit) {
+            // Transaction to hide the task to avoid flicker for entering PiP from split-screen.
+            PictureInPictureSurfaceTransaction tx =
+                    new PictureInPictureSurfaceTransaction.Builder()
+                            .setAlpha(0f)
+                            .build();
+            int[] taskIds =
+                        LauncherSplitScreenListener.INSTANCE.getNoCreate().getRunningSplitTaskIds();
+            for (int taskId : taskIds) {
+                mRecentsAnimationController.setFinishTaskTransaction(taskId,
+                        tx, null /* overlay */);
+            }
         }
     }
 
@@ -1622,7 +1792,10 @@
         endLauncherTransitionController();
         mRecentsView.onSwipeUpAnimationSuccess();
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mTaskAnimationManager.setLiveTileCleanUpHandler(mInputConsumerProxy::destroy);
+            mTaskAnimationManager.setLiveTileCleanUpHandler(() -> {
+                mRecentsView.cleanupRemoteTargets();
+                mInputConsumerProxy.destroy();
+            });
             mTaskAnimationManager.enableLiveTileRestartListener();
         }
 
@@ -1641,8 +1814,8 @@
      * depend on proper class initialization.
      */
     protected void initAfterSubclassConstructor() {
-        initTransitionEndpoints(
-                mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
+        initTransitionEndpoints(mRemoteTargetHandles[0].getTaskViewSimulator()
+                        .getOrientationState().getLauncherDeviceProfile());
     }
 
     protected void performHapticFeedback() {
@@ -1659,13 +1832,14 @@
 
     protected void linkRecentsViewScroll() {
         SurfaceTransactionApplier.create(mRecentsView, applier -> {
-            mTransformParams.setSyncTransactionApplier(applier);
-            runOnRecentsAnimationStart(() ->
+            runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
+                            .setSyncTransactionApplier(applier));
+            runOnRecentsAnimationAndLauncherBound(() ->
                     mRecentsAnimationTargets.addReleaseCheck(applier));
         });
 
         mRecentsView.addOnScrollChangedListener(mOnRecentsScrollListener);
-        runOnRecentsAnimationStart(() ->
+        runOnRecentsAnimationAndLauncherBound(() ->
                 mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
                         mRecentsAnimationTargets));
         mRecentsViewScrollLinked = true;
@@ -1711,14 +1885,26 @@
     }
 
     /**
-     * Runs the given {@param action} if the recents animation has already started, or queues it to
-     * be run when it is next started.
+     * Runs the given {@param action} if the recents animation has already started and Launcher has
+     * been created and bound to the TouchInteractionService, or queues it to be run when it this
+     * next happens.
      */
-    protected void runOnRecentsAnimationStart(Runnable action) {
-        if (mRecentsAnimationTargets == null) {
-            mRecentsAnimationStartCallbacks.add(action);
-        } else {
-            action.run();
+    private void runOnRecentsAnimationAndLauncherBound(Runnable action) {
+        mRecentsAnimationStartCallbacks.add(action);
+        flushOnRecentsAnimationAndLauncherBound();
+    }
+
+    private void flushOnRecentsAnimationAndLauncherBound() {
+        if (mRecentsAnimationTargets == null ||
+                !mStateCallback.hasStates(STATE_LAUNCHER_BIND_TO_SERVICE)) {
+            return;
+        }
+
+        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+                action.run();
+            }
+            mRecentsAnimationStartCallbacks.clear();
         }
     }
 
@@ -1732,6 +1918,9 @@
 
     @Override
     public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+        if (!controller.getFinishTargetIsLauncher()) {
+            setDividerShown(true, false /* immediate */);
+        }
         mRecentsAnimationController = null;
         mRecentsAnimationTargets = null;
         if (mRecentsView != null) {
@@ -1740,9 +1929,9 @@
     }
 
     @Override
-    public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+    public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
         if (mRecentsAnimationController != null) {
-            if (handleTaskAppeared(appearedTaskTarget)) {
+            if (handleTaskAppeared(appearedTaskTargets)) {
                 mRecentsAnimationController.finish(false /* toRecents */,
                         null /* onFinishComplete */);
                 mActivityInterface.onLaunchTaskSuccess();
@@ -1782,21 +1971,72 @@
     /**
      * 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;
+    }
+
+    private void setDividerShown(boolean shown, boolean immediate) {
+        if (mDividerAnimator != null) {
+            mDividerAnimator.cancel();
+        }
+        mDividerAnimator = TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
+                mRecentsAnimationTargets.nonApps, shown, (dividerAnimator) -> {
+                    dividerAnimator.start();
+                    if (immediate) {
+                        dividerAnimator.end();
+                    }
+                });
+    }
+
     /**
      * Used for winscope tracing, see launcher_trace.proto
      * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
@@ -1818,7 +2058,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..6a7d066 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -42,6 +42,8 @@
 
     private final Runnable mUpdateCallback;
     private ObjectAnimator mValueAnimator;
+    // Only non-null when an animation is playing to this value.
+    private Float mEndValue;
 
     public float value;
 
@@ -53,14 +55,32 @@
         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);
         mValueAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
+            public void onAnimationStart(Animator animator) {
+                if (mValueAnimator == animator) {
+                    mEndValue = end;
+                }
+            }
+
+            @Override
             public void onAnimationEnd(Animator animator) {
                 if (mValueAnimator == animator) {
                     mValueAnimator = null;
+                    mEndValue = null;
                 }
             }
         });
@@ -93,4 +113,15 @@
     public ObjectAnimator getCurrentAnimation() {
         return mValueAnimator;
     }
+
+    public boolean isAnimating() {
+        return mValueAnimator != null;
+    }
+
+    /**
+     * Returns whether we are currently animating, and the animation's end value matches the given.
+     */
+    public boolean isAnimatingToValue(float endValue) {
+        return isAnimating() && mEndValue != null && mEndValue == endValue;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 923a959..1d4ed4c 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -30,6 +30,7 @@
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -40,6 +41,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 +50,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;
@@ -65,6 +67,7 @@
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
+import java.util.HashMap;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -138,6 +141,9 @@
         return null;
     }
 
+    @Nullable
+    public abstract TaskbarUIController getTaskbarController();
+
     public final boolean isResumed() {
         ACTIVITY_TYPE activity = getCreatedActivity();
         return activity != null && activity.hasBeenResumed();
@@ -188,7 +194,8 @@
 
     public void closeOverlay() { }
 
-    public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {
+    public void switchRunningTaskViewToScreenshot(HashMap<Integer, ThumbnailData> thumbnailDatas,
+            Runnable runnable) {
         ACTIVITY_TYPE activity = getCreatedActivity();
         if (activity == null) {
             return;
@@ -200,46 +207,31 @@
             }
             return;
         }
-        recentsView.switchToScreenshot(thumbnailData, runnable);
+        recentsView.switchToScreenshot(thumbnailDatas, runnable);
     }
 
     /**
      * 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;
+            scale = Math.min(scale, res.getFloat(R.dimen.overview_max_scale));
             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);
         }
@@ -280,19 +272,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);
+            }
         }
     }
 
@@ -300,14 +308,13 @@
      * Calculates the overview grid size for the provided device configuration.
      */
     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);
-        int sideMargin = res.getDimensionPixelSize(R.dimen.overview_grid_side_margin);
-
         Rect insets = dp.getInsets();
+        int topMargin = dp.overviewTaskThumbnailTopMarginPx;
+        int bottomMargin = getOverviewActionsHeight(context, dp);
+        int sideMargin = dp.overviewGridSideMargin;
+
         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));
     }
 
@@ -317,22 +324,21 @@
     public final void calculateGridTaskSize(Context context, DeviceProfile dp, Rect outRect,
             PagedOrientationHandler orientedState) {
         Resources res = context.getResources();
-        Rect gridRect = new Rect();
-        calculateGridSize(context, dp, gridRect);
+        Rect taskRect = new Rect();
+        calculateTaskSize(context, dp, taskRect);
 
-        int rowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
-        float rowHeight = (gridRect.height() - rowSpacing) / 2f;
+        float rowHeight =
+                (taskRect.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);
+        Gravity.apply(gravity, outWidth, outHeight, taskRect, outRect);
     }
 
     /**
@@ -360,7 +366,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) {
@@ -387,6 +394,20 @@
      */
     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) {
+        TaskbarUIController taskbarUIController = getTaskbarController();
+        if (taskbarUIController != null) {
+            taskbarUIController.setSystemGestureInProgress(false);
+            return taskbarUIController.getRootView();
+        }
+        return null;
+    }
+
     public interface AnimationFactory {
 
         void createActivityInterface(long transitionLength);
@@ -402,6 +423,10 @@
             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) { }
     }
@@ -413,6 +438,7 @@
         private final Consumer<AnimatorControllerWithResistance> mCallback;
 
         private boolean mIsAttachedToWindow;
+        private boolean mHasEverAttachedToWindow;
 
         DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) {
             mCallback = callback;
@@ -466,6 +492,9 @@
             }
             mIsAttachedToWindow = attached;
             RecentsView recentsView = mActivity.getOverviewPanel();
+            if (attached) {
+                mHasEverAttachedToWindow = true;
+            }
             Animator fadeAnim = mActivity.getStateManager()
                     .createStateElementAnimation(INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
 
@@ -496,6 +525,11 @@
         }
 
         @Override
+        public boolean hasRecentsEverAttachedToAppWindow() {
+            return mHasEverAttachedToWindow;
+        }
+
+        @Override
         public void setEndTarget(GestureState.GestureEndTarget endTarget) {
             mTargetState = stateFromGestureEndTarget(endTarget);
         }
@@ -506,11 +540,16 @@
             pa.addFloat(recentsView, RECENTS_SCALE_PROPERTY,
                     recentsView.getMaxScaleForFullScreen(), 1, LINEAR);
             pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
-        }
-    }
 
-    /** Called when OverviewService is bound to this process */
-    void onOverviewServiceBound() {
-        // Do nothing
+            pa.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    TaskbarUIController taskbarUIController = getTaskbarController();
+                    if (taskbarUIController != null) {
+                        taskbarUIController.setSystemGestureInProgress(true);
+                    }
+                }
+            });
+        }
     }
 }
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..a82137e 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;
@@ -49,7 +50,6 @@
 import android.view.SurfaceControl.Transaction;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
@@ -58,7 +58,6 @@
 import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsState;
-import com.android.quickstep.util.AppCloseConfig;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.TransformParams.BuilderProxy;
@@ -101,7 +100,9 @@
 
         mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask());
         if (mRunningOverHome) {
-            mTransformParams.setHomeBuilderProxy(this::updateHomeActivityTransformDuringSwipeUp);
+            runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTransformParams().setHomeBuilderProxy(
+                    FallbackSwipeHandler.this::updateHomeActivityTransformDuringSwipeUp));
         }
     }
 
@@ -109,7 +110,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();
         }
     }
 
@@ -144,7 +147,7 @@
     }
 
     @Override
-    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
         if (mActiveAnimationFactory != null
                 && mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) {
             mActiveAnimationFactory = null;
@@ -174,7 +177,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 +206,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
@@ -251,7 +260,8 @@
             }
         }
 
-        public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
+            RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
             if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
                 RemoteAnimationTargets targets = new RemoteAnimationTargets(
                         new RemoteAnimationTargetCompat[] {appearedTaskTarget},
@@ -306,8 +316,7 @@
         }
 
         @Override
-        public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
-                 float radius) {
+        public void update(RectF currentRect, float progress, float radius) {
             if (mSurfaceControl != null) {
                 currentRect.roundOut(mTempRect);
                 Transaction t = new Transaction();
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index aabba66..ed0623d 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 
+import android.annotation.Nullable;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.Intent;
@@ -35,6 +36,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -122,10 +124,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");
@@ -138,12 +136,13 @@
     private final int mGestureId;
 
     private ActivityManager.RunningTaskInfo mRunningTask;
+    private ActivityManager.RunningTaskInfo[] mRunningTasks;
     private GestureEndTarget mEndTarget;
     private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
     private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
     private int mLastStartedTaskId = -1;
     private RecentsAnimationController mRecentsAnimationController;
-    private ThumbnailData mRecentsAnimationCanceledSnapshot;
+    private HashMap<Integer, ThumbnailData> mRecentsAnimationCanceledSnapshots;
 
     /** The time when the swipe up gesture is triggered. */
     private long mSwipeUpStartTimeMs;
@@ -238,6 +237,14 @@
     }
 
     /**
+     * This will array will contain the task returned by {@link #getRunningTask()}
+     * @return the running tasks for this gesture.
+     */
+    public ActivityManager.RunningTaskInfo[] getRunningTasks() {
+        return mRunningTasks;
+    }
+
+    /**
      * @return the running task id for this gesture.
      */
     public int getRunningTaskId() {
@@ -252,6 +259,15 @@
     }
 
     /**
+     * TODO(b/210903248) refactor to consolidate w/ method above
+     * Updates the running task for the gesture to be the given {@param runningTask}.
+     */
+    public void updateRunningTasks(ActivityManager.RunningTaskInfo[] runningTasks) {
+        mRunningTasks = runningTasks;
+        updateRunningTask(runningTasks[0]);
+    }
+
+    /**
      * Updates the last task that appeared during this gesture.
      */
     public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
@@ -358,16 +374,16 @@
     }
 
     @Override
-    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        mRecentsAnimationCanceledSnapshot = thumbnailData;
+    public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
+        mRecentsAnimationCanceledSnapshots = thumbnailDatas;
         mStateCallback.setState(STATE_RECENTS_ANIMATION_CANCELED);
         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
-        if (mRecentsAnimationCanceledSnapshot != null) {
+        if (mRecentsAnimationCanceledSnapshots != null) {
             // Clean up the screenshot to finalize the recents animation cancel
             if (mRecentsAnimationController != null) {
                 mRecentsAnimationController.cleanupScreenshot();
             }
-            mRecentsAnimationCanceledSnapshot = null;
+            mRecentsAnimationCanceledSnapshots = null;
         }
     }
 
@@ -382,10 +398,15 @@
      * while STATE_RECENTS_ANIMATION_CANCELED state is being set, and the caller is responsible for
      * calling {@link RecentsAnimationController#cleanupScreenshot()}.
      */
-    ThumbnailData consumeRecentsAnimationCanceledSnapshot() {
-        ThumbnailData data = mRecentsAnimationCanceledSnapshot;
-        mRecentsAnimationCanceledSnapshot = null;
-        return data;
+    @Nullable
+    HashMap<Integer, ThumbnailData> consumeRecentsAnimationCanceledSnapshot() {
+        if (mRecentsAnimationCanceledSnapshots != null) {
+            HashMap<Integer, ThumbnailData> data =
+                    new HashMap<Integer, ThumbnailData>(mRecentsAnimationCanceledSnapshots);
+            mRecentsAnimationCanceledSnapshots = null;
+            return data;
+        }
+        return null;
     }
 
     void setSwipeUpStartTimeMs(long uptimeMs) {
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 0b2a057..c455dc7 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -36,9 +36,10 @@
     int TYPE_SCREEN_PINNED = 1 << 6;
     int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
     int TYPE_RESET_GESTURE = 1 << 8;
-    int TYPE_OVERSCROLL = 1 << 9;
+    int TYPE_PROGRESS_DELEGATE = 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
@@ -50,9 +51,10 @@
             "TYPE_SCREEN_PINNED",           // 6
             "TYPE_OVERVIEW_WITHOUT_FOCUS",  // 7
             "TYPE_RESET_GESTURE",           // 8
-            "TYPE_OVERSCROLL",              // 9
+            "TYPE_PROGRESS_DELEGATE",       // 9
             "TYPE_SYSUI_OVERLAY",           // 10
             "TYPE_ONE_HANDED",              // 11
+            "TYPE_TASKBAR_STASH",           // 12
     };
 
     InputConsumer NO_OP = () -> TYPE_NO_OP;
@@ -97,6 +99,8 @@
 
     default void onMotionEvent(MotionEvent ev) { }
 
+    default void onHoverEvent(MotionEvent ev) { }
+
     default void onKeyEvent(KeyEvent ev) { }
 
     default void onInputEvent(InputEvent ev) {
diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java
new file mode 100644
index 0000000..a768ef5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/KtR.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.android.launcher3.R;
+
+/**
+ * Bridge class to allow using resources in Kotlin.
+ * <br/>
+ * TODO(b/204069723) Can't use resources directly in Kotlin
+ */
+public class KtR {
+    public static final class id {
+        public static int menu_option_layout = R.id.menu_option_layout;
+    }
+
+    public static final class dimen {
+        public static int task_menu_spacing = R.dimen.task_menu_spacing;
+        public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding;
+    }
+
+    public static final class layout {
+        public static int task_menu_with_arrow = R.layout.task_menu_with_arrow;
+        public static int task_view_menu_option = R.layout.task_view_menu_option;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index fb1391a..719c2ae 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -71,7 +71,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 {
@@ -130,7 +130,6 @@
                 pa.addFloat(getDepthController(),
                         new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
                         fromDepthRatio, toDepthRatio, LINEAR);
-
             }
         };
 
@@ -173,7 +172,8 @@
     }
 
     @Nullable
-    private LauncherTaskbarUIController getTaskbarController() {
+    @Override
+    public LauncherTaskbarUIController getTaskbarController() {
         BaseQuickstepLauncher launcher = getCreatedActivity();
         if (launcher == null) {
             return null;
@@ -189,7 +189,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 +288,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 {
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 19cad53..a72935b 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -18,18 +18,14 @@
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.Utilities.boundToRange;
 import static com.android.launcher3.Utilities.dpToPx;
 import static com.android.launcher3.Utilities.mapBoundToRange;
 import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
 
-import static java.lang.Math.round;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -59,10 +55,8 @@
 import com.android.launcher3.views.FloatingIconView;
 import com.android.launcher3.views.FloatingView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
-import com.android.quickstep.util.AppCloseConfig;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
-import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.FloatingWidgetView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -106,9 +100,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) {
@@ -119,8 +115,6 @@
     }
 
     private HomeAnimationFactory createIconHomeAnimationFactory(View workspaceView) {
-        final ResourceProvider rp = DynamicResource.provider(mActivity);
-        final float transY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
         RectF iconLocation = new RectF();
         FloatingIconView floatingIconView = getFloatingIconView(mActivity, workspaceView,
                 true /* hideOriginal */, iconLocation, false /* isOpening */);
@@ -131,13 +125,15 @@
 
         return new FloatingViewHomeAnimationFactory(floatingIconView) {
 
-            // There is a delay in loading the icon, so we need to keep the window
-            // opaque until it is ready.
-            private boolean mIsFloatingIconReady = false;
+            @Nullable
+            @Override
+            protected View getViewIgnoredInWorkspaceRevealAnimation() {
+                return workspaceView;
+            }
 
+            @NonNull
             @Override
             public RectF getWindowTargetRect() {
-                super.getWindowTargetRect();
                 return iconLocation;
             }
 
@@ -150,24 +146,9 @@
             }
 
             @Override
-            public boolean keepWindowOpaque() {
-                if (mIsFloatingIconReady || floatingIconView.isVisibleToUser()) {
-                    mIsFloatingIconReady = true;
-                    return false;
-                }
-                return true;
-            }
-
-            @Override
-            public void update(@Nullable AppCloseConfig config, RectF currentRect,
-                    float progress, float radius) {
-                super.update(config, currentRect, progress, radius);
-                int fgAlpha = 255;
-                if (config != null && PROTOTYPE_APP_CLOSE.get()) {
-                    progress = config.getInterpolatedProgress();
-                    fgAlpha = config.getFgAlpha();
-                }
-                floatingIconView.update(1f, fgAlpha, currentRect, progress,
+            public void update(RectF currentRect, float progress, float radius) {
+                super.update(currentRect, progress, radius);
+                floatingIconView.update(1f /* alpha */, 255 /* fgAlpha */, currentRect, progress,
                         windowAlphaThreshold, radius, false);
             }
         };
@@ -179,14 +160,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) {
 
@@ -217,14 +200,8 @@
             }
 
             @Override
-            public boolean keepWindowOpaque() {
-                return false;
-            }
-
-            @Override
-            public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
-                    float radius) {
-                super.update(config, currentRect, progress, radius);
+            public void update(RectF currentRect, float progress, float radius) {
+                super.update(currentRect, progress, radius);
                 final float fallbackBackgroundAlpha =
                         1 - mapBoundToRange(progress, 0.8f, 1, 0, 1, EXAGGERATED_EASE);
                 final float foregroundAlpha =
@@ -267,7 +244,7 @@
             }
         }
 
-        return mActivity.getWorkspace().getFirstMatchForAppClose(launchCookieItemId,
+        return mActivity.getFirstMatchForAppClose(launchCookieItemId,
                 runningTaskView.getTask().key.getComponent().getPackageName(),
                 UserHandle.of(runningTaskView.getTask().key.userId));
     }
@@ -283,27 +260,12 @@
         private final float mTransY;
         private final FloatingView mFloatingView;
         private ValueAnimator mBounceBackAnimator;
-        private final AnimatorSet mWorkspaceReveal;
 
         FloatingViewHomeAnimationFactory(FloatingView floatingView) {
             mFloatingView = floatingView;
 
             ResourceProvider rp = DynamicResource.provider(mActivity);
             mTransY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
-
-            mWorkspaceReveal = PROTOTYPE_APP_CLOSE.get()
-                    ? new WorkspaceRevealAnim(mActivity, true /* animateScrim */).getAnimators()
-                    : null;
-        }
-
-        @Override
-        public @NonNull RectF getWindowTargetRect() {
-            if (PROTOTYPE_APP_CLOSE.get()) {
-                // We want the target rect to be at this offset position, so that all
-                // launcher content can spring back upwards.
-                mFloatingView.setPositionOffsetY(mTransY);
-            }
-            return super.getWindowTargetRect();
         }
 
         @Override
@@ -311,20 +273,6 @@
             return false;
         }
 
-        @Override
-        public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
-                float radius) {
-            if (config != null && PROTOTYPE_APP_CLOSE.get()) {
-                DragLayer dl = mActivity.getDragLayer();
-                float translationY = config.getWorkspaceTransY();
-                dl.setTranslationY(translationY);
-
-                long duration = mWorkspaceReveal.getDuration();
-                long playTime = boundToRange(round(duration * progress), 0, duration);
-                mWorkspaceReveal.setCurrentPlayTime(playTime);
-            }
-        }
-
         protected void bounceBackToRestingPosition() {
             final float startValue = mTransY;
             final float endValue = 0;
@@ -359,31 +307,6 @@
         }
 
         @Override
-        public void setAnimation(RectFSpringAnim anim) {
-            if (PROTOTYPE_APP_CLOSE.get()) {
-                // Use a spring to put drag layer translation back to 0.
-                anim.addAnimatorListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mFloatingView.setPositionOffsetY(0);
-                        bounceBackToRestingPosition();
-                    }
-                });
-
-                // Will be updated manually below so that the two animations are in sync.
-                mWorkspaceReveal.start();
-                mWorkspaceReveal.pause();
-
-                anim.addAnimatorListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mWorkspaceReveal.end();
-                    }
-                });
-            }
-        }
-
-        @Override
         public void onCancel() {
             mFloatingView.fastFinish();
             if (mBounceBackAnimator != null) {
@@ -415,13 +338,9 @@
 
         @Override
         public void playAtomicAnimation(float velocity) {
-            if (!PROTOTYPE_APP_CLOSE.get()) {
-                new StaggeredWorkspaceAnim(mActivity, velocity, true /* animateOverviewScrim */,
-                        getViewIgnoredInWorkspaceRevealAnimation())
-                        .start();
-            } else if (shouldPlayAtomicWorkspaceReveal()) {
-                new WorkspaceRevealAnim(mActivity, true).start();
-            }
+            new StaggeredWorkspaceAnim(mActivity, velocity, true /* animateOverviewScrim */,
+                    getViewIgnoredInWorkspaceRevealAnimation())
+                    .start();
         }
 
         @Override
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..17baa3a 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -32,12 +32,15 @@
 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;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 
 /**
  * Helper class to handle various atomic commands for switching between Overview.
@@ -49,6 +52,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";
 
@@ -114,11 +118,12 @@
         mPendingCommands.clear();
     }
 
+    @Nullable
     private TaskView getNextTask(RecentsView view) {
         final TaskView runningTaskView = view.getRunningTaskView();
 
         if (runningTaskView == null) {
-            return view.getTaskViewCount() > 0 ? view.getTaskViewAt(0) : null;
+            return view.getTaskViewAt(0);
         } else {
             final TaskView nextTask = view.getNextTaskView();
             return nextTask != null ? nextTask : runningTaskView;
@@ -154,6 +159,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 +178,10 @@
                 }
                 case TYPE_TOGGLE:
                     return launchTask(recents, getNextTask(recents), cmd);
+                case TYPE_HOME:
+                    recents.startHome();
+                    LauncherSplitScreenListener.INSTANCE.getNoCreate().notifySwipingToHome();
+                    return true;
             }
         }
 
@@ -200,7 +214,7 @@
             }
 
             @Override
-            public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+            public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
                 interactionHandler.onGestureCancelled();
                 cmd.removeListener(this);
 
@@ -244,8 +258,8 @@
                 // Ensure that recents view has focus so that it receives the followup key inputs
                 TaskView taskView = rv.getNextTaskView();
                 if (taskView == null) {
-                    if (rv.getTaskViewCount() > 0) {
-                        taskView = rv.getTaskViewAt(0);
+                    taskView = rv.getTaskViewAt(0);
+                    if (taskView != null) {
                         taskView.requestFocus();
                     } else {
                         rv.requestFocus();
@@ -258,6 +272,14 @@
         scheduleNextTask(cmd);
     }
 
+    public void dump(PrintWriter pw) {
+        pw.println("OverviewCommandHelper:");
+        pw.println("  mPendingCommands=" + mPendingCommands.size());
+        if (!mPendingCommands.isEmpty()) {
+            pw.println("    pendingCommandType=" + mPendingCommands.get(0).type);
+        }
+    }
+
     private static class CommandInfo {
         public final long createTime = SystemClock.elapsedRealtime();
         public final int type;
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 65847f1..a551f55 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -24,8 +24,6 @@
 
 import com.android.launcher3.BuildConfig;
 import com.android.launcher3.MainProcessInitializer;
-import com.android.launcher3.util.Executors;
-import com.android.quickstep.logging.SettingsChangeLogger;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.ThreadedRendererCompat;
 
@@ -62,9 +60,5 @@
         // Elevate GPU priority for Quickstep and Remote animations.
         ThreadedRendererCompat.setContextPriority(
                 ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
-
-        // Initialize settings logger after a default timeout
-        Executors.MAIN_EXECUTOR.getHandler()
-                .postDelayed(() -> new SettingsChangeLogger(context), SETUP_DELAY_MILLIS);
     }
 }
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/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 3080f04..097850f 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -22,35 +22,36 @@
 import android.app.ActivityManager;
 import android.os.Build;
 import android.os.Process;
-import android.util.Log;
+import android.os.RemoteException;
 import android.util.SparseBooleanArray;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.testing.TestProtocol;
+import com.android.quickstep.util.GroupTask;
 import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.KeyguardManagerCompat;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.recents.IRecentTasksListener;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.util.StagedSplitBounds;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 import java.util.function.Consumer;
 
 /**
  * Manages the recent task list from the system, caching it as necessary.
  */
 @TargetApi(Build.VERSION_CODES.R)
-public class RecentTasksList extends TaskStackChangeListener {
+public class RecentTasksList {
 
     private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
 
     private final KeyguardManagerCompat mKeyguardManager;
     private final LooperExecutor mMainThreadExecutor;
-    private final ActivityManagerWrapper mActivityManagerWrapper;
+    private final SystemUiProxy mSysUiProxy;
 
     // The list change id, increments as the task list changes in the system
     private int mChangeId;
@@ -62,12 +63,17 @@
     private TaskLoadResult mResultsUi = INVALID_RESULT;
 
     public RecentTasksList(LooperExecutor mainThreadExecutor,
-            KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) {
+            KeyguardManagerCompat keyguardManager, SystemUiProxy sysUiProxy) {
         mMainThreadExecutor = mainThreadExecutor;
         mKeyguardManager = keyguardManager;
         mChangeId = 1;
-        mActivityManagerWrapper = activityManagerWrapper;
-        TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
+        mSysUiProxy = sysUiProxy;
+        sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() {
+            @Override
+            public void onRecentTasksChanged() throws RemoteException {
+                mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
+            }
+        });
     }
 
     @VisibleForTesting
@@ -78,10 +84,11 @@
     /**
      * Fetches the task keys skipping any local cache.
      */
-    public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
+    public void getTaskKeys(int numTasks, Consumer<ArrayList<GroupTask>> callback) {
         // Kick off task loading in the background
         UI_HELPER_EXECUTOR.execute(() -> {
-            ArrayList<Task> tasks = loadTasksInBackground(numTasks, -1, true /* loadKeysOnly */);
+            ArrayList<GroupTask> tasks = loadTasksInBackground(numTasks, -1,
+                    true /* loadKeysOnly */);
             mMainThreadExecutor.execute(() -> callback.accept(tasks));
         });
     }
@@ -93,14 +100,15 @@
      * @param callback The callback to receive the list of recent tasks
      * @return The change id of the current task list
      */
-    public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
+    public synchronized int getTasks(boolean loadKeysOnly,
+            Consumer<ArrayList<GroupTask>> callback) {
         final int requestLoadId = mChangeId;
         if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
             // The list is up to date, send the callback on the next frame,
             // so that requestID can be returned first.
             if (callback != null) {
                 // Copy synchronously as the changeId might change by next frame
-                ArrayList<Task> result = copyOf(mResultsUi);
+                ArrayList<GroupTask> result = copyOf(mResultsUi);
                 mMainThreadExecutor.post(() -> {
                     callback.accept(result);
                 });
@@ -120,7 +128,7 @@
                 mLoadingTasksInBackground = false;
                 mResultsUi = loadResult;
                 if (callback != null) {
-                    ArrayList<Task> result = copyOf(mResultsUi);
+                    ArrayList<GroupTask> result = copyOf(mResultsUi);
                     callback.accept(result);
                 }
             });
@@ -136,35 +144,7 @@
         return mChangeId == changeId;
     }
 
-    @Override
-    public void onTaskStackChanged() {
-        invalidateLoadedTasks();
-    }
-
-    @Override
-    public void onRecentTaskListUpdated() {
-        // In some cases immediately after booting, the tasks in the system recent task list may be
-        // loaded, but not in the active task hierarchy in the system.  These tasks are displayed in 
-        // overview, but removing them don't result in a onTaskStackChanged() nor a onTaskRemoved()
-        // callback (those are for changes to the active tasks), but the task list is still updated,
-        // so we should also invalidate the change id to ensure we load a new list instead of 
-        // reusing a stale list.
-        invalidateLoadedTasks();
-    }
-
-    @Override
-    public void onTaskRemoved(int taskId) {
-        invalidateLoadedTasks();
-    }
-
-
-    @Override
-    public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
-        invalidateLoadedTasks();
-    }
-
-    @Override
-    public synchronized void onActivityUnpinned() {
+    public void onRecentTasksChanged() {
         invalidateLoadedTasks();
     }
 
@@ -180,8 +160,8 @@
     @VisibleForTesting
     TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) {
         int currentUserId = Process.myUserHandle().getIdentifier();
-        List<ActivityManager.RecentTaskInfo> rawTasks =
-                mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId);
+        ArrayList<GroupedRecentTaskInfo> rawTasks =
+                mSysUiProxy.getRecentTasks(numTasks, currentUserId);
         // The raw tasks are given in most-recent to least-recent order, we need to reverse it
         Collections.reverse(rawTasks);
 
@@ -197,45 +177,84 @@
         };
 
         TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
-        for (ActivityManager.RecentTaskInfo rawTask : rawTasks) {
-            Task.TaskKey taskKey = new Task.TaskKey(rawTask);
-            Task task;
-            if (!loadKeysOnly) {
-                boolean isLocked = tmpLockedUsers.get(taskKey.userId);
-                task = Task.from(taskKey, rawTask, isLocked);
-            } else {
-                task = new Task(taskKey);
+        for (GroupedRecentTaskInfo rawTask : rawTasks) {
+            ActivityManager.RecentTaskInfo taskInfo1 = rawTask.mTaskInfo1;
+            ActivityManager.RecentTaskInfo taskInfo2 = rawTask.mTaskInfo2;
+            Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
+            Task task1 = loadKeysOnly
+                    ? new Task(task1Key)
+                    : Task.from(task1Key, taskInfo1,
+                            tmpLockedUsers.get(task1Key.userId) /* isLocked */);
+            task1.setLastSnapshotData(taskInfo1);
+            Task task2 = null;
+            if (taskInfo2 != null) {
+                Task.TaskKey task2Key = new Task.TaskKey(taskInfo2);
+                task2 = loadKeysOnly
+                        ? new Task(task2Key)
+                        : Task.from(task2Key, taskInfo2,
+                                tmpLockedUsers.get(task2Key.userId) /* isLocked */);
+                task2.setLastSnapshotData(taskInfo2);
             }
-            task.setLastSnapshotData(rawTask);
-            allTasks.add(task);
+            final SplitConfigurationOptions.StagedSplitBounds launcherSplitBounds =
+                    convertSplitBounds(rawTask.mStagedSplitBounds);
+            allTasks.add(new GroupTask(task1, task2, launcherSplitBounds));
         }
 
         return allTasks;
     }
 
-    private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
-        ArrayList<Task> newTasks = new ArrayList<>();
+    private SplitConfigurationOptions.StagedSplitBounds convertSplitBounds(
+            StagedSplitBounds shellSplitBounds) {
+        return shellSplitBounds == null ?
+                null :
+                new SplitConfigurationOptions.StagedSplitBounds(
+                        shellSplitBounds.leftTopBounds, shellSplitBounds.rightBottomBounds,
+                        shellSplitBounds.leftTopTaskId, shellSplitBounds.rightBottomTaskId);
+    }
+
+    private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
+        ArrayList<GroupTask> newTasks = new ArrayList<>();
         for (int i = 0; i < tasks.size(); i++) {
-            newTasks.add(new Task(tasks.get(i)));
+            newTasks.add(new GroupTask(tasks.get(i)));
         }
         return newTasks;
     }
 
-    private static class TaskLoadResult extends ArrayList<Task> {
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "RecentTasksList:");
+        writer.println(prefix + "  mChangeId=" + mChangeId);
+        writer.println(prefix + "  mResultsUi=[id=" + mResultsUi.mRequestId + ", tasks=");
+        for (GroupTask task : mResultsUi) {
+            writer.println(prefix + "    t1=" + task.task1.key.id
+                    + " t2=" + (task.hasMultipleTasks() ? task.task2.key.id : "-1"));
+        }
+        writer.println(prefix + "  ]");
+        int currentUserId = Process.myUserHandle().getIdentifier();
+        ArrayList<GroupedRecentTaskInfo> rawTasks =
+                mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId);
+        writer.println(prefix + "  rawTasks=[");
+        for (GroupedRecentTaskInfo task : rawTasks) {
+            writer.println(prefix + "    t1=" + task.mTaskInfo1.taskId
+                    + " t2=" + (task.mTaskInfo2 != null ? task.mTaskInfo2.taskId : "-1"));
+        }
+        writer.println(prefix + "  ]");
+    }
 
-        final int mId;
+    private static class TaskLoadResult extends ArrayList<GroupTask> {
+
+        final int mRequestId;
 
         // If the result was loaded with keysOnly  = true
         final boolean mKeysOnly;
 
-        TaskLoadResult(int id, boolean keysOnly, int size) {
+        TaskLoadResult(int requestId, boolean keysOnly, int size) {
             super(size);
-            mId = id;
+            mRequestId = requestId;
             mKeysOnly = keysOnly;
         }
 
         boolean isValidForRequest(int requestId, boolean loadKeysOnly) {
-            return mId == requestId && (!mKeysOnly || loadKeysOnly);
+            return mRequestId == requestId && (!mKeysOnly || loadKeysOnly);
         }
     }
 }
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 9dfcd12..1dc4933 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;
@@ -97,11 +99,15 @@
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
 
     private static final long HOME_APPEAR_DURATION = 250;
+    private static final long RECENTS_ANIMATION_TIMEOUT = 1000;
 
     private RecentsDragLayer mDragLayer;
     private ScrimView mScrimView;
     private FallbackRecentsView mFallbackRecentsView;
     private OverviewActionsView mActionsView;
+    private TISBindHelper mTISBindHelper;
+    private @Nullable TaskbarManager mTaskbarManager;
+    private @Nullable FallbackTaskbarUIController mTaskbarUIController;
 
     private Configuration mOldConfig;
 
@@ -110,6 +116,11 @@
     // Strong refs to runners which are cleared when the activity is destroyed
     private RemoteAnimationFactory mActivityLaunchAnimationRunner;
 
+    // For handling degenerate cases where starting an activity doesn't actually trigger the remote
+    // animation callback
+    private final Handler mHandler = new Handler();
+    private final Runnable mAnimationStartTimeoutRunnable = this::onAnimationStartTimeout;
+
     /**
      * Init drag layer and overview panel views.
      */
@@ -122,13 +133,31 @@
         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),
+                        getStateManager(), null /*depthController*/);
         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);
+    }
+
+    @Override
+    public void runOnBindToTouchInteractionService(Runnable r) {
+        mTISBindHelper.runOnBindToTouchInteractionService(r);
+    }
+
+    public void setTaskbarUIController(FallbackTaskbarUIController taskbarUIController) {
+        mTaskbarUIController = taskbarUIController;
+    }
+
+    public FallbackTaskbarUIController getTaskbarUIController() {
+        return mTaskbarUIController;
     }
 
     @Override
@@ -196,6 +225,16 @@
         // TODO(b/137318995) This should go home, but doing so removes freeform windows
     }
 
+    /**
+     * Called if the remote animation callback from #getActivityLaunchOptions() hasn't called back
+     * in a reasonable time due to a conflict with the recents animation.
+     */
+    private void onAnimationStartTimeout() {
+        if (mActivityLaunchAnimationRunner != null) {
+            mActivityLaunchAnimationRunner.onAnimationCancelled();
+        }
+    }
+
     @Override
     public ActivityOptionsWrapper getActivityLaunchOptions(final View v, @Nullable ItemInfo item) {
         if (!(v instanceof TaskView)) {
@@ -210,6 +249,7 @@
             public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
                     RemoteAnimationTargetCompat[] wallpaperTargets,
                     RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+                mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
                 AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
                         wallpaperTargets, nonAppTargets);
                 anim.addListener(resetStateListener());
@@ -219,6 +259,7 @@
 
             @Override
             public void onAnimationCancelled() {
+                mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
                 onEndCallback.executeAllAndDestroy();
             }
         };
@@ -228,11 +269,12 @@
         RemoteAnimationAdapterCompat adapterCompat = new RemoteAnimationAdapterCompat(
                 wrapper, RECENTS_LAUNCH_DURATION,
                 RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
-                        - STATUS_BAR_TRANSITION_PRE_DELAY);
+                        - STATUS_BAR_TRANSITION_PRE_DELAY, getIApplicationThread());
         final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(
                 ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
                 onEndCallback);
         activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        mHandler.postDelayed(mAnimationStartTimeoutRunnable, RECENTS_ANIMATION_TIMEOUT);
         return activityOptions;
     }
 
@@ -290,7 +332,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 +392,11 @@
         super.onDestroy();
         ACTIVITY_TRACKER.onActivityDestroyed(this);
         mActivityLaunchAnimationRunner = null;
+
+        mTISBindHelper.onDestroy();
+        if (mTaskbarManager != null) {
+            mTaskbarManager.clearActivity(this);
+        }
     }
 
     @Override
@@ -372,7 +419,8 @@
         LauncherAnimationRunner runner = new LauncherAnimationRunner(
                 getMainThreadHandler(), mAnimationToHomeFactory, true);
         RemoteAnimationAdapterCompat adapterCompat =
-                new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0);
+                new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
+                        getIApplicationThread());
         startActivity(createHomeIntent(),
                 ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index a21c714..b502676 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -16,11 +16,14 @@
 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.NonNull;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.Utilities;
@@ -29,6 +32,8 @@
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Set;
 
 /**
@@ -39,6 +44,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 +52,9 @@
 
     private boolean mCancelled;
 
-    public RecentsAnimationCallbacks(boolean allowMinimizeSplitScreen) {
+    public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy,
+            boolean allowMinimizeSplitScreen) {
+        mSystemUiProxy = systemUiProxy;
         mAllowMinimizeSplitScreen = allowMinimizeSplitScreen;
     }
 
@@ -70,7 +78,7 @@
 
     public void notifyAnimationCanceled() {
         mCancelled = true;
-        onAnimationCanceled(null);
+        onAnimationCanceled(new HashMap<>());
     }
 
     // Called only in Q platform
@@ -89,8 +97,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);
 
@@ -108,20 +127,20 @@
 
     @BinderThread
     @Override
-    public final void onAnimationCanceled(ThumbnailData thumbnailData) {
+    public final void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
         Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
             for (RecentsAnimationListener listener : getListeners()) {
-                listener.onRecentsAnimationCanceled(thumbnailData);
+                listener.onRecentsAnimationCanceled(thumbnailDatas);
             }
         });
     }
 
     @BinderThread
     @Override
-    public void onTaskAppeared(RemoteAnimationTargetCompat app) {
+    public void onTasksAppeared(RemoteAnimationTargetCompat[] apps) {
         Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
             for (RecentsAnimationListener listener : getListeners()) {
-                listener.onTaskAppeared(app);
+                listener.onTasksAppeared(apps);
             }
         });
     }
@@ -149,16 +168,17 @@
          * Callback from the system when the recents animation is canceled. {@param thumbnailData}
          * is passed back for rendering screenshot to replace live tile.
          */
-        default void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {}
+        default void onRecentsAnimationCanceled(
+                @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {}
 
         /**
          * Callback made whenever the recents animation is finished.
          */
-        default void onRecentsAnimationFinished(RecentsAnimationController controller) {}
+        default void onRecentsAnimationFinished(@NonNull RecentsAnimationController controller) {}
 
         /**
          * Callback made when a task started from the recents is ready for an app transition.
          */
-        default void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {}
+        default void onTasksAppeared(@NonNull RemoteAnimationTargetCompat[] appearedTaskTarget) {}
     }
 }
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 fde6902..73d1424 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;
@@ -82,7 +83,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.
@@ -144,7 +144,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);
@@ -396,14 +396,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/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 1e82c8c..5d77a6e 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -24,6 +24,7 @@
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
@@ -34,6 +35,7 @@
 import com.android.launcher3.icons.IconProvider.IconChangeListener;
 import com.android.launcher3.util.Executors.SimpleThreadFactory;
 import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.quickstep.util.GroupTask;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -41,6 +43,7 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -70,7 +73,7 @@
     private RecentsModel(Context context) {
         mContext = context;
         mTaskList = new RecentTasksList(MAIN_EXECUTOR,
-                new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
+                new KeyguardManagerCompat(context), SystemUiProxy.INSTANCE.get(context));
 
         IconProvider iconProvider = new IconProvider(context);
         mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
@@ -95,7 +98,7 @@
      *                always called on the UI thread.
      * @return the request id associated with this call.
      */
-    public int getTasks(Consumer<ArrayList<Task>> callback) {
+    public int getTasks(Consumer<ArrayList<GroupTask>> callback) {
         return mTaskList.getTasks(false /* loadKeysOnly */, callback);
     }
 
@@ -120,9 +123,9 @@
      * @param callback Receives true if task is removed, false otherwise
      */
     public void isTaskRemoved(int taskId, Consumer<Boolean> callback) {
-        mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
-            for (Task task : tasks) {
-                if (task.key.id == taskId) {
+        mTaskList.getTasks(true /* loadKeysOnly */, (taskGroups) -> {
+            for (GroupTask group : taskGroups) {
+                if (group.containsTask(taskId)) {
                     callback.accept(false);
                     return;
                 }
@@ -148,14 +151,15 @@
         ActivityManager.RunningTaskInfo runningTask =
                 ActivityManagerWrapper.getInstance().getRunningTask();
         int runningTaskId = runningTask != null ? runningTask.id : -1;
-        mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
-            for (Task task : tasks) {
-                if (task.key.id == runningTaskId) {
+        mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), taskGroups -> {
+            for (GroupTask group : taskGroups) {
+                if (group.containsTask(runningTaskId)) {
                     // Skip the running task, it's not going to have an up-to-date snapshot by the
                     // time the user next enters overview
                     continue;
                 }
-                mThumbnailCache.updateThumbnailInCache(task);
+                mThumbnailCache.updateThumbnailInCache(group.task1);
+                mThumbnailCache.updateThumbnailInCache(group.task2);
             }
         });
     }
@@ -174,7 +178,7 @@
 
     @Override
     public void onTaskRemoved(int taskId) {
-        Task.TaskKey stubKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
+        Task.TaskKey stubKey = new Task.TaskKey(taskId, 0, new Intent(), null, 0, 0);
         mThumbnailCache.remove(stubKey);
         mIconCache.onTaskRemoved(stubKey);
     }
@@ -217,6 +221,11 @@
         mThumbnailChangeListeners.remove(listener);
     }
 
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "RecentsModel:");
+        mTaskList.dump("  ", writer);
+    }
+
     /**
      * Listener for receiving various task properties changes
      */
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..ed1a06d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -0,0 +1,205 @@
+/*
+ * 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 androidx.annotation.Nullable;
+
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+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 RemoteTargetHandle[] mRemoteTargetHandles;
+    private 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(targets, null));
+            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 _active_ split screened tasks.
+     * See {@link #assignTargetsForSplitScreen(RemoteAnimationTargets, int[])}
+     */
+    public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
+        int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
+                .getRunningSplitTaskIds();
+        return assignTargetsForSplitScreen(targets, splitIds);
+    }
+
+    /**
+     * Assigns the provided splitIDs to the {@link #mRemoteTargetHandles}, with index 0 will being
+     * the left/top task, index 1 right/bottom
+     */
+    public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
+            int[] splitIds) {
+        RemoteAnimationTargetCompat topLeftTarget; // only one set if single/fullscreen task
+        RemoteAnimationTargetCompat bottomRightTarget;
+        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.
+            mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
+            if (targets.apps.length > 0) {
+                // Unclear why/when target.apps length == 0, but it sure does happen :(
+                topLeftTarget = targets.apps[0];
+                mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, null);
+            }
+        } else {
+            // split screen
+            topLeftTarget = targets.findTask(splitIds[0]);
+            bottomRightTarget = targets.findTask(splitIds[1]);
+
+            // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
+            // vice versa
+            mStagedSplitBounds = new StagedSplitBounds(
+                    topLeftTarget.screenSpaceBounds,
+                    bottomRightTarget.screenSpaceBounds, splitIds[0], splitIds[1]);
+            mRemoteTargetHandles[0].mTransformParams.setTargetSet(
+                    createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
+            mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
+                    mStagedSplitBounds);
+
+            mRemoteTargetHandles[1].mTransformParams.setTargetSet(
+                    createRemoteAnimationTargetsForTarget(targets, topLeftTarget));
+            mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget,
+                    mStagedSplitBounds);
+        }
+        return mRemoteTargetHandles;
+    }
+
+    /**
+     * Ensures that we aren't excluding ancillary targets such as home/recents
+     *
+     * @param targetToExclude Will be excluded from the resulting return value.
+     *                        Pass in {@code null} to not exclude anything
+     * @return RemoteAnimationTargets where all the app targets from the passed in
+     *         {@param targets} are included except {@param targetToExclude}
+     */
+    private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
+            RemoteAnimationTargets targets,
+            @Nullable RemoteAnimationTargetCompat targetToExclude) {
+        int finalLength = targets.unfilteredApps.length - (targetToExclude == null ? 0 : 1);
+        RemoteAnimationTargetCompat[] targetsWithoutExcluded =
+                new RemoteAnimationTargetCompat[finalLength];
+        int i = 0;
+        for (RemoteAnimationTargetCompat targetCompat : targets.unfilteredApps) {
+            if (targetCompat == targetToExclude) {
+                continue;
+            }
+            targetsWithoutExcluded[i] = targetCompat;
+            i++;
+        }
+        return new RemoteAnimationTargets(targetsWithoutExcluded,
+                targets.wallpapers, targets.nonApps, targets.targetMode);
+    }
+
+    public RemoteTargetHandle[] getRemoteTargetHandles() {
+        return mRemoteTargetHandles;
+    }
+
+    public 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..8e9b668 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -17,7 +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.PROTOTYPE_APP_CLOSE;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_SELECT;
 
 import android.animation.Animator;
 import android.content.Context;
@@ -27,7 +27,6 @@
 import android.graphics.RectF;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.DeviceProfile;
@@ -36,28 +35,32 @@
 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;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.TransformParams.BuilderProxy;
 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 +73,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 +139,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 {
@@ -167,10 +183,7 @@
 
         public void setAnimation(RectFSpringAnim anim) { }
 
-        public boolean keepWindowOpaque() { return false; }
-
-        public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
-                float radius) { }
+        public void update(RectF currentRect, float progress, float radius) { }
 
         public void onCancel() { }
 
@@ -207,16 +220,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,33 +256,41 @@
      * @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);
         windowToHomePositionMap.mapRect(startRect);
 
-        RectFSpringAnim anim;
-        if (PROTOTYPE_APP_CLOSE.get()) {
-            anim = new RectFSpringAnim2(startRect, targetRect, mContext,
-                    mTaskViewSimulator.getCurrentCornerRadius(),
-                    homeAnimationFactory.getEndRadius(cropRectF));
-        } else {
-            anim = new RectFSpringAnim(startRect, targetRect, mContext);
-        }
+        RectFSpringAnim 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 +302,7 @@
 
         final RectF mWindowCurrentRect = new RectF();
         final Matrix mHomeToWindowPositionMap;
+        private final TransformParams mLocalTransformParams;
         final HomeAnimationFactory mAnimationFactory;
 
         final AnimatorPlaybackController mHomeAnim;
@@ -271,41 +312,36 @@
         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);
         }
 
         @Override
-        public void onUpdate(@Nullable AppCloseConfig config, RectF currentRect, float progress) {
+        public void onUpdate(RectF currentRect, float progress) {
             mHomeAnim.setPlayFraction(progress);
             mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect);
 
             mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
             float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
             float alpha = mAnimationFactory.getWindowAlpha(progress);
-            if (config != null && PROTOTYPE_APP_CLOSE.get()) {
-                alpha = config.getWindowAlpha();
-                cornerRadius = config.getCornerRadius();
-            }
-            if (mAnimationFactory.keepWindowOpaque()) {
-                alpha = 1f;
-            }
-            mTransformParams
+            mLocalTransformParams
                     .setTargetAlpha(alpha)
                     .setCornerRadius(cornerRadius);
-            mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
-            mAnimationFactory.update(config, currentRect, progress,
-                    mMatrix.mapRadius(cornerRadius));
+            mLocalTransformParams.applySurfaceParams(mLocalTransformParams
+                    .createSurfaceParams(this));
+            mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index d040904..67e7f88 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
+
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.app.PendingIntent;
@@ -33,6 +35,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;
@@ -45,11 +49,17 @@
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
 import com.android.wm.shell.pip.IPipAnimationListener;
+import com.android.wm.shell.recents.IRecentTasks;
+import com.android.wm.shell.recents.IRecentTasksListener;
 import com.android.wm.shell.splitscreen.ISplitScreen;
 import com.android.wm.shell.splitscreen.ISplitScreenListener;
 import com.android.wm.shell.startingsurface.IStartingWindow;
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
 import com.android.wm.shell.transition.IShellTransitions;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Holds the reference to SystemUI.
@@ -68,16 +78,21 @@
     private IOneHanded mOneHanded;
     private IShellTransitions mShellTransitions;
     private IStartingWindow mStartingWindow;
+    private IRecentTasks mRecentTasks;
     private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
         MAIN_EXECUTOR.execute(() -> clearProxy());
     };
 
-    // Save the listeners passed into the proxy since when set/register these listeners,
-    // setProxy may not have been called, eg. OverviewProxyService is not connected yet.
-    private IPipAnimationListener mPendingPipAnimationListener;
-    private ISplitScreenListener mPendingSplitScreenListener;
-    private IStartingWindowListener mPendingStartingWindowListener;
-    private ISmartspaceCallback mPendingSmartspaceCallback;
+    // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
+    // yet, and we'll need to set/register these listeners with SysUI when they do.  Note that it is
+    // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
+    // in case SysUI needs to rebind.
+    private IPipAnimationListener mPipAnimationListener;
+    private ISplitScreenListener mSplitScreenListener;
+    private IStartingWindowListener mStartingWindowListener;
+    private ISmartspaceCallback mSmartspaceCallback;
+    private IRecentTasksListener mRecentTasksListener;
+    private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
 
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
@@ -85,6 +100,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;
@@ -111,6 +127,17 @@
     }
 
     @Override
+    public void onImeSwitcherPressed() {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.onImeSwitcherPressed();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call onImeSwitcherPressed", e);
+            }
+        }
+    }
+
+    @Override
     public void setHomeRotationEnabled(boolean enabled) {
         if (mSystemUiProxy != null) {
             try {
@@ -129,7 +156,7 @@
 
     public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
             IOneHanded oneHanded, IShellTransitions shellTransitions,
-            IStartingWindow startingWindow,
+            IStartingWindow startingWindow, IRecentTasks recentTasks,
             ISmartspaceTransitionController smartSpaceTransitionController) {
         unlinkToDeath();
         mSystemUiProxy = proxy;
@@ -139,28 +166,36 @@
         mShellTransitions = shellTransitions;
         mStartingWindow = startingWindow;
         mSmartspaceTransitionController = smartSpaceTransitionController;
+        mRecentTasks = recentTasks;
         linkToDeath();
         // re-attach the listeners once missing due to setProxy has not been initialized yet.
-        if (mPendingPipAnimationListener != null && mPip != null) {
-            setPinnedStackAnimationListener(mPendingPipAnimationListener);
-            mPendingPipAnimationListener = null;
+        if (mPipAnimationListener != null && mPip != null) {
+            setPinnedStackAnimationListener(mPipAnimationListener);
         }
-        if (mPendingSplitScreenListener != null && mSplitScreen != null) {
-            registerSplitScreenListener(mPendingSplitScreenListener);
-            mPendingSplitScreenListener = null;
+        if (mSplitScreenListener != null && mSplitScreen != null) {
+            registerSplitScreenListener(mSplitScreenListener);
         }
-        if (mPendingStartingWindowListener != null && mStartingWindow != null) {
-            setStartingWindowListener(mPendingStartingWindowListener);
-            mPendingStartingWindowListener = null;
+        if (mStartingWindowListener != null && mStartingWindow != null) {
+            setStartingWindowListener(mStartingWindowListener);
         }
-        if (mPendingSmartspaceCallback != null && mSmartspaceTransitionController != null) {
-            setSmartspaceCallback(mPendingSmartspaceCallback);
-            mPendingSmartspaceCallback = null;
+        if (mSmartspaceCallback != null && mSmartspaceTransitionController != null) {
+            setSmartspaceCallback(mSmartspaceCallback);
+        }
+        for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
+            registerRemoteTransition(mRemoteTransitions.get(i));
+        }
+        if (mRecentTasksListener != null && mRecentTasks != null) {
+            registerRecentTasksListener(mRecentTasksListener);
+        }
+
+        if (mPendingSetNavButtonAlpha != null) {
+            mPendingSetNavButtonAlpha.run();
+            mPendingSetNavButtonAlpha = null;
         }
     }
 
     public void clearProxy() {
-        setProxy(null, null, null, null, null, null, null);
+        setProxy(null, null, null, null, null, null, null, null);
     }
 
     // TODO(141886704): Find a way to remove this
@@ -240,14 +275,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 +385,7 @@
             try {
                 mSystemUiProxy.setSplitScreenMinimized(minimized);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stopScreenPinning", e);
+                Log.w(TAG, "Failed call setSplitScreenMinimized", e);
             }
         }
     }
@@ -388,6 +427,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) {
@@ -442,9 +509,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
             }
-        } else {
-            mPendingPipAnimationListener = listener;
         }
+        mPipAnimationListener = listener;
     }
 
     public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
@@ -482,9 +548,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call registerSplitScreenListener");
             }
-        } else {
-            mPendingSplitScreenListener = listener;
         }
+        mSplitScreenListener = listener;
     }
 
     public void unregisterSplitScreenListener(ISplitScreenListener listener) {
@@ -495,68 +560,44 @@
                 Log.w(TAG, "Failed call unregisterSplitScreenListener");
             }
         }
-        mPendingSplitScreenListener = null;
-    }
-
-    public void setSideStageVisibility(boolean visible) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.setSideStageVisibility(visible);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setSideStageVisibility");
-            }
-        }
-    }
-
-    public void exitSplitScreen() {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.exitSplitScreen();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call exitSplitScreen");
-            }
-        }
-    }
-
-    public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.exitSplitScreenOnHide(exitSplitScreenOnHide);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call exitSplitScreen");
-            }
-        }
-    }
-
-    public void startTask(int taskId, int stage, int position, Bundle options) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.startTask(taskId, stage, position, options);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startTask");
-            }
-        }
+        mSplitScreenListener = null;
     }
 
     /** Start multiple tasks in split-screen simultaneously. */
     public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
-            @SplitConfigurationOptions.StagePosition int sidePosition,
+            @SplitConfigurationOptions.StagePosition int sidePosition, float splitRatio,
             RemoteTransitionCompat remoteTransition) {
         if (mSystemUiProxy != null) {
             try {
                 mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
-                        sidePosition, remoteTransition.getTransition());
+                        sidePosition, splitRatio, remoteTransition.getTransition());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startTask");
             }
         }
     }
 
-    public void startShortcut(String packageName, String shortcutId, int stage, int position,
+    /**
+     * Start multiple tasks in split-screen simultaneously.
+     */
+    public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
+            Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
+            float splitRatio, RemoteAnimationAdapter adapter) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
+                        sideOptions, sidePosition, splitRatio, adapter);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startTasksWithLegacyTransition");
+            }
+        }
+    }
+
+    public void startShortcut(String packageName, String shortcutId, int position,
             Bundle options, UserHandle user) {
         if (mSplitScreen != null) {
             try {
-                mSplitScreen.startShortcut(packageName, shortcutId, stage, position, options,
+                mSplitScreen.startShortcut(packageName, shortcutId, position, options,
                         user);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startShortcut");
@@ -564,11 +605,11 @@
         }
     }
 
-    public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
+    public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
             Bundle options) {
         if (mSplitScreen != null) {
             try {
-                mSplitScreen.startIntent(intent, fillInIntent, stage, position, options);
+                mSplitScreen.startIntent(intent, fillInIntent, position, options);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call startIntent");
             }
@@ -585,6 +626,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
     //
@@ -622,6 +682,9 @@
                 Log.w(TAG, "Failed call registerRemoteTransition");
             }
         }
+        if (!mRemoteTransitions.contains(remoteTransition)) {
+            mRemoteTransitions.add(remoteTransition);
+        }
     }
 
     public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
@@ -632,6 +695,7 @@
                 Log.w(TAG, "Failed call registerRemoteTransition");
             }
         }
+        mRemoteTransitions.remove(remoteTransition);
     }
 
     //
@@ -648,12 +712,10 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setStartingWindowListener", e);
             }
-        } else {
-            mPendingStartingWindowListener = listener;
         }
+        mStartingWindowListener = listener;
     }
 
-
     //
     // SmartSpace transitions
     //
@@ -665,8 +727,45 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setStartingWindowListener", e);
             }
-        } else {
-            mPendingSmartspaceCallback = callback;
         }
+        mSmartspaceCallback = callback;
+    }
+
+    //
+    // Recents
+    //
+
+    public void registerRecentTasksListener(IRecentTasksListener listener) {
+        if (mRecentTasks != null) {
+            try {
+                mRecentTasks.registerRecentTasksListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call registerRecentTasksListener", e);
+            }
+        }
+        mRecentTasksListener = listener;
+    }
+
+    public void unregisterRecentTasksListener(IRecentTasksListener listener) {
+        if (mRecentTasks != null) {
+            try {
+                mRecentTasks.unregisterRecentTasksListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call unregisterRecentTasksListener");
+            }
+        }
+        mRecentTasksListener = null;
+    }
+
+    public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
+        if (mRecentTasks != null) {
+            try {
+                return new ArrayList<>(Arrays.asList(mRecentTasks.getRecentTasks(numTasks,
+                        RECENT_IGNORE_UNAVAILABLE, userId)));
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call getRecentTasks", e);
+            }
+        }
+        return new ArrayList<>();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index d7af074..82c7c08 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -27,7 +28,9 @@
 import android.os.Bundle;
 import android.os.SystemProperties;
 import android.util.Log;
+import android.view.RemoteAnimationTarget;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.Utilities;
@@ -41,6 +44,9 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
+import java.util.Arrays;
+import java.util.HashMap;
+
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
     public static final boolean ENABLE_SHELL_TRANSITIONS =
             SystemProperties.getBoolean("persist.debug.shell_transit", false);
@@ -115,7 +121,8 @@
 
         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,
@@ -133,7 +140,7 @@
             }
 
             @Override
-            public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+            public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
                 cleanUpRecentsAnimation();
             }
 
@@ -143,18 +150,29 @@
             }
 
             @Override
-            public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+            public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
+                RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
                 BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
+                // Convert appTargets to type RemoteAnimationTarget for all apps except Home app
+                RemoteAnimationTarget[] nonHomeApps = Arrays.stream(appearedTaskTargets)
+                        .filter(remoteAnimationTarget ->
+                                remoteAnimationTarget.activityType != ACTIVITY_TYPE_HOME)
+                        .map(RemoteAnimationTargetCompat::unwrap)
+                        .toArray(RemoteAnimationTarget[]::new);
+
+                RemoteAnimationTarget[] nonAppTargets =
+                        SystemUiProxy.INSTANCE.getNoCreate()
+                                .onGoingToRecentsLegacy(false, nonHomeApps);
+
                 if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
                         && activityInterface.getCreatedActivity() != null) {
                     RecentsView recentsView =
                             activityInterface.getCreatedActivity().getOverviewPanel();
                     if (recentsView != null) {
-                        RemoteAnimationTargetCompat[] apps = new RemoteAnimationTargetCompat[1];
-                        apps[0] = appearedTaskTarget;
-                        recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId, apps,
+                        recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
+                                appearedTaskTargets,
                                 new RemoteAnimationTargetCompat[0] /* wallpaper */,
-                                new RemoteAnimationTargetCompat[0] /* nonApps */);
+                                RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */);
                         return;
                     }
                 }
@@ -176,7 +194,8 @@
 
         if (ENABLE_SHELL_TRANSITIONS) {
             RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
-                    mController != null ? mController.getController() : null);
+                    mController != null ? mController.getController() : null,
+                    mCtx.getIApplicationThread());
             Bundle options = ActivityOptionsCompat.makeRemoteTransition(transition)
                     .setTransientLaunch().toBundle();
             mCtx.startActivity(intent, options);
@@ -201,6 +220,22 @@
         return mCallbacks;
     }
 
+    public void endLiveTile() {
+        if (mLastGestureState == null) {
+            return;
+        }
+        BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
+                && activityInterface.getCreatedActivity() != null) {
+            RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
+            if (recentsView != null) {
+                recentsView.switchToScreenshot(null,
+                        () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+                                false /* shouldPip */, null));
+            }
+        }
+    }
+
     public void setLiveTileCleanUpHandler(Runnable cleanUpHandler) {
         mLiveTileCleanUpHandler = cleanUpHandler;
     }
@@ -269,6 +304,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..276e1c2 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -23,6 +23,7 @@
 import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
 
 import android.annotation.SuppressLint;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Matrix;
@@ -51,8 +52,10 @@
 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;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -63,41 +66,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 +113,35 @@
     }
 
 
-    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
+     * * Device is in "Lock task mode"
+     * * 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 button 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));
+        ActivityManager activityManager =
+                (ActivityManager) taskView.getContext().getSystemService(Context.ACTIVITY_SERVICE);
+        boolean isLockTaskMode = activityManager.isInLockTaskMode();
+
+        if (taskViewHasMultipleTasks || notEnoughTasksToSplit || isLockTaskMode ||
+                (isFocusedTask && isTaskInExpectedScrollPosition)) {
+            return;
+        }
+
         List<SplitPositionOption> positions =
                 orientationHandler.getSplitPositionOptions(deviceProfile);
         for (SplitPositionOption option : positions) {
@@ -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..f507ea3 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,31 @@
  * 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) {
+            TaskView taskView = taskContainer.getTaskView();
+            AppInfo.SplitAccessibilityInfo accessibilityInfo =
+                    new AppInfo.SplitAccessibilityInfo(taskView.containsMultipleTasks(),
+                            TaskUtils.getTitle(taskView.getContext(), taskContainer.getTask()),
+                            taskContainer.getA11yNodeId()
+                    );
+            return new AppInfo(activity, taskContainer.getItemInfo(), accessibilityInfo);
+        }
+
+        @Override
+        public boolean showForSplitscreen() {
+            return true;
+        }
+    };
 
     abstract class MultiWindowFactory implements TaskShortcutFactory {
 
@@ -82,28 +104,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 +134,7 @@
         }
     }
 
-    class MultiWindowSystemShortcut extends SystemShortcut {
+    class MultiWindowSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
 
         private Handler mHandler;
 
@@ -123,13 +145,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;
         }
 
@@ -219,6 +242,7 @@
         }
     }
 
+    /** @Deprecated */
     TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(R.drawable.ic_split_screen,
             R.string.recent_task_option_split_screen, LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP) {
 
@@ -233,16 +257,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 +298,7 @@
         }
     };
 
-    TaskShortcutFactory PIN = (activity, tv) -> {
+    TaskShortcutFactory PIN = (activity, taskContainer) -> {
         if (!SystemUiProxy.INSTANCE.get(activity).isActive()) {
             return null;
         }
@@ -295,18 +309,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 +336,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/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index a8a0219..3175ba8 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -104,6 +104,9 @@
      * Synchronously fetches the thumbnail for the given {@param task} and puts it in the cache.
      */
     public void updateThumbnailInCache(Task task) {
+        if (task == null) {
+            return;
+        }
         Preconditions.assertUIThread();
         // Fetch the thumbnail for this task and put it in the cache
         if (task.thumbnail == null) {
@@ -131,7 +134,8 @@
         Preconditions.assertUIThread();
 
         boolean lowResolution = !mHighResLoadingState.isEnabled();
-        if (task.thumbnail != null && (!task.thumbnail.reducedResolution || lowResolution)) {
+        if (task.thumbnail != null && task.thumbnail.thumbnail != null
+                && (!task.thumbnail.reducedResolution || lowResolution)) {
             // Nothing to load, the thumbnail is already high-resolution or matches what the
             // request, so just callback
             callback.accept(task.thumbnail);
@@ -149,7 +153,8 @@
         Preconditions.assertUIThread();
 
         ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
-        if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || lowResolution)) {
+        if (cachedThumbnail != null &&  cachedThumbnail.thumbnail != null
+                && (!cachedThumbnail.reducedResolution || lowResolution)) {
             // Already cached, lets use that thumbnail
             callback.accept(cachedThumbnail);
             return null;
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 37fda73..30e225a 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;
 
@@ -27,10 +28,11 @@
 import static com.android.launcher3.QuickstepTransitionManager.NAV_FADE_IN_INTERPOLATOR;
 import static com.android.launcher3.QuickstepTransitionManager.NAV_FADE_OUT_INTERPOLATOR;
 import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.SPLIT_DIVIDER_ANIM_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.SPLIT_LAUNCH_DURATION;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
@@ -41,6 +43,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;
@@ -62,15 +65,16 @@
 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;
 import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.views.GroupedTaskView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
@@ -79,6 +83,10 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
 /**
  * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
  */
@@ -139,39 +147,21 @@
 
         // 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;
         }
         return taskView;
     }
 
-    public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            RemoteAnimationTargetCompat[] nonAppTargets, DepthController depthController,
+    public static void createRecentsWindowAnimator(
+            @NonNull TaskView v, boolean skipViewChanges,
+            @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+            @Nullable 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 +172,77 @@
                         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 (v.containsMultipleTasks()) {
+                remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets, v.getTaskIds());
+            } 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);
+        int taskRectTranslationPrimary = recentsView.getScrollOffset(taskIndex);
+        int taskRectTranslationSecondary = showAsGrid ? (int) v.getGridTranslationY() : 0;
 
-        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;
+                tvsLocal.setIsGridTask(v.isGridTask());
+                tvsLocal.getOrientationState().getOrientationHandler().set(tvsLocal,
+                        TaskViewSimulator::setTaskRectTranslation, taskRectTranslationPrimary,
+                        taskRectTranslationSecondary);
+
+                // 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);
-            if (showAsGrid) {
-                out.setFloat(tsv.taskSecondaryTranslation, AnimatedFloat.VALUE, 0,
-                        TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL);
-            }
-            out.setFloat(tsv.recentsViewScroll, AnimatedFloat.VALUE, 0,
+            out.setFloat(tvsLocal.recentsViewScale,
+                    AnimatedFloat.VALUE, tvsLocal.getFullScreenScale(),
+                    TOUCH_RESPONSE_INTERPOLATOR);
+            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 +255,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 +280,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 +303,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();
+                localMt.invert(localMti);
+                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 +388,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 +399,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 +408,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());
             }
         }
@@ -404,78 +427,102 @@
         finishCallback.run();
     }
 
-    /** Legacy version (until shell transitions are enabled) */
-    public static void composeRecentsSplitLaunchAnimatorLegacy(@NonNull AnimatorSet anim,
-            @NonNull TaskView v, @NonNull RemoteAnimationTargetCompat[] appTargets,
+    /**
+     * Legacy version (until shell transitions are enabled)
+     *
+     * If {@param launchingTaskView} is not null, then this will play the tasks launch animation
+     * from the position of the GroupedTaskView (when user taps on the TaskView to start it).
+     * Technically this case should be taken care of by
+     * {@link #composeRecentsSplitLaunchAnimatorLegacy()} below, but the way we launch tasks whether
+     * it's a single task or multiple tasks results in different entry-points.
+     *
+     * If it is null, then it will simply fade in the starting apps and fade out launcher (for the
+     * case where launcher handles animating starting split tasks from app icon) */
+    public static void composeRecentsSplitLaunchAnimatorLegacy(
+            @Nullable GroupedTaskView launchingTaskView,
+            @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 StateManager stateManager,
+            @Nullable DepthController depthController,
+            @NonNull Runnable finishCallback) {
+        if (launchingTaskView != null) {
+            AnimatorSet animatorSet = new AnimatorSet();
+            RecentsView recentsView = launchingTaskView.getRecentsView();
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    finishCallback.run();
+                }
+            });
+            composeRecentsLaunchAnimator(animatorSet, launchingTaskView,
+                    appTargets, wallpaperTargets, nonAppTargets,
+                    true, stateManager,
+                    recentsView, depthController);
+            animatorSet.start();
+            return;
         }
 
-        boolean inLiveTileMode =
-                ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1;
-        final RemoteAnimationTargets targets =
-                new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
-                        inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
+        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 (params == null) {
-            SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
-            targets.addReleaseCheck(applier);
-
-            params = new TransformParams()
-                    .setSyncTransactionApplier(applier)
-                    .setTargetSet(targets);
+            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);
+            }
         }
 
-        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);
-
-            // 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));
+        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);
+            }
         }
 
-        TaskViewSimulator topMostSimulator = null;
+        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+        animator.setDuration(SPLIT_LAUNCH_DURATION);
+        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();
+            }
 
-        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,
@@ -483,13 +530,26 @@
             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
             @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
             @NonNull StateManager stateManager, @NonNull RecentsView recentsView,
-            @NonNull DepthController depthController) {
+            @Nullable DepthController depthController) {
         boolean skipLauncherChanges = !launcherClosing;
 
         TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
         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.createSplitAuxiliarySurfacesAnimator(nonAppTargets, true /*shown*/,
+                    (dividerAnimator) -> {
+                        // If split apps are launching, we want to delay showing the divider bar
+                        // until the very end once the apps are mostly in place. This is because we
+                        // aren't moving the divider leash in the relative position with the
+                        // launching apps.
+                        dividerAnimator.setStartDelay(pa.getDuration()
+                                - SPLIT_DIVIDER_ANIM_DURATION);
+                        pa.add(dividerAnimator);
+                    });
+        }
 
         Animator childStateAnimation = null;
         // Found a visible recents task that matches the opening app, lets launch the app from there
@@ -498,7 +558,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 +602,70 @@
         stateManager.setCurrentAnimation(anim, childStateAnimation);
         anim.addListener(windowAnimEndListener);
     }
+
+    /**
+     * Creates an animation to show/hide the auxiliary surfaces (aka. divider bar), only calling
+     * {@param animatorHandler} if there are valid surfaces to animate.
+     *
+     * @return the animator animating the surfaces
+     */
+    public static ValueAnimator createSplitAuxiliarySurfacesAnimator(
+            RemoteAnimationTargetCompat[] nonApps, boolean shown,
+            Consumer<ValueAnimator> animatorHandler) {
+        if (nonApps == null || nonApps.length == 0) {
+            return null;
+        }
+
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        List<SurfaceControl> auxiliarySurfaces = new ArrayList<>(nonApps.length);
+        boolean hasSurfaceToAnimate = false;
+        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) {
+                auxiliarySurfaces.add(leash);
+                hasSurfaceToAnimate = true;
+            }
+        }
+        if (!hasSurfaceToAnimate) {
+            return null;
+        }
+
+        ValueAnimator dockFadeAnimator = ValueAnimator.ofFloat(0f, 1f);
+        dockFadeAnimator.addUpdateListener(valueAnimator -> {
+            float progress = valueAnimator.getAnimatedFraction();
+            for (SurfaceControl leash : auxiliarySurfaces) {
+                t.setAlpha(leash, shown ? progress : 1 - progress);
+            }
+            t.apply();
+        });
+        dockFadeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                if (shown) {
+                    for (SurfaceControl leash : auxiliarySurfaces) {
+                        t.setAlpha(leash, 0);
+                        t.show(leash);
+                    }
+                    t.apply();
+                }
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (!shown) {
+                    for (SurfaceControl leash : auxiliarySurfaces) {
+                        t.hide(leash);
+                    }
+                    t.apply();
+                }
+                t.close();
+            }
+        });
+        dockFadeAnimator.setDuration(SPLIT_DIVIDER_ANIM_DURATION);
+        animatorHandler.accept(dockFadeAnimator);
+        return dockFadeAnimator;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 0f5671c..1f6a974 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -25,6 +25,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.GestureState.DEFAULT_STATE;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -32,6 +33,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
 import android.annotation.TargetApi;
@@ -40,7 +42,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 +68,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 +90,19 @@
 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.ProgressDelegateInputConsumer;
 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;
@@ -113,6 +114,7 @@
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
+import com.android.wm.shell.recents.IRecentTasks;
 import com.android.wm.shell.splitscreen.ISplitScreen;
 import com.android.wm.shell.startingsurface.IStartingWindow;
 import com.android.wm.shell.transition.IShellTransitions;
@@ -121,13 +123,14 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.LinkedList;
+import java.util.function.Function;
 
 /**
  * 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 +149,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
@@ -170,18 +173,14 @@
             ISmartspaceTransitionController smartspaceTransitionController =
                     ISmartspaceTransitionController.Stub.asInterface(
                             bundle.getBinder(KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER));
+            IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
+                    bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
             MAIN_EXECUTOR.execute(() -> {
                 SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
-                        splitscreen, onehanded, shellTransitions, startingWindow,
+                        splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
                         smartspaceTransitionController);
                 TouchInteractionService.this.initInputMonitor();
                 preloadOverview(true /* fromInit */);
-                mDeviceState.runOnUserUnlocked(() -> {
-                    final BaseActivityInterface ai =
-                            mOverviewComponentObserver.getActivityInterface();
-                    if (ai == null) return;
-                    ai.onOverviewServiceBound();
-                });
             });
             sIsInitialized = true;
         }
@@ -262,16 +261,47 @@
         }
 
         @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));
+        }
+
+        @Override
+        public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+            executeForTaskbarManager(() -> mTaskbarManager
+                    .onNavButtonsDarkIntensityChanged(darkIntensity));
+        }
+
+        private void executeForTaskbarManager(final Runnable r) {
+            MAIN_EXECUTOR.execute(() -> {
+                if (mTaskbarManager == null) {
+                    return;
+                }
+                r.run();
+            });
         }
 
         public TaskbarManager getTaskbarManager() {
@@ -281,6 +311,13 @@
         public OverviewCommandHelper getOverviewCommandHelper() {
             return mOverviewCommandHelper;
         }
+
+        /**
+         * Sets a proxy to bypass swipe up behavior
+         */
+        public void setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy) {
+            mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null);
+        }
     }
 
     private static boolean sConnected = false;
@@ -291,7 +328,6 @@
         return sConnected;
     }
 
-
     public static boolean isInitialized() {
         return sIsInitialized;
     }
@@ -320,6 +356,7 @@
     private DisplayManager mDisplayManager;
 
     private TaskbarManager mTaskbarManager;
+    private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
 
     @Override
     public void onCreate() {
@@ -340,6 +377,7 @@
         mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
 
         ProtoTracer.INSTANCE.get(this).add(this);
+        LauncherSplitScreenListener.INSTANCE.get(this).init();
         sConnected = true;
     }
 
@@ -401,9 +439,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 +480,12 @@
         } else {
             am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
         }
+
+        StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
+                .getCreatedActivity();
+        if (newOverviewActivity != null) {
+            mTaskbarManager.setActivity(newOverviewActivity);
+        }
     }
 
     @UiThread
@@ -455,6 +496,15 @@
             mOverviewComponentObserver.onSystemUiStateChanged();
             mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
 
+            boolean wasExpanded = (lastSysUIFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
+            boolean isExpanded =
+                    (systemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0;
+            if (wasExpanded != isExpanded && isExpanded) {
+                // End live tile when expanding the notification panel for the first time from
+                // overview.
+                mTaskAnimationManager.endLiveTile();
+            }
+
             if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) !=
                     (systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) {
                 // Update the tracing state
@@ -485,7 +535,6 @@
         if (mDeviceState.isUserUnlocked()) {
             mInputConsumer.unregisterInputConsumer();
             mOverviewComponentObserver.onDestroy();
-            PluginManagerWrapper.INSTANCE.get(getBaseContext()).removePluginListener(this);
         }
         disposeEventHandlers();
         mDeviceState.destroy();
@@ -496,6 +545,7 @@
         getSystemService(AccessibilityManager.class)
                 .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
 
+        LauncherSplitScreenListener.INSTANCE.get(this).destroy();
         mTaskbarManager.destroy();
         sConnected = false;
         super.onDestroy();
@@ -504,7 +554,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) {
@@ -625,14 +675,20 @@
             gestureState.updatePreviouslyAppearedTaskIds(
                     previousGestureState.getPreviouslyAppearedTaskIds());
         } else {
-            gestureState.updateRunningTask(TraceHelper.allowIpcs("getRunningTask.0",
-                    () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
+            gestureState.updateRunningTasks(TraceHelper.allowIpcs("getRunningTask.0",
+                    () -> mAM.getRunningTasks(false /* filterOnlyVisibleRecents */)));
         }
         return gestureState;
     }
 
     private InputConsumer newConsumer(GestureState previousGestureState,
             GestureState newGestureState, MotionEvent event) {
+        AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
+        if (progressProxy != null) {
+            return new ProgressDelegateInputConsumer(this, mTaskAnimationManager,
+                    mGestureState, mInputMonitorCompat, progressProxy);
+        }
+
         boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
 
         if (!mDeviceState.isUserUnlocked()) {
@@ -660,24 +716,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
@@ -923,6 +967,9 @@
             if (mOverviewComponentObserver != null) {
                 mOverviewComponentObserver.dump(pw);
             }
+            if (mOverviewCommandHelper != null) {
+                mOverviewCommandHelper.dump(pw);
+            }
             if (mGestureState != null) {
                 mGestureState.dump(pw);
             }
@@ -939,6 +986,7 @@
             pw.println("  resumed=" + resumed);
             pw.println("  mConsumer=" + mConsumer.getName());
             ActiveGestureLog.INSTANCE.dump("", pw);
+            RecentsModel.INSTANCE.get(this).dump("", pw);
             pw.println("ProtoTrace:");
             pw.println("  file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
         }
@@ -971,32 +1019,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/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
index 184ab17..e290be8 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -15,8 +15,10 @@
  */
 package com.android.quickstep;
 
+import android.graphics.HardwareRenderer;
 import android.os.Handler;
 import android.view.View;
+import android.view.ViewRootImpl;
 
 import com.android.launcher3.Utilities;
 import com.android.systemui.shared.system.ViewRootImplCompat;
@@ -45,9 +47,9 @@
         return new FrameHandler(view, onFinishRunnable, canceled).schedule();
     }
 
-    private static class FrameHandler implements LongConsumer {
+    private static class FrameHandler implements HardwareRenderer.FrameDrawingCallback {
 
-        final ViewRootImplCompat mViewRoot;
+        final ViewRootImpl mViewRoot;
         final Runnable mFinishCallback;
         final BooleanSupplier mCancelled;
         final Handler mHandler;
@@ -55,14 +57,14 @@
         int mDeferFrameCount = 1;
 
         FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
-            mViewRoot = new ViewRootImplCompat(view);
+            mViewRoot = view.getViewRootImpl();
             mFinishCallback = finishCallback;
             mCancelled = cancelled;
             mHandler = new Handler();
         }
 
         @Override
-        public void accept(long l) {
+        public void onFrameDraw(long frame) {
             Utilities.postAsyncCallback(mHandler, this::onFrame);
         }
 
@@ -83,7 +85,7 @@
         }
 
         private boolean schedule() {
-            if (mViewRoot.isValid()) {
+            if (mViewRoot.getView() != null) {
                 mViewRoot.registerRtFrameCallback(this);
                 mViewRoot.getView().invalidate();
                 return true;
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index f0364eb..ff175f1 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;
@@ -22,14 +24,22 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
+import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
 
+import android.util.FloatProperty;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -77,9 +87,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,10 +102,37 @@
         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));
+
+        RecentsState currentState = mActivity.getStateManager().getState();
+        if (isSplitSelectionState(state) && !isSplitSelectionState(currentState)) {
+            setter.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
+        }
+
+        Pair<FloatProperty, FloatProperty> taskViewsFloat =
+                mRecentsView.getPagedOrientationHandler().getSplitSelectTaskOffset(
+                        TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+                        mActivity.getDeviceProfile());
+        setter.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
+        if (isSplitSelectionState(state)) {
+            mRecentsView.applySplitPrimaryScrollOffset();
+            setter.setFloat(mRecentsView, taskViewsFloat.first,
+                    mRecentsView.getSplitSelectTranslation(), LINEAR);
+        } else {
+            mRecentsView.resetSplitPrimaryScrollOffset();
+            setter.setFloat(mRecentsView, taskViewsFloat.first, 0, LINEAR);
+        }
+    }
+
+    /**
+     * @return true if {@param toState} is {@link RecentsState#OVERVIEW_SPLIT_SELECT}
+     */
+    private boolean isSplitSelectionState(@NonNull RecentsState toState) {
+        return toState == OVERVIEW_SPLIT_SELECT;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index ac3fb27..6d22b17 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -19,6 +19,7 @@
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
 import static com.android.quickstep.fallback.RecentsState.HOME;
 import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
+import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
 
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
@@ -26,20 +27,23 @@
 import android.content.Context;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.util.Log;
+import android.view.MotionEvent;
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.AbstractFloatingView;
 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.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.FallbackActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.util.GroupTask;
+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 +66,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);
     }
@@ -71,6 +75,7 @@
     @Override
     public void startHome() {
         mActivity.startHome();
+        AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
     }
 
     /**
@@ -78,8 +83,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 +97,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,48 +123,66 @@
     }
 
     @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) {
-        if (mHomeTaskInfo != null && runningTaskInfo != null &&
-                mHomeTaskInfo.taskId == runningTaskInfo.taskId
-                && getTaskViewCount() == 0) {
-            // Do not add a stub task if we are running over home with empty recents, so that we
-            // show the empty recents message instead of showing a stub task and later removing it.
-            return false;
-        }
-        return super.shouldAddStubTaskView(runningTaskInfo);
+    protected TaskView getHomeTaskView() {
+        return mHomeTaskInfo != null ? getTaskViewByTaskId(mHomeTaskInfo.taskId) : null;
     }
 
     @Override
-    protected void applyLoadPlan(ArrayList<Task> tasks) {
+    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 && mLoadPlanEverApplied) {
+            // Do not add a stub task if we are running over home with empty recents, so that we
+            // show the empty recents message instead of showing a stub task and later removing it.
+            // Ignore empty task signal if applyLoadPlan has never run.
+            return false;
+        }
+        return super.shouldAddStubTaskView(runningTaskInfos);
+    }
+
+    @Override
+    protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
         // 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 && !taskGroups.isEmpty()) {
             // Check if the task list has running task
             boolean found = false;
-            for (Task t : tasks) {
-                if (t.key.id == mRunningTaskId) {
+            for (GroupTask group : taskGroups) {
+                if (group.containsTask(runningTaskId)) {
                     found = true;
                     break;
                 }
             }
             if (!found) {
-                ArrayList<Task> newList = new ArrayList<>(tasks.size() + 1);
-                newList.addAll(tasks);
-                newList.add(Task.from(new TaskKey(mHomeTaskInfo), mHomeTaskInfo, false));
-                tasks = newList;
+                ArrayList<GroupTask> newList = new ArrayList<>(taskGroups.size() + 1);
+                newList.addAll(taskGroups);
+                newList.add(new GroupTask(
+                        Task.from(new TaskKey(mHomeTaskInfo), mHomeTaskInfo, false),
+                        null, null));
+                taskGroups = newList;
             }
         }
-        super.applyLoadPlan(tasks);
+        super.applyLoadPlan(taskGroups);
     }
 
     @Override
@@ -175,11 +202,19 @@
         } else {
             if (mActivity.isInState(RecentsState.MODAL_TASK)) {
                 mActivity.getStateManager().goToState(DEFAULT);
+                resetModalVisuals();
             }
         }
     }
 
     @Override
+    public void initiateSplitSelect(TaskView taskView,
+            @SplitConfigurationOptions.StagePosition int stagePosition) {
+        super.initiateSplitSelect(taskView, stagePosition);
+        mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
+    }
+
+    @Override
     public void onStateTransitionStart(RecentsState toState) {
         setOverviewStateEnabled(true);
         setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
@@ -193,8 +228,14 @@
             // Clean-up logic that occurs when recents is no longer in use/visible.
             reset();
         }
-        setOverlayEnabled(finalState == DEFAULT || finalState == MODAL_TASK);
+        boolean isOverlayEnabled = finalState == DEFAULT || finalState == MODAL_TASK;
+        setOverlayEnabled(isOverlayEnabled);
         setFreezeViewVisibility(false);
+
+        if (isOverlayEnabled) {
+            runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+        }
     }
 
     @Override
@@ -205,4 +246,11 @@
             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();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index b6cfdce..15feb18 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,17 +39,20 @@
     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);
+    public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
+            FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI);
 
     public final int ordinal;
     private final int mFlags;
@@ -133,11 +135,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/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 510820a..162ace4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -24,6 +24,8 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_GESTURE;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
 import static com.android.launcher3.Utilities.squaredHypot;
 
 import android.animation.Animator;
@@ -64,8 +66,6 @@
     private static final String OPA_BUNDLE_TRIGGER = "triggered_by";
     // From //java/com/google/android/apps/gsa/assistant/shared/proto/opa_trigger.proto.
     private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83;
-    private static final String INVOCATION_TYPE_KEY = "invocation_type";
-    private static final int INVOCATION_TYPE_GESTURE = 1;
 
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index fcc0217..3d737ca 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -60,6 +60,8 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
+import java.util.HashMap;
+
 /**
  * A placeholder input consumer used when the device is still locked, e.g. from secure camera.
  */
@@ -239,7 +241,7 @@
     }
 
     @Override
-    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+    public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
         mRecentsAnimationController = null;
         mTransformParams.setTargetSet(null);
     }
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/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index b0df286..02ac48e 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -101,6 +101,13 @@
     }
 
     @Override
+    public void onHoverEvent(MotionEvent ev) {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mActivity.dispatchGenericMotionEvent(ev);
+        }
+    }
+
+    @Override
     public void onKeyEvent(KeyEvent ev) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             switch (ev.getKeyCode()) {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
new file mode 100644
index 0000000..71dca66
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -0,0 +1,194 @@
+/*
+ * 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.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.touch.BaseSwipeDetector.calculateDuration;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.view.MotionEvent;
+
+import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.MultiStateCallback;
+import com.android.quickstep.RecentsAnimationCallbacks;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.TaskAnimationManager;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import java.util.HashMap;
+
+/**
+ * Input consumer which delegates the swipe-progress handling
+ */
+public class ProgressDelegateInputConsumer implements InputConsumer,
+        RecentsAnimationCallbacks.RecentsAnimationListener,
+        SingleAxisSwipeDetector.Listener {
+
+    private static final float SWIPE_DISTANCE_THRESHOLD = 0.2f;
+
+    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[3] : null;
+    private static int getFlagForIndex(int index, String name) {
+        if (DEBUG_STATES) {
+            STATE_NAMES[index] = name;
+        }
+        return 1 << index;
+    }
+
+    private static final int STATE_TARGET_RECEIVED =
+            getFlagForIndex(0, "STATE_TARGET_RECEIVED");
+    private static final int STATE_HANDLER_INVALIDATED =
+            getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
+    private static final int STATE_FLING_FINISHED =
+            getFlagForIndex(2, "STATE_FLING_FINISHED");
+
+    private final Context mContext;
+    private final TaskAnimationManager mTaskAnimationManager;
+    private final GestureState mGestureState;
+    private final InputMonitorCompat mInputMonitorCompat;
+    private final MultiStateCallback mStateCallback;
+
+    private final Point mDisplaySize;
+    private final SingleAxisSwipeDetector mSwipeDetector;
+
+    private final AnimatedFloat mProgress;
+
+    private boolean mDragStarted = false;
+
+    private RecentsAnimationController mRecentsAnimationController;
+    private Boolean mFlingEndsOnHome;
+
+    public ProgressDelegateInputConsumer(Context context,
+            TaskAnimationManager taskAnimationManager, GestureState gestureState,
+            InputMonitorCompat inputMonitorCompat, AnimatedFloat progress) {
+        mContext = context;
+        mTaskAnimationManager = taskAnimationManager;
+        mGestureState = gestureState;
+        mInputMonitorCompat = inputMonitorCompat;
+        mProgress = progress;
+
+        // Do not use DeviceProfile as the user data might be locked
+        mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
+
+        // Init states
+        mStateCallback = new MultiStateCallback(STATE_NAMES);
+        mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
+                this::endRemoteAnimation);
+        mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
+                this::onFlingFinished);
+
+        mSwipeDetector = new SingleAxisSwipeDetector(mContext, this, VERTICAL);
+        mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_PROGRESS_DELEGATE;
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (mFlingEndsOnHome == null) {
+            mSwipeDetector.onTouchEvent(ev);
+        }
+    }
+
+    @Override
+    public void onDragStart(boolean start, float startDisplacement) {
+        mDragStarted = true;
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
+        mInputMonitorCompat.pilferPointers();
+        Intent intent = mGestureState.getHomeIntent()
+                .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
+        mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
+    }
+
+    @Override
+    public boolean onDrag(float displacement) {
+        if (mDisplaySize.y > 0) {
+            mProgress.updateValue(displacement / -mDisplaySize.y);
+        }
+        return true;
+    }
+
+    @Override
+    public void onDragEnd(float velocity) {
+        final boolean willExit;
+        if (mSwipeDetector.isFling(velocity)) {
+            willExit = velocity < 0;
+        } else {
+            willExit = mProgress.value > SWIPE_DISTANCE_THRESHOLD;
+        }
+        float endValue = willExit ? 1 : 0;
+        long duration = calculateDuration(velocity, endValue - mProgress.value);
+        mFlingEndsOnHome = willExit;
+
+        ObjectAnimator anim = mProgress.animateToValue(endValue);
+        anim.setDuration(duration).setInterpolator(scrollInterpolatorForVelocity(velocity));
+        anim.addListener(AnimatorListeners.forSuccessCallback(
+                () -> mStateCallback.setState(STATE_FLING_FINISHED)));
+        anim.start();
+    }
+
+    private void onFlingFinished() {
+        if (mRecentsAnimationController != null) {
+            boolean endToRecents = mFlingEndsOnHome == null ? true : mFlingEndsOnHome;
+            mRecentsAnimationController.finishController(endToRecents /* toRecents */,
+                    null /* callback */, false /* sendUserLeaveHint */);
+        }
+    }
+
+    @Override
+    public void onRecentsAnimationStart(RecentsAnimationController controller,
+            RecentsAnimationTargets targets) {
+        mRecentsAnimationController = controller;
+        mStateCallback.setState(STATE_TARGET_RECEIVED);
+    }
+
+    @Override
+    public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
+        mRecentsAnimationController = null;
+    }
+
+    private void endRemoteAnimation() {
+        onDragEnd(Float.MIN_VALUE);
+    }
+
+    @Override
+    public void onConsumerAboutToBeSwitched() {
+        mStateCallback.setState(STATE_HANDLER_INVALIDATED);
+    }
+
+    @Override
+    public boolean allowInterceptByParent() {
+        return !mDragStarted;
+    }
+}
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..1c3e784 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -15,11 +15,32 @@
  */
 package com.android.quickstep.interaction;
 
+import static com.android.launcher3.Utilities.mapBoundToRange;
+import static com.android.launcher3.Utilities.mapRange;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.animation.Animator;
 import android.app.Activity;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PointF;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.util.Log;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
@@ -29,9 +50,16 @@
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.TouchInteractionService.TISBinder;
+import com.android.quickstep.util.TISBindHelper;
+
+import com.airbnb.lottie.LottieAnimationView;
 
 import java.net.URISyntaxException;
 
@@ -47,10 +75,27 @@
     private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "suwColorAccentDark";
     private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "suwColorAccentLight";
 
+    private static final float HINT_BOTTOM_FACTOR = 1 - .94f;
+
+    private TISBindHelper mTISBindHelper;
+    private TISBinder mBinder;
+
+    private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate);
+    private BgDrawable mBackground;
+    private View mContentView;
+    private float mSwipeUpShift;
+
+    @Nullable private Vibrator mVibrator;
+    private LottieAnimationView mAnimatedBackground;
+    private Animator.AnimatorListener mBackgroundAnimatorListener;
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_allset);
+        findViewById(R.id.root_view).setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
 
         int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
         boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
@@ -60,6 +105,11 @@
 
         ((ImageView) findViewById(R.id.icon)).getDrawable().mutate().setTint(accentColor);
 
+        mBackground = new BgDrawable(this);
+        findViewById(R.id.root_view).setBackground(mBackground);
+        mContentView = findViewById(R.id.content_view);
+        mSwipeUpShift = getResources().getDimension(R.dimen.allset_swipe_up_shift);
+
         TextView tv = findViewById(R.id.navigation_settings);
         tv.setTextColor(accentColor);
         tv.setOnClickListener(v -> {
@@ -73,6 +123,120 @@
         });
 
         findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+        mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
+
+        mVibrator = getSystemService(Vibrator.class);
+        mAnimatedBackground = findViewById(R.id.animated_background);
+        startBackgroundAnimation();
+    }
+
+    private void startBackgroundAnimation() {
+        if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported(
+                VibrationEffect.Composition.PRIMITIVE_THUD)) {
+            if (mBackgroundAnimatorListener == null) {
+                mBackgroundAnimatorListener =
+                        new Animator.AnimatorListener() {
+                            @Override
+                            public void onAnimationStart(Animator animation) {
+                                mVibrator.vibrate(getVibrationEffect());
+                            }
+
+                            @Override
+                            public void onAnimationRepeat(Animator animation) {
+                                mVibrator.vibrate(getVibrationEffect());
+                            }
+
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                mVibrator.cancel();
+                            }
+
+                            @Override
+                            public void onAnimationCancel(Animator animation) {
+                                mVibrator.cancel();
+                            }
+                        };
+            }
+            mAnimatedBackground.addAnimatorListener(mBackgroundAnimatorListener);
+        }
+        mAnimatedBackground.playAnimation();
+    }
+
+    /**
+     * Sets up the vibration effect for the next round of animation. The parameters vary between
+     * different illustrations.
+     */
+    private VibrationEffect getVibrationEffect() {
+        return VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, 50)
+                .compose();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mBinder != null) {
+            mBinder.getTaskbarManager().setSetupUIVisible(true);
+            mBinder.setSwipeUpProxy(this::createSwipeUpProxy);
+        }
+    }
+
+    private void onTISConnected(TISBinder binder) {
+        mBinder = binder;
+        mBinder.getTaskbarManager().setSetupUIVisible(isResumed());
+        mBinder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        clearBinderOverride();
+        if (mSwipeProgress.value >= 1) {
+            finishAndRemoveTask();
+        }
+    }
+
+    private void clearBinderOverride() {
+        if (mBinder != null) {
+            mBinder.getTaskbarManager().setSetupUIVisible(false);
+            mBinder.setSwipeUpProxy(null);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mTISBindHelper.onDestroy();
+        clearBinderOverride();
+        if (mBackgroundAnimatorListener != null) {
+            mAnimatedBackground.removeAnimatorListener(mBackgroundAnimatorListener);
+        }
+    }
+
+    private AnimatedFloat createSwipeUpProxy(GestureState state) {
+        if (!state.getHomeIntent().getComponent().getPackageName().equals(getPackageName())) {
+            return null;
+        }
+        RunningTaskInfo rti = state.getRunningTask();
+        if (rti == null || !rti.topActivity.equals(getComponentName())) {
+            return null;
+        }
+        mSwipeProgress.updateValue(0);
+        return mSwipeProgress;
+    }
+
+    private void onSwipeProgressUpdate() {
+        mBackground.setProgress(mSwipeProgress.value);
+        float alpha = Utilities.mapBoundToRange(mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR,
+                1, 0, LINEAR);
+        mContentView.setAlpha(alpha);
+        mContentView.setTranslationY((alpha - 1) * mSwipeUpShift);
+
+        if (alpha == 0f) {
+            mAnimatedBackground.pauseAnimation();
+        } else if (!mAnimatedBackground.isAnimating()) {
+            mAnimatedBackground.resumeAnimation();
+        }
     }
 
     /**
@@ -99,4 +263,79 @@
             return super.performAccessibilityAction(host, action, args);
         }
     }
+
+    private static class BgDrawable extends Drawable {
+
+        private static final float START_SIZE_FACTOR = .5f;
+        private static final float END_SIZE_FACTOR = 2;
+        private static final float GRADIENT_END_PROGRESS = .5f;
+
+        private final Paint mPaint = new Paint();
+        private final RadialGradient mMaskGrad;
+        private final Matrix mMatrix = new Matrix();
+
+        private final ColorMatrix mColorMatrix = new ColorMatrix();
+        private final ColorMatrixColorFilter mColorFilter =
+                new ColorMatrixColorFilter(mColorMatrix);
+
+        private final int mColor;
+        private float mProgress = 0;
+
+        BgDrawable(Context context) {
+            mColor = context.getColor(R.color.all_set_page_background);
+            mMaskGrad = new RadialGradient(0, 0, 1,
+                    new int[] {ColorUtils.setAlphaComponent(mColor, 0), mColor},
+                    new float[]{0, 1}, TileMode.CLAMP);
+
+            mPaint.setShader(mMaskGrad);
+            mPaint.setColorFilter(mColorFilter);
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            if (mProgress <= 0) {
+                canvas.drawColor(mColor);
+                return;
+            }
+
+            // Update the progress to half the size only.
+            float progress = mapBoundToRange(mProgress,
+                    0, GRADIENT_END_PROGRESS, 0, 1, LINEAR);
+            Rect bounds = getBounds();
+            float x = bounds.exactCenterX();
+            float height = bounds.height();
+
+            float size = PointF.length(x, height);
+            float radius = size * mapRange(progress, START_SIZE_FACTOR, END_SIZE_FACTOR);
+            float y = mapRange(progress, height + radius , height / 2);
+            mMatrix.setTranslate(x, y);
+            mMatrix.postScale(radius, radius, x, y);
+            mMaskGrad.setLocalMatrix(mMatrix);
+
+            // Change the alpha-addition-component (index 19) so that every pixel is updated
+            // accordingly
+            mColorMatrix.getArray()[19] = mapBoundToRange(mProgress, 0, 1, 0, -255, LINEAR);
+            mColorFilter.setColorMatrix(mColorMatrix);
+
+            canvas.drawPaint(mPaint);
+        }
+
+        public void setProgress(float progress) {
+            if (mProgress != progress) {
+                mProgress = progress;
+                invalidateSelf();
+            }
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.TRANSLUCENT;
+        }
+
+        @Override
+        public void setAlpha(int i) { }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) { }
+    }
 }
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/AnimatedTaskbarView.java b/quickstep/src/com/android/quickstep/interaction/AnimatedTaskbarView.java
new file mode 100644
index 0000000..e8cc45b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/AnimatedTaskbarView.java
@@ -0,0 +1,368 @@
+/*
+ * 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.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.launcher3.R;
+
+import java.util.ArrayList;
+
+
+/**
+ * Helper View for the gesture tutorial mock taskbar view.
+ *
+ * This helper class allows animating this mock taskview to and from a mock hotseat and the bottom
+ * of the screen.
+ */
+public class AnimatedTaskbarView extends ConstraintLayout {
+
+    private View mBackground;
+    private View mIconContainer;
+    private View mIcon1;
+    private View mIcon2;
+    private View mIcon3;
+    private View mIcon4;
+    private View mIcon5;
+    private View mIcon6;
+
+    @Nullable private Animator mRunningAnimator;
+
+    public AnimatedTaskbarView(@NonNull Context context) {
+        super(context);
+    }
+
+    public AnimatedTaskbarView(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AnimatedTaskbarView(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public AnimatedTaskbarView(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mBackground = findViewById(R.id.taskbar_background);
+        mIconContainer = findViewById(R.id.icon_container);
+        mIcon1 = findViewById(R.id.taskbar_icon_1);
+        mIcon2 = findViewById(R.id.taskbar_icon_2);
+        mIcon3 = findViewById(R.id.taskbar_icon_3);
+        mIcon4 = findViewById(R.id.taskbar_icon_4);
+        mIcon5 = findViewById(R.id.taskbar_icon_5);
+        mIcon6 = findViewById(R.id.taskbar_icon_6);
+    }
+
+    /**
+     * Animates this fake taskbar's disappearance into the given hotseat view.
+     */
+    public void animateDisappearanceToHotseat(ViewGroup hotseat) {
+        ArrayList<Animator> animators = new ArrayList<>();
+        int hotseatTop = hotseat.getTop();
+
+        animators.add(ObjectAnimator.ofFloat(
+                mBackground, View.TRANSLATION_Y, 0, mBackground.getHeight()));
+        animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 1f, 0f));
+        animators.add(createIconDisappearanceToHotseatAnimator(
+                mIcon1, hotseat.findViewById(R.id.hotseat_icon_1), hotseatTop));
+        animators.add(createIconDisappearanceToHotseatAnimator(
+                mIcon2, hotseat.findViewById(R.id.hotseat_icon_2), hotseatTop));
+        animators.add(createIconDisappearanceToHotseatAnimator(
+                mIcon3, hotseat.findViewById(R.id.hotseat_icon_3), hotseatTop));
+        animators.add(createIconDisappearanceToHotseatAnimator(
+                mIcon4, hotseat.findViewById(R.id.hotseat_icon_4), hotseatTop));
+        animators.add(createIconDisappearanceToHotseatAnimator(
+                mIcon5, hotseat.findViewById(R.id.hotseat_icon_5), hotseatTop));
+        animators.add(createIconDisappearanceToHotseatAnimator(
+                mIcon6, hotseat.findViewById(R.id.hotseat_icon_6), hotseatTop));
+
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        animatorSet.playTogether(animators);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                setVisibility(INVISIBLE);
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                setVisibility(VISIBLE);
+            }
+        });
+
+        start(animatorSet);
+    }
+
+    /**
+     * Animates this fake taskbar's appearance from the given hotseat view.
+     */
+    public void animateAppearanceFromHotseat(ViewGroup hotseat) {
+        ArrayList<Animator> animators = new ArrayList<>();
+        int hotseatTop = hotseat.getTop();
+
+        animators.add(ObjectAnimator.ofFloat(
+                mBackground, View.TRANSLATION_Y, mBackground.getHeight(), 0));
+        animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 0f, 1f));
+        animators.add(createIconAppearanceFromHotseatAnimator(
+                mIcon1, hotseat.findViewById(R.id.hotseat_icon_1), hotseatTop));
+        animators.add(createIconAppearanceFromHotseatAnimator(
+                mIcon2, hotseat.findViewById(R.id.hotseat_icon_2), hotseatTop));
+        animators.add(createIconAppearanceFromHotseatAnimator(
+                mIcon3, hotseat.findViewById(R.id.hotseat_icon_3), hotseatTop));
+        animators.add(createIconAppearanceFromHotseatAnimator(
+                mIcon4, hotseat.findViewById(R.id.hotseat_icon_4), hotseatTop));
+        animators.add(createIconAppearanceFromHotseatAnimator(
+                mIcon5, hotseat.findViewById(R.id.hotseat_icon_5), hotseatTop));
+        animators.add(createIconAppearanceFromHotseatAnimator(
+                mIcon6, hotseat.findViewById(R.id.hotseat_icon_6), hotseatTop));
+
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        animatorSet.playTogether(animators);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                setVisibility(VISIBLE);
+            }
+        });
+
+        start(animatorSet);
+    }
+
+    /**
+     * Animates this fake taskbar's disappearance to the bottom of the screen.
+     */
+    public void animateDisappearanceToBottom() {
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(ObjectAnimator.ofFloat(
+                mBackground, View.TRANSLATION_Y, 0, mBackground.getHeight()));
+        animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 1f, 0f));
+        animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_X, 1f, 0f));
+        animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_Y, 1f, 0f));
+
+        initializeIconContainerPivot();
+
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        animatorSet.playTogether(animators);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                setVisibility(INVISIBLE);
+                resetIconContainerPivot();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                resetIconContainerPivot();
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                setVisibility(VISIBLE);
+            }
+        });
+
+        start(animatorSet);
+    }
+
+    /**
+     * Animates this fake taskbar's appearance from the bottom of the screen.
+     */
+    public void animateAppearanceFromBottom() {
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(ObjectAnimator.ofFloat(
+                mBackground, View.TRANSLATION_Y, mBackground.getHeight(), 0));
+        animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 0f, 1f));
+        animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_X, 0f, 1f));
+        animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_Y, 0f, 1f));
+
+        initializeIconContainerPivot();
+
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        animatorSet.playTogether(animators);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                setVisibility(VISIBLE);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                resetIconContainerPivot();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                resetIconContainerPivot();
+            }
+        });
+
+        start(animatorSet);
+    }
+
+    private void initializeIconContainerPivot() {
+        mIconContainer.setPivotX(getWidth() / 2f);
+        mIconContainer.setPivotY(getHeight() * 0.8f);
+    }
+
+    private void resetIconContainerPivot() {
+        mIconContainer.resetPivot();
+        mIconContainer.setScaleX(1f);
+        mIconContainer.setScaleY(1f);
+    }
+
+    private void start(Animator animator) {
+        if (mRunningAnimator != null) {
+            mRunningAnimator.cancel();
+        }
+
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                mRunningAnimator = null;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                mRunningAnimator = null;
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                mRunningAnimator = animator;
+            }
+        });
+
+        animator.start();
+    }
+
+    private Animator createIconDisappearanceToHotseatAnimator(
+            View taskbarIcon, View hotseatIcon, int hotseatTop) {
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(ObjectAnimator.ofFloat(
+                taskbarIcon,
+                View.TRANSLATION_Y,
+                0,
+                (hotseatTop + hotseatIcon.getTop()) - (getTop() + taskbarIcon.getTop())));
+        animators.add(ObjectAnimator.ofFloat(
+                taskbarIcon, View.TRANSLATION_X, 0, hotseatIcon.getLeft() - taskbarIcon.getLeft()));
+        animators.add(ObjectAnimator.ofFloat(
+                taskbarIcon,
+                View.SCALE_X,
+                1f,
+                (float) hotseatIcon.getWidth() / (float) taskbarIcon.getWidth()));
+        animators.add(ObjectAnimator.ofFloat(
+                taskbarIcon,
+                View.SCALE_Y,
+                1f,
+                (float) hotseatIcon.getHeight() / (float) taskbarIcon.getHeight()));
+        animators.add(ObjectAnimator.ofFloat(taskbarIcon, View.ALPHA, 1f, 0f));
+
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        animatorSet.playTogether(animators);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                taskbarIcon.setVisibility(INVISIBLE);
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                taskbarIcon.setVisibility(VISIBLE);
+            }
+        });
+
+        return animatorSet;
+    }
+
+    private Animator createIconAppearanceFromHotseatAnimator(
+            View taskbarIcon, View hotseatIcon, int hotseatTop) {
+        ArrayList<Animator> animators = new ArrayList<>();
+
+        animators.add(ObjectAnimator.ofFloat(
+                taskbarIcon,
+                View.TRANSLATION_Y,
+                (hotseatTop + hotseatIcon.getTop()) - (getTop() + taskbarIcon.getTop()),
+                0));
+        animators.add(ObjectAnimator.ofFloat(
+                taskbarIcon, View.TRANSLATION_X, hotseatIcon.getLeft() - taskbarIcon.getLeft(), 0));
+        animators.add(ObjectAnimator.ofFloat(
+                taskbarIcon,
+                View.SCALE_X,
+                (float) hotseatIcon.getWidth() / (float) taskbarIcon.getWidth(),
+                1f));
+        animators.add(ObjectAnimator.ofFloat(
+                taskbarIcon,
+                View.SCALE_Y,
+                (float) hotseatIcon.getHeight() / (float) taskbarIcon.getHeight(),
+                1f));
+        animators.add(ObjectAnimator.ofFloat(taskbarIcon, View.ALPHA, 0f, 1f));
+
+        AnimatorSet animatorSet = new AnimatorSet();
+
+        animatorSet.playTogether(animators);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                taskbarIcon.setVisibility(VISIBLE);
+            }
+        });
+
+        return animatorSet;
+    }
+}
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..49d8203 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,34 @@
     }
 
     @Override
-    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
-        return R.drawable.mock_conversation;
+    public Integer getSuccessFeedbackSubtitle() {
+        return mTutorialFragment.isAtFinalStep()
+                ? R.string.back_gesture_feedback_complete_without_follow_up
+                : R.string.back_gesture_feedback_complete_with_overview_follow_up;
+    }
+
+    @Override
+    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,14 +90,9 @@
         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));
-                int subtitleResId = mTutorialFragment.isAtFinalStep()
-                        ? R.string.back_gesture_feedback_complete_without_follow_up
-                        : R.string.back_gesture_feedback_complete_with_overview_follow_up;
-                showFeedback(subtitleResId, true);
+                mTutorialFragment.releaseFeedbackAnimation();
+                updateFakeAppTaskViewLayout(getMockAppTaskPreviousPageLayoutResId());
+                showSuccessFeedback();
                 break;
             case BACK_CANCELLED_FROM_LEFT:
             case BACK_CANCELLED_FROM_RIGHT:
@@ -94,7 +109,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/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index 7fb7d29..c2524b1 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -35,10 +35,9 @@
 /** Shows the gesture interactive sandbox in full screen mode. */
 public class GestureSandboxActivity extends FragmentActivity {
 
-    private static final String LOG_TAG = "GestureSandboxActivity";
-
     private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
     private static final String KEY_CURRENT_STEP = "current_step";
+    private static final String KEY_GESTURE_COMPLETE = "gesture_complete";
 
     private TutorialType[] mTutorialSteps;
     private TutorialType mCurrentTutorialStep;
@@ -56,7 +55,8 @@
         Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
         mTutorialSteps = getTutorialSteps(args);
         mCurrentTutorialStep = mTutorialSteps[mCurrentStep - 1];
-        mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
+        mFragment = TutorialFragment.newInstance(
+                mCurrentTutorialStep, args.getBoolean(KEY_GESTURE_COMPLETE, false));
         getSupportFragmentManager().beginTransaction()
                 .add(R.id.gesture_tutorial_fragment_container, mFragment)
                 .commit();
@@ -87,6 +87,7 @@
     protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
         savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
         savedInstanceState.putInt(KEY_CURRENT_STEP, mCurrentStep);
+        savedInstanceState.putBoolean(KEY_GESTURE_COMPLETE, mFragment.isGestureComplete());
         super.onSaveInstanceState(savedInstanceState);
     }
 
@@ -121,7 +122,7 @@
             return;
         }
         mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
-        mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
+        mFragment = TutorialFragment.newInstance(mCurrentTutorialStep, false);
         getSupportFragmentManager().beginTransaction()
                 .replace(R.id.gesture_tutorial_fragment_container, mFragment)
                 .runOnCommit(() -> mFragment.onAttachedToWindow())
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 819c91c..0bc3691 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -42,13 +42,22 @@
     }
 
     @Override
-    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
-        return forDarkMode ? R.drawable.mock_webpage_dark_mode : R.drawable.mock_webpage_light_mode;
+    public Integer getSuccessFeedbackSubtitle() {
+        return mTutorialFragment.isAtFinalStep()
+                ? R.string.home_gesture_feedback_complete_without_follow_up
+                : R.string.home_gesture_feedback_complete_with_follow_up;
+    }
+
+    @Override
+    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,19 +82,16 @@
 
     @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
-                                : R.string.home_gesture_feedback_complete_with_follow_up;
-                        showFeedback(subtitleResId, true);
+                        showSuccessFeedback();
                         break;
                     }
                     case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
@@ -93,8 +99,10 @@
                         showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
                         break;
                     case OVERVIEW_GESTURE_COMPLETED:
-                        fadeOutFakeTaskView(true, true, () ->
-                                showFeedback(R.string.home_gesture_feedback_overview_detected));
+                        fadeOutFakeTaskView(true, true, () -> {
+                            showFeedback(R.string.home_gesture_feedback_overview_detected);
+                            showFakeTaskbar(/* animateFromHotseat= */ false);
+                        });
                         break;
                     case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
                     case HOME_OR_OVERVIEW_CANCELLED:
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
index 9572637..423e66f 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(true);
+            }
+        });
+        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(true);
+            }
+        });
+        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..f308f27 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,22 @@
     }
 
     @Override
-    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
-        return R.drawable.mock_conversations_list;
+    public Integer getSuccessFeedbackSubtitle() {
+        return mTutorialFragment.getNumSteps() > 1 && mTutorialFragment.isAtFinalStep()
+                ? R.string.overview_gesture_feedback_complete_with_follow_up
+                : R.string.overview_gesture_feedback_complete_without_follow_up;
+    }
+
+    @Override
+    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 +92,7 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         switch (mTutorialType) {
@@ -88,8 +100,8 @@
                 switch (result) {
                     case HOME_GESTURE_COMPLETED: {
                         animateFakeTaskViewHome(finalVelocity, () -> {
-                            resetFakeTaskView();
                             showFeedback(R.string.overview_gesture_feedback_home_detected);
+                            resetFakeTaskView(true);
                         });
                         break;
                     }
@@ -98,19 +110,10 @@
                         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()
-                                ? R.string.overview_gesture_feedback_complete_with_follow_up
-                                : R.string.overview_gesture_feedback_complete_without_follow_up;
-                        showFeedback(subtitleResId, true);
+                        showSuccessFeedback();
                         break;
                     case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
                     case HOME_OR_OVERVIEW_CANCELLED:
@@ -126,4 +129,28 @@
                 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);
+        hideFakeTaskbar(/* animateToHotseat= */ false);
+        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..f63a945 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(false);
+            }
+        });
+        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(false);
+            }
+        });
+        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..672687d 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,9 +50,9 @@
 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;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.TransformParams;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@@ -62,23 +62,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 +89,7 @@
             mFakeTaskView.setAlpha(1);
             mFakePreviousTaskView.setVisibility(View.INVISIBLE);
             mFakePreviousTaskView.setAlpha(1);
+            mFakePreviousTaskView.setToSingleRowLayout(false);
             mShowTasks = false;
             mShowPreviousTasks = false;
             mRunningWindowAnim = null;
@@ -107,9 +110,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 +139,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 +147,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 +161,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);
@@ -178,24 +197,27 @@
             }
         }
         AnimatorSet animset = anim.buildAnim();
+        hideFakeTaskbar(/* animateToHotseat= */ false);
         animset.start();
         mRunningWindowAnim = RunningWindowAnim.wrap(animset);
     }
 
-    void resetFakeTaskView() {
+    void resetFakeTaskView(boolean animateFromHome) {
+        mFakeTaskView.setVisibility(View.VISIBLE);
         PendingAnimation anim = new PendingAnimation(300);
         anim.setFloat(mTaskViewSwipeUpAnimation
                 .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
         anim.setViewAlpha(mFakeTaskView, 1, ACCEL);
         anim.addListener(mResetTaskView);
         AnimatorSet animset = anim.buildAnim();
+        showFakeTaskbar(animateFromHome);
         animset.start();
         mRunningWindowAnim = RunningWindowAnim.wrap(animset);
     }
 
     void animateFakeTaskViewHome(PointF finalVelocity, @Nullable Runnable onEndRunnable) {
-        hideFeedback(true);
         cancelRunningAnimation();
+        hideFakeTaskbar(/* animateToHotseat= */ true);
         mFakePreviousTaskView.setVisibility(View.INVISIBLE);
         mFakeHotseatView.setVisibility(View.VISIBLE);
         mShowPreviousTasks = false;
@@ -214,12 +236,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 +257,7 @@
 
     @Override
     public void onMotionPaused(boolean unused) {
-        if (mGestureCompleted) {
+        if (isGestureCompleted()) {
             return;
         }
         if (mShowTasks) {
@@ -258,19 +277,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,16 +322,15 @@
                 @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);
                 }
 
                 @Override
-                public void update(@Nullable AppCloseConfig config, RectF rect, float progress,
-                        float radius) {
+                public void update(RectF rect, float progress, float radius) {
                     mFakeIconView.setVisibility(View.VISIBLE);
                     mFakeIconView.update(rect, progress,
                             1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
@@ -326,12 +348,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..4145393 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,30 @@
     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 AnimatedTaskbarView mFakeTaskbarView;
+    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;
+    @Nullable private Runnable mFakeTaskbarViewCallback;
+    private final Runnable mShowFeedbackRunnable;
 
     TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
         mTutorialFragment = tutorialFragment;
@@ -100,12 +119,12 @@
         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);
         mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
+        mFakeTaskbarView = rootView.findViewById(R.id.gesture_tutorial_fake_taskbar_view);
         mFakePreviousTaskView =
                 rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
         mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
@@ -113,10 +132,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 +168,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
@@ -168,26 +223,31 @@
         return null;
     }
 
+    @StringRes
+    public Integer getSuccessFeedbackSubtitle() {
+        return null;
+    }
+
     void showFeedback() {
         if (mGestureCompleted) {
             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);
         }
     }
 
     /**
+     * Show feedback reflecting a successful gesture attempt.
+     **/
+    void showSuccessFeedback() {
+        showFeedback(getSuccessFeedbackSubtitle(), true);
+    }
+
+    /**
      * Show feedback reflecting a failed gesture attempt.
      *
      * @param subtitleResId Resource of the text to display.
@@ -215,88 +275,91 @@
             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);
         if (isGestureSuccessful) {
-            hideCloseButton();
             if (mTutorialFragment.isAtFinalStep()) {
                 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;
+        }
+        if (mFakeTaskbarViewCallback != null) {
+            mFakeTaskbarView.removeCallbacks(mFakeTaskbarViewCallback);
+            mFakeTaskbarViewCallback = 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 +368,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 +411,12 @@
 
     @CallSuper
     void transitToController() {
-        hideFeedback(false);
+        hideFeedback();
         hideActionButton();
+        updateCloseButton();
         updateSubtext();
         updateDrawables();
+        updateLayout();
 
         mGestureCompleted = false;
         if (mFakeHotseatView != null) {
@@ -371,30 +424,72 @@
         }
     }
 
-    void hideCloseButton() {
-        mCloseButton.setVisibility(GONE);
-    }
-
-    void showCloseButton() {
-        mCloseButton.setVisibility(View.VISIBLE);
+    void updateCloseButton() {
         mCloseButton.setTextAppearance(Utilities.isDarkTheme(mContext)
                 ? R.style.TextAppearance_GestureTutorial_Feedback_Subtext
                 : R.style.TextAppearance_GestureTutorial_Feedback_Subtext_Dark);
     }
 
     void hideActionButton() {
-        showCloseButton();
+        mCloseButton.setVisibility(View.VISIBLE);
         // Invisible to maintain the layout.
         mActionButton.setVisibility(View.INVISIBLE);
         mActionButton.setOnClickListener(null);
     }
 
     void showActionButton() {
-        hideCloseButton();
+        mCloseButton.setVisibility(GONE);
         mActionButton.setVisibility(View.VISIBLE);
         mActionButton.setOnClickListener(this::onActionButtonClicked);
     }
 
+    void hideFakeTaskbar(boolean animateToHotseat) {
+        if (!mTutorialFragment.isLargeScreen()) {
+            return;
+        }
+        if (mFakeTaskbarViewCallback != null) {
+            mFakeTaskbarView.removeCallbacks(mFakeTaskbarViewCallback);
+        }
+        if (animateToHotseat) {
+            mFakeTaskbarViewCallback = () ->
+                    mFakeTaskbarView.animateDisappearanceToHotseat(mFakeHotseatView);
+        } else {
+            mFakeTaskbarViewCallback = mFakeTaskbarView::animateDisappearanceToBottom;
+        }
+        mFakeTaskbarView.post(mFakeTaskbarViewCallback);
+    }
+
+    void showFakeTaskbar(boolean animateFromHotseat) {
+        if (!mTutorialFragment.isLargeScreen()) {
+            return;
+        }
+        if (mFakeTaskbarViewCallback != null) {
+            mFakeTaskbarView.removeCallbacks(mFakeTaskbarViewCallback);
+        }
+        if (animateFromHotseat) {
+            mFakeTaskbarViewCallback = () ->
+                    mFakeTaskbarView.animateAppearanceFromHotseat(mFakeHotseatView);
+        } else {
+            mFakeTaskbarViewCallback = mFakeTaskbarView::animateAppearanceFromBottom;
+        }
+        mFakeTaskbarView.post(mFakeTaskbarViewCallback);
+    }
+
+    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 +499,38 @@
         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));
+
+            mFakeTaskbarView.setVisibility(mTutorialFragment.isLargeScreen() ? View.VISIBLE : GONE);
+        }
+    }
+
     private AlertDialog createSkipTutorialDialog() {
         if (mContext instanceof GestureSandboxActivity) {
             GestureSandboxActivity sandboxActivity = (GestureSandboxActivity) mContext;
@@ -485,6 +594,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..2fd7cde 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,30 +41,35 @@
 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 {
 
     private static final String LOG_TAG = "TutorialFragment";
     static final String KEY_TUTORIAL_TYPE = "tutorial_type";
+    static final String KEY_GESTURE_COMPLETE = "gesture_complete";
 
     TutorialType mTutorialType;
+    boolean mGestureComplete = false;
     @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;
 
-    public static TutorialFragment newInstance(TutorialType tutorialType) {
+    private boolean mIsLargeScreen;
+
+    public static TutorialFragment newInstance(TutorialType tutorialType, boolean gestureComplete) {
         TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
         if (fragment == null) {
             fragment = new BackGestureTutorialFragment();
@@ -69,6 +78,7 @@
 
         Bundle args = new Bundle();
         args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
+        args.putBoolean(KEY_GESTURE_COMPLETE, gestureComplete);
         fragment.setArguments(args);
         return fragment;
     }
@@ -96,24 +106,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();
@@ -123,8 +135,24 @@
         super.onCreate(savedInstanceState);
         Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
         mTutorialType = (TutorialType) args.getSerializable(KEY_TUTORIAL_TYPE);
+        mGestureComplete = args.getBoolean(KEY_GESTURE_COMPLETE, false);
         mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext());
         mNavBarGestureHandler = new NavBarGestureHandler(getContext());
+
+        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,25 +175,28 @@
             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() || mTutorialController == null) {
             return;
         }
 
-        if (!mIntroductionShown && mTutorialController != null) {
+        if (isGestureComplete()) {
+            mTutorialController.showSuccessFeedback();
+        } else if (!mIntroductionShown) {
             Integer introTileStringResId = mTutorialController.getIntroductionTitle();
             Integer introSubtitleResId = mTutorialController.getIntroductionSubtitle();
             if (introTileStringResId != null && introSubtitleResId != null) {
@@ -176,87 +207,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 +326,7 @@
         mEdgeBackGestureHandler.registerBackGestureAttemptCallback(mTutorialController);
         mNavBarGestureHandler.registerNavBarGestureAttemptCallback(mTutorialController);
         mTutorialType = tutorialType;
+
         initializeFeedbackVideoView();
     }
 
@@ -343,6 +378,11 @@
         return getCurrentStep() == getNumSteps();
     }
 
+    boolean isGestureComplete() {
+        return mGestureComplete
+                || (mTutorialController != null && mTutorialController.isGestureCompleted());
+    }
+
     @Nullable
     private GestureSandboxActivity getGestureSandboxActivity() {
         Context context = getContext();
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 7eca360..a0cd0d7 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;
@@ -44,9 +40,11 @@
 import com.android.launcher3.AutoInstallsLayout;
 import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.model.DeviceGridState;
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SettingsCache;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
@@ -56,12 +54,19 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.Optional;
 
 /**
  * Utility class to log launcher settings changes
  */
 public class SettingsChangeLogger implements
         NavigationModeChangeListener, OnSharedPreferenceChangeListener {
+  
+    /**
+     * Singleton instance
+     */
+    public static MainThreadInitializedObject<SettingsChangeLogger> INSTANCE =
+            new MainThreadInitializedObject<>(SettingsChangeLogger::new);
 
     private static final String TAG = "SettingsChangeLogger";
     private static final String ROOT_TAG = "androidx.preference.PreferenceScreen";
@@ -69,12 +74,15 @@
 
     private final Context mContext;
     private final ArrayMap<String, LoggablePref> mLoggablePrefs;
+    private final StatsLogManager mStatsLogManager;
 
     private Mode mNavMode;
-    private boolean mNotificationDotsEnabled;
+    private StatsLogManager.LauncherEvent mNotificationDotsEvent;
+    private StatsLogManager.LauncherEvent mHomeScreenSuggestionEvent;
 
-    public SettingsChangeLogger(Context context) {
+    private SettingsChangeLogger(Context context) {
         mContext = context;
+        mStatsLogManager = StatsLogManager.newInstance(mContext);
         mLoggablePrefs = loadPrefKeys(context);
         mNavMode = SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
 
@@ -121,60 +129,52 @@
     }
 
     private void onNotificationDotsChanged(boolean isDotsEnabled) {
-        mNotificationDotsEnabled = isDotsEnabled;
-        dispatchUserEvent();
+        StatsLogManager.LauncherEvent mEvent =
+                isDotsEnabled ? LAUNCHER_NOTIFICATION_DOT_ENABLED
+                        : LAUNCHER_NOTIFICATION_DOT_DISABLED;
+
+        // Log only when the setting is actually changed and not during initialization.
+        if (mNotificationDotsEvent != null && mNotificationDotsEvent != mEvent) {
+            mStatsLogManager.logger().log(mNotificationDotsEvent);
+        }
+        mNotificationDotsEvent = mEvent;
     }
 
     @Override
     public void onNavigationModeChanged(Mode newMode) {
         mNavMode = newMode;
-        dispatchUserEvent();
+        mStatsLogManager.logger().log(newMode.launcherEvent);
     }
 
     @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();
+
+            mHomeScreenSuggestionEvent = getDevicePrefs(mContext)
+                    .getBoolean(LAST_PREDICTION_ENABLED_STATE, true)
+                    ? LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED
+                    : LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED;
+
+            mStatsLogManager.logger().log(mHomeScreenSuggestionEvent);
         }
     }
 
-    private void dispatchUserEvent() {
-        StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
-                .withInstanceId(new InstanceIdSequence().newInstanceId());
+    /**
+     * Takes snapshot of all eligible launcher settings and log them with the provided instance ID.
+     */
+    public void logSnapshot(InstanceId snapshotInstanceId) {
+        StatsLogger logger = mStatsLogManager.logger().withInstanceId(snapshotInstanceId);
 
-        logger.log(mNotificationDotsEnabled
-                ? LAUNCHER_NOTIFICATION_DOT_ENABLED
-                : LAUNCHER_NOTIFICATION_DOT_DISABLED);
-        logger.log(mNavMode.launcherEvent);
-        logger.log(getDevicePrefs(mContext).getBoolean(LAST_PREDICTION_ENABLED_STATE, true)
-                ? LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED
-                : LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED);
+        Optional.ofNullable(mNotificationDotsEvent).ifPresent(logger::log);
+        Optional.ofNullable(mNavMode).map(mode -> mode.launcherEvent).ifPresent(logger::log);
+        Optional.ofNullable(mHomeScreenSuggestionEvent).ifPresent(logger::log);
+        Optional.ofNullable(new DeviceGridState(mContext).getWorkspaceSizeEvent()).ifPresent(
+                logger::log);
 
         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;
-        }
-        if (gridSizeChangedEvent != null) {
-            logger.log(gridSizeChangedEvent);
-        }
-
         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/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
index b9879ab..aeec36f 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -15,11 +15,6 @@
  */
 package com.android.quickstep.util;
 
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
@@ -75,17 +70,4 @@
         mIsRegistered = false;
         mOnInitListener = null;
     }
-
-    /**
-     * Starts the given intent with the provided animation. Unlike {@link #register(Intent)}, this
-     * method will not call {@link #init} if the activity already exists, it will only call it when
-     * we get handleIntent() for the provided intent that we're starting.
-     */
-    public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
-            Context context, Handler handler, long duration) {
-        register();
-
-        Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
-        context.startActivity(new Intent(intent), options);
-    }
 }
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/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
new file mode 100644
index 0000000..861ff96
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -0,0 +1,93 @@
+/*
+ * 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.CallSuper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Animation that moves launcher icons and widgets from center to the sides (final position)
+ */
+public abstract class BaseUnfoldMoveFromCenterAnimator implements TransitionProgressListener {
+
+    private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;
+
+    private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
+    private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();
+
+    public BaseUnfoldMoveFromCenterAnimator(WindowManager windowManager) {
+        mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
+                new LauncherViewsMoveFromCenterTranslationApplier());
+    }
+
+    @CallSuper
+    @Override
+    public void onTransitionStarted() {
+        mMoveFromCenterAnimation.updateDisplayProperties();
+        onPrepareViewsForAnimation();
+        onTransitionProgress(0f);
+    }
+
+    @CallSuper
+    @Override
+    public void onTransitionProgress(float progress) {
+        mMoveFromCenterAnimation.onTransitionProgress(progress);
+    }
+
+    @CallSuper
+    @Override
+    public void onTransitionFinished() {
+        mMoveFromCenterAnimation.onTransitionFinished();
+        mMoveFromCenterAnimation.clearRegisteredViews();
+
+        mOriginalClipChildren.clear();
+        mOriginalClipToPadding.clear();
+    }
+
+    protected void onPrepareViewsForAnimation() {
+
+    }
+
+    protected void registerViewForAnimation(View view) {
+        mMoveFromCenterAnimation.registerViewForAnimation(view);
+    }
+
+    protected void disableClipping(ViewGroup view) {
+        mOriginalClipToPadding.put(view, view.getClipToPadding());
+        mOriginalClipChildren.put(view, view.getClipChildren());
+        view.setClipToPadding(false);
+        view.setClipChildren(false);
+    }
+
+    protected 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/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
new file mode 100644
index 0000000..e2563e3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.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.quickstep.util;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * A {@link Task} container that can contain one or two tasks, depending on if the two tasks
+ * are represented as an app-pair in the recents task list.
+ */
+public class GroupTask {
+    public @NonNull Task task1;
+    public @Nullable Task task2;
+    public @Nullable StagedSplitBounds mStagedSplitBounds;
+
+    public GroupTask(@NonNull Task t1, @Nullable Task t2,
+            @Nullable StagedSplitBounds stagedSplitBounds) {
+        task1 = t1;
+        task2 = t2;
+        mStagedSplitBounds = stagedSplitBounds;
+    }
+
+    public GroupTask(@NonNull GroupTask group) {
+        task1 = new Task(group.task1);
+        task2 = group.task2 != null
+                ? new Task(group.task2)
+                : null;
+        mStagedSplitBounds = group.mStagedSplitBounds;
+    }
+
+    public boolean containsTask(int taskId) {
+        return task1.key.id == taskId || (task2 != null && task2.key.id == taskId);
+    }
+
+    public boolean hasMultipleTasks() {
+        return task2 != null;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
index 2e5b33a..91b53c7 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;
@@ -64,7 +71,16 @@
 
     private boolean onInputConsumerEvent(InputEvent ev) {
         if (ev instanceof MotionEvent) {
-            onInputConsumerMotionEvent((MotionEvent) ev);
+            MotionEvent event = (MotionEvent) ev;
+            int action = event.getActionMasked();
+            boolean isHoverEvent = action == MotionEvent.ACTION_HOVER_ENTER
+                    || action == MotionEvent.ACTION_HOVER_MOVE
+                    || action == MotionEvent.ACTION_HOVER_EXIT;
+            if (isHoverEvent) {
+                onInputConsumerHoverEvent(event);
+            } else {
+                onInputConsumerMotionEvent(event);
+            }
         } else if (ev instanceof KeyEvent) {
             initInputConsumerIfNeeded();
             mInputConsumer.onKeyEvent((KeyEvent) ev);
@@ -98,12 +114,23 @@
             }
         }
         if (mInputConsumer != null) {
+            SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev,
+                    mRotationSupplier.get());
             mInputConsumer.onMotionEvent(ev);
         }
 
         return true;
     }
 
+    private void onInputConsumerHoverEvent(MotionEvent ev) {
+        initInputConsumerIfNeeded();
+        if (mInputConsumer != null) {
+            SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev,
+                    mRotationSupplier.get());
+            mInputConsumer.onHoverEvent(ev);
+        }
+    }
+
     public void destroy() {
         if (mTouchInProgress) {
             mDestroyPending = true;
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..99efb39
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
@@ -0,0 +1,163 @@
+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;
+        }
+
+        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..6b6bd6a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -0,0 +1,123 @@
+/*
+ * 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.WindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.core.view.OneShotPreDrawListener;
+
+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.NaturalRotationUnfoldProgressProvider;
+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;
+    private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider;
+
+    public LauncherUnfoldAnimationController(
+            Launcher launcher,
+            WindowManager windowManager,
+            UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) {
+        mLauncher = launcher;
+        mProgressProvider = new ScopedUnfoldTransitionProgressProvider(
+                unfoldTransitionProgressProvider);
+        mNaturalOrientationProgressProvider = new NaturalRotationUnfoldProgressProvider(launcher,
+                WindowManagerGlobal.getWindowManagerService(), mProgressProvider);
+        mNaturalOrientationProgressProvider.init();
+
+        // Animated in all orientations
+        mProgressProvider.addCallback(new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
+                windowManager));
+
+        // Animated only in natural orientation
+        mNaturalOrientationProgressProvider
+                .addCallback(new QsbAnimationListener());
+        mNaturalOrientationProgressProvider
+                .addCallback(new UnfoldMoveFromCenterHotseatAnimator(launcher, windowManager));
+    }
+
+    /**
+     * Called when launcher is resumed
+     */
+    public void onResume() {
+        Hotseat hotseat = mLauncher.getHotseat();
+        if (hotseat != null && hotseat.getQsb() instanceof HorizontalInsettableView) {
+            mQsbInsettable = (HorizontalInsettableView) hotseat.getQsb();
+        }
+
+        OneShotPreDrawListener.add(mLauncher.getWorkspace(),
+                () -> mProgressProvider.setReadyToHandleTransition(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();
+        mNaturalOrientationProgressProvider.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..8ccab71 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -162,7 +162,7 @@
      */
     public void setDeviceProfile(DeviceProfile deviceProfile) {
         boolean oldMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
-        setFlag(FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY, !deviceProfile.allowRotation);
+        setFlag(FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY, !deviceProfile.isTablet);
         if (mListenersInitialized) {
             boolean newMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
             // If isMultipleOrientationSupportedByDevice is changed, init or destroy listeners
@@ -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..c4909de 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,15 +307,28 @@
                     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);
+                onUpdateListener.onUpdate(mCurrentRect, mCurrentScaleProgress);
             }
         }
     }
@@ -267,7 +353,12 @@
     }
 
     public interface OnUpdateListener {
-        void onUpdate(@Nullable AppCloseConfig values, RectF currentRect, float progress);
+        /**
+         * Called when an update is made to the animation.
+         * @param currentRect The rect of the window.
+         * @param progress [0, 1] The progress of the rect scale animation.
+         */
+        void onUpdate(RectF currentRect, float progress);
 
         default void onCancel() { }
     }
diff --git a/quickstep/src/com/android/quickstep/util/RectFSpringAnim2.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim2.java
deleted file mode 100644
index c331a13..0000000
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim2.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.launcher3.Utilities.dpToPx;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.util.PathParser;
-import android.util.Property;
-import android.view.animation.Interpolator;
-
-import androidx.core.view.animation.PathInterpolatorCompat;
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.DynamicResource;
-import com.android.systemui.plugins.ResourceProvider;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Applies spring forces to animate from a starting rect to a target rect,
- * while providing update callbacks to the caller.
- */
-public class RectFSpringAnim2 extends RectFSpringAnim {
-
-    private static final FloatPropertyCompat<RectFSpringAnim2> RECT_CENTER_X =
-            new FloatPropertyCompat<RectFSpringAnim2>("rectCenterXSpring") {
-                @Override
-                public float getValue(RectFSpringAnim2 anim) {
-                    return anim.mCurrentCenterX;
-                }
-
-                @Override
-                public void setValue(RectFSpringAnim2 anim, float currentCenterX) {
-                    anim.mCurrentCenterX = currentCenterX;
-                    anim.onUpdate();
-                }
-            };
-
-    private static final FloatPropertyCompat<RectFSpringAnim2> RECT_Y =
-            new FloatPropertyCompat<RectFSpringAnim2>("rectYSpring") {
-                @Override
-                public float getValue(RectFSpringAnim2 anim) {
-                    return anim.mCurrentCenterY;
-                }
-
-                @Override
-                public void setValue(RectFSpringAnim2 anim, float y) {
-                    anim.mCurrentCenterY = y;
-                    anim.onUpdate();
-                }
-            };
-
-    private static final Property<RectFSpringAnim2, Float> PROGRESS =
-            new Property<RectFSpringAnim2, Float>(Float.class, "rectFProgress") {
-                @Override
-                public Float get(RectFSpringAnim2 rectFSpringAnim) {
-                    return rectFSpringAnim.mProgress;
-                }
-
-                @Override
-                public void set(RectFSpringAnim2 rectFSpringAnim, Float progress) {
-                    rectFSpringAnim.mProgress = progress;
-                    rectFSpringAnim.onUpdate();
-                }
-            };
-
-    private final RectF mStartRect;
-    private final RectF mTargetRect;
-    private final RectF mCurrentRect = new RectF();
-    private final List<OnUpdateListener> mOnUpdateListeners = new ArrayList<>();
-    private final List<Animator.AnimatorListener> mAnimatorListeners = new ArrayList<>();
-
-    private float mCurrentCenterX;
-    private float mCurrentCenterY;
-
-    private float mTargetX;
-    private float mTargetY;
-
-    // If true, tracking the bottom of the rects, else tracking the top.
-    private float mProgress;
-    private SpringAnimation mRectXAnim;
-    private SpringAnimation mRectYAnim;
-    private ValueAnimator mRectScaleAnim;
-    private boolean mAnimsStarted;
-    private boolean mRectXAnimEnded;
-    private boolean mRectYAnimEnded;
-    private boolean mRectScaleAnimEnded;
-
-    private final float mXDamping;
-    private final float mXStiffness;
-
-    private final float mYDamping;
-    private float mYStiffness;
-
-    private long mDuration;
-
-    private final Interpolator mCloseInterpolator;
-
-    private AppCloseConfig mValues;
-    final float mStartRadius;
-    final float mEndRadius;
-
-    final float mHomeTransYEnd;
-    final float mScaleStart;
-
-    public RectFSpringAnim2(RectF startRect, RectF targetRect, Context context, float startRadius,
-            float endRadius) {
-        super(startRect, targetRect, context);
-        mStartRect = startRect;
-        mTargetRect = targetRect;
-
-        mCurrentCenterY = mStartRect.centerY();
-        mCurrentCenterX = mStartRect.centerX();
-
-        mTargetY = mTargetRect.centerY();
-        mTargetX = mTargetRect.centerX();
-
-        ResourceProvider rp = DynamicResource.provider(context);
-        mXDamping = rp.getFloat(R.dimen.swipe_up_rect_2_x_damping_ratio);
-        mXStiffness = rp.getFloat(R.dimen.swipe_up_rect_2_x_stiffness);
-
-        mYDamping = rp.getFloat(R.dimen.swipe_up_rect_2_y_damping_ratio);
-        mYStiffness = rp.getFloat(R.dimen.swipe_up_rect_2_y_stiffness);
-        mDuration = Math.round(rp.getFloat(R.dimen.swipe_up_duration));
-
-        mHomeTransYEnd = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
-        mScaleStart = rp.getFloat(R.dimen.swipe_up_scale_start);
-
-        mCloseInterpolator = getAppCloseInterpolator(context);
-
-        // 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 = startRadius;
-        mEndRadius = endRadius;
-
-        setCanRelease(true);
-    }
-
-    public void onTargetPositionChanged() {
-        if (mRectXAnim != null && mTargetX != mTargetRect.centerX()) {
-            mTargetX = mTargetRect.centerX();
-            mRectXAnim.animateToFinalPosition(mTargetX);
-        }
-
-        if (mRectYAnim != null) {
-            if (mTargetY != mTargetRect.centerY()) {
-                mTargetY = mTargetRect.centerY();
-                mRectYAnim.animateToFinalPosition(mTargetY);
-            }
-        }
-    }
-
-    public void addOnUpdateListener(OnUpdateListener onUpdateListener) {
-        mOnUpdateListeners.add(onUpdateListener);
-    }
-
-    public void addAnimatorListener(Animator.AnimatorListener animatorListener) {
-        mAnimatorListeners.add(animatorListener);
-    }
-
-    /**
-     * Starts the fling/spring animation.
-     * @param context The activity context.
-     * @param velocityPxPerMs Velocity of swipe in px/ms.
-     */
-    public void start(Context context, PointF velocityPxPerMs) {
-        mRectXAnim = new SpringAnimation(this, RECT_CENTER_X)
-                .setStartValue(mCurrentCenterX)
-                .setStartVelocity(velocityPxPerMs.x * 1000)
-                .setSpring(new SpringForce(mTargetX)
-                        .setStiffness(mXStiffness)
-                        .setDampingRatio(mXDamping));
-        mRectXAnim.addEndListener(((animation, canceled, centerX, velocityX) -> {
-            mRectXAnimEnded = true;
-            maybeOnEnd();
-        }));
-
-        mRectYAnim = new SpringAnimation(this, RECT_Y)
-                .setStartValue(mCurrentCenterY)
-                .setStartVelocity(velocityPxPerMs.y * 1000)
-                .setSpring(new SpringForce(mTargetY)
-                        .setStiffness(mYStiffness)
-                        .setDampingRatio(mYDamping));
-        mRectYAnim.addEndListener(((animation, canceled, centerY, velocityY) -> {
-            mRectYAnimEnded = true;
-            maybeOnEnd();
-        }));
-
-        mRectScaleAnim = ObjectAnimator.ofFloat(this, PROGRESS, 0, 1f)
-                .setDuration(mDuration);
-        mRectScaleAnim.setInterpolator(mCloseInterpolator);
-        mRectScaleAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mRectScaleAnimEnded = true;
-                maybeOnEnd();
-            }
-        });
-
-        mValues = buildConfig();
-        mRectScaleAnim.addUpdateListener(mValues);
-
-        setCanRelease(false);
-        mAnimsStarted = true;
-
-        mRectXAnim.start();
-        mRectYAnim.start();
-        mRectScaleAnim.start();
-        for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
-            animatorListener.onAnimationStart(null);
-        }
-    }
-
-    private AppCloseConfig buildConfig() {
-        return new AppCloseConfig() {
-            FloatProp mHomeTransY = new FloatProp(0, mHomeTransYEnd, 0, mDuration, LINEAR);
-            FloatProp mHomeScale = new FloatProp(mScaleStart, 1f, 0, mDuration, LINEAR);
-            FloatProp mWindowFadeOut = new FloatProp(1f, 0f, 0, 116, LINEAR);
-            // There should be a slight overlap b/w window fading out and fg fading in.
-            // (fg startDelay < window fade out duration)
-            FloatProp mFgFadeIn = new FloatProp(0, 255f, 100, mDuration - 100, LINEAR);
-            FloatProp mRadius = new FloatProp(mStartRadius, mEndRadius, 0, mDuration, LINEAR);
-            FloatProp mThreePointInterpolation = new FloatProp(0, 1, 0, mDuration, LINEAR);
-
-            @Override
-            public float getWorkspaceTransY() {
-                return mHomeTransY.value;
-            }
-
-            @Override
-            public float getWorkspaceScale() {
-                return mHomeScale.value;
-            }
-
-            @Override
-            public float getWindowAlpha() {
-                return mWindowFadeOut.value;
-            }
-
-            @Override
-            public int getFgAlpha() {
-                return (int) mFgFadeIn.value;
-            }
-
-            @Override
-            public float getCornerRadius() {
-                return mRadius.value;
-            }
-
-            @Override
-            public float getInterpolatedProgress() {
-                return mThreePointInterpolation.value;
-            }
-
-            @Override
-            public void onUpdate(float percent, boolean initOnly) {}
-        };
-    }
-
-    public void end() {
-        if (mAnimsStarted) {
-            if (mRectXAnim.canSkipToEnd()) {
-                mRectXAnim.skipToEnd();
-            }
-            if (mRectYAnim.canSkipToEnd()) {
-                mRectYAnim.skipToEnd();
-            }
-            mRectScaleAnim.end();
-        }
-        mRectXAnimEnded = true;
-        mRectYAnimEnded = true;
-        mRectScaleAnimEnded = true;
-        maybeOnEnd();
-    }
-
-    private boolean isEnded() {
-        return mRectXAnimEnded && mRectYAnimEnded && mRectScaleAnimEnded;
-    }
-
-    private void onUpdate() {
-        if (isEnded()) {
-            // Prevent further updates from being called. This can happen between callbacks for
-            // ending the x/y/scale animations.
-            return;
-        }
-
-        if (!mOnUpdateListeners.isEmpty()) {
-            float rectProgress = mProgress;
-            float currentWidth = Utilities.mapRange(rectProgress, mStartRect.width(),
-                    mTargetRect.width());
-            float currentHeight = Utilities.mapRange(rectProgress, mStartRect.height(),
-                    mTargetRect.height());
-
-            mCurrentRect.set(mCurrentCenterX - currentWidth / 2,
-                    mCurrentCenterY - currentHeight / 2,
-                    mCurrentCenterX + currentWidth / 2,
-                    mCurrentCenterY + currentHeight / 2);
-
-            float currentPlayTime = mRectScaleAnimEnded ? mRectScaleAnim.getDuration()
-                    : mRectScaleAnim.getCurrentPlayTime();
-            float linearProgress = Math.min(1f, currentPlayTime / mRectScaleAnim.getDuration());
-            for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
-                onUpdateListener.onUpdate(mValues, mCurrentRect, linearProgress);
-            }
-        }
-    }
-
-    private void maybeOnEnd() {
-        if (mAnimsStarted && isEnded()) {
-            mAnimsStarted = false;
-            setCanRelease(true);
-            for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
-                animatorListener.onAnimationEnd(null);
-            }
-        }
-    }
-
-    public void cancel() {
-        if (mAnimsStarted) {
-            for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
-                onUpdateListener.onCancel();
-            }
-        }
-        end();
-    }
-
-    private Interpolator getAppCloseInterpolator(Context context) {
-        ResourceProvider rp = DynamicResource.provider(context);
-        String path = String.format(Locale.ENGLISH,
-                "M 0,0 C %f, %f, %f, %f, %f, %f C %f, %f, %f, %f, 1, 1",
-                rp.getFloat(R.dimen.c1_a),
-                rp.getFloat(R.dimen.c1_b),
-                rp.getFloat(R.dimen.c1_c),
-                rp.getFloat(R.dimen.c1_d),
-                rp.getFloat(R.dimen.mp_x),
-                rp.getFloat(R.dimen.mp_y),
-                rp.getFloat(R.dimen.c2_a),
-                rp.getFloat(R.dimen.c2_b),
-                rp.getFloat(R.dimen.c2_c),
-                rp.getFloat(R.dimen.c2_d));
-        return PathInterpolatorCompat.create(PathParser.createPathFromPathData(path));
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 98dbd47..ee82ae6 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -16,32 +16,14 @@
 package com.android.quickstep.util;
 
 import android.animation.AnimatorSet;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.os.Handler;
 
-import com.android.launcher3.LauncherAnimationRunner;
-import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 public abstract class RemoteAnimationProvider {
 
-    RemoteAnimationFactory mAnimationRunner;
-
     public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets);
 
-    ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
-        mAnimationRunner = (transit, appTargets, wallpaperTargets, nonApps, result) ->
-                result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
-        final LauncherAnimationRunner wrapper = new LauncherAnimationRunner(
-                handler, mAnimationRunner, false /* startAtFrontOfQueue */);
-        return ActivityOptionsCompat.makeRemoteAnimation(
-                new RemoteAnimationAdapterCompat(wrapper, duration, 0));
-    }
-
     /**
      * @return the target with the lowest opaque layer for a certain app animation, or null.
      */
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index a147b68..5253e8c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -16,158 +16,159 @@
 
 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.DEFAULT_SPLIT_RATIO;
 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.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager;
+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.GroupedTaskView;
 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 final StateManager mStateManager;
+    private final DepthController mDepthController;
+    private @StagePosition int mStagePosition;
+    private Task mInitialTask;
+    private Task mSecondTask;
+    private boolean mRecentsAnimationRunning;
+    /** If not null, this is the TaskView we want to launch from */
+    @Nullable
+    private GroupedTaskView mLaunchingTaskView;
 
-    public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
-        mSystemUiProxy = systemUiProxy;
+    public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy,
+            StateManager stateManager,
+            DepthController depthController) {
         mHandler = handler;
+        mSystemUiProxy = systemUiProxy;
+        mStateManager = stateManager;
+        mDepthController = depthController;
     }
 
     /**
      * To be called after first task selected
      */
-    public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
+    public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
             Rect initialBounds) {
-        mInitialTaskView = taskView;
-        mInitialPosition = positionOption;
-        mInitialBounds = initialBounds;
+        mInitialTask = task;
+        mStagePosition = stagePosition;
     }
 
     /**
      * 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 task, Consumer<Boolean> callback) {
+        mSecondTask = task;
+        launchTasks(mInitialTask, mSecondTask, mStagePosition, callback,
+                false /* freezeTaskList */, DEFAULT_SPLIT_RATIO);
     }
 
     /**
-     * @return {@link InsettableFrameLayout.LayoutParams} to correctly position the
-     * split placeholder view
+     * To be called when we want to launch split pairs from an existing GroupedTaskView.
      */
-    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;
-        } else {
-            params.height = (int) resources.getDimension(R.dimen.split_placeholder_size);
-            params.gravity = Gravity.TOP;
-        }
-
-        return params;
+    public void launchTasks(GroupedTaskView groupedTaskView,
+            Consumer<Boolean> callback, boolean freezeTaskList) {
+        mLaunchingTaskView = groupedTaskView;
+        TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
+                groupedTaskView.getTaskIdAttributeContainers();
+        launchTasks(taskIdAttributeContainers[0].getTask(), taskIdAttributeContainers[1].getTask(),
+                taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList,
+                groupedTaskView.getSplitRatio());
     }
 
-    @Nullable
-    public SplitPositionOption getActiveSplitPositionOption() {
-        return mInitialPosition;
+    /**
+     * @param stagePosition representing location of task1
+     */
+    public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition,
+            Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
+        // 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, splitRatio,
+                    new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
+                            ActivityThread.currentActivityThread().getApplicationThread()));
+        } else {
+            RemoteSplitLaunchAnimationRunner animationRunner =
+                    new RemoteSplitLaunchAnimationRunner(task1, task2, callback);
+            final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+                    RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
+                    300, 150,
+                    ActivityThread.currentActivityThread().getApplicationThread());
+
+            ActivityOptions mainOpts = ActivityOptions.makeBasic();
+            if (freezeTaskList) {
+                mainOpts.setFreezeRecentTasksReordering();
+            }
+            mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
+                    taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
+                    splitRatio, adapter);
+        }
+    }
+
+    public @StagePosition int getActiveSplitStagePosition() {
+        return mStagePosition;
+    }
+
+    public void setRecentsAnimationRunning(boolean running) {
+        this.mRecentsAnimationRunning = running;
     }
 
     /**
      * 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,62 +176,66 @@
 
     /**
      * 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(
+                            mLaunchingTaskView, mInitialTask, mSecondTask, apps, wallpapers,
+                            nonApps, mStateManager, mDepthController, () -> {
+                                finishedCallback.run();
+                                if (mSuccessCallback != null) {
+                                    mSuccessCallback.accept(true);
+                                }
+                                resetState();
+                            }));
+        }
+
+        @Override
+        public void onAnimationCancelled() {
+            postAsyncCallback(mHandler, () -> {
+                if (mSuccessCallback != null) {
+                    // Launching legacy tasks while recents animation is running will always cause
+                    // onAnimationCancelled to be called (should be fixed w/ shell transitions?)
+                    mSuccessCallback.accept(mRecentsAnimationRunning);
+                }
+                resetState();
+            });
         }
     }
 
-
     /**
      * To be called if split select was cancelled
      */
     public void resetState() {
-        mInitialTaskView = null;
-        mInitialPosition = null;
-        mInitialBounds = null;
+        mInitialTask = null;
+        mSecondTask = null;
+        mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
+        mRecentsAnimationRunning = false;
+        mLaunchingTaskView = 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;
-    }
-
-    public Rect getInitialBounds() {
-        return mInitialBounds;
+        return mInitialTask != null && mSecondTask == null;
     }
 }
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/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 3b4fd31..3b1c150 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -22,10 +22,10 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.view.ViewRootImpl;
 
 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.ViewRootImplCompat;
 
 import java.util.function.Consumer;
 
@@ -41,7 +41,7 @@
     private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
 
     private final SurfaceControl mBarrierSurfaceControl;
-    private final ViewRootImplCompat mTargetViewRootImpl;
+    private final ViewRootImpl mTargetViewRootImpl;
     private final Handler mApplyHandler;
 
     private int mLastSequenceNumber = 0;
@@ -50,8 +50,8 @@
      * @param targetView The view in the surface that acts as synchronization anchor.
      */
     public SurfaceTransactionApplier(View targetView) {
-        mTargetViewRootImpl = new ViewRootImplCompat(targetView);
-        mBarrierSurfaceControl = mTargetViewRootImpl.getRenderSurfaceControl();
+        mTargetViewRootImpl = targetView.getViewRootImpl();
+        mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl();
         mApplyHandler = new Handler(this::onApplyMessage);
     }
 
@@ -109,7 +109,7 @@
         if (targetView == null) {
             // No target view, no applier
             callback.accept(null);
-        } else if (new ViewRootImplCompat(targetView).isValid()) {
+        } else if (targetView.isAttachedToWindow()) {
             // Already attached, we're good to go
             callback.accept(new SurfaceTransactionApplier(targetView));
         } else {
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index c0f5c14..a534450 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;
@@ -157,7 +158,7 @@
             t.reparent(mContentOverlay, mLeash);
             t.apply();
 
-            addOnUpdateListener((values, currentRect, progress) -> {
+            addOnUpdateListener((currentRect, progress) -> {
                 float alpha = progress < 0.5f
                         ? 0
                         : Utilities.mapToRange(Math.min(progress, 1f), 0.5f, 1f,
@@ -200,8 +201,7 @@
         addOnUpdateListener(this::onAnimationUpdate);
     }
 
-    private void onAnimationUpdate(@Nullable AppCloseConfig values, RectF currentRect,
-            float progress) {
+    private void onAnimationUpdate(RectF currentRect, float progress) {
         if (mHasAnimationEnded) return;
         final SurfaceControl.Transaction tx =
                 PipSurfaceTransactionHelper.newSurfaceControlTransaction();
@@ -278,19 +278,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..7b122c6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -0,0 +1,145 @@
+/*
+ * 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.ArrayList;
+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 final ArrayList<Runnable> mPendingConnectedCallbacks = new ArrayList<>();
+
+    private short mConnectionAttempts;
+    private boolean mTisServiceBound;
+    private boolean mIsConnected;
+
+    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");
+        mIsConnected = true;
+        mConnectionCallback.accept((TISBinder) iBinder);
+        // Flush the pending callbacks
+        for (Runnable r : mPendingConnectedCallbacks) {
+            r.run();
+        }
+        mPendingConnectedCallbacks.clear();
+        resetServiceBindRetryState();
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName componentName) { }
+
+    @Override
+    public void onBindingDied(ComponentName name) {
+        Log.w(TAG, "TIS binding died");
+        internalBindToTIS();
+    }
+
+    /**
+     * Runs the given {@param r} runnable when the service is connected.
+     */
+    public void runOnBindToTouchInteractionService(Runnable r) {
+        if (mIsConnected) {
+            r.run();
+        } else {
+            mPendingConnectedCallbacks.add(r);
+        }
+    }
+
+    /**
+     * 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();
+        mIsConnected = false;
+        mPendingConnectedCallbacks.clear();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java b/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
index d87feec..08a65fa 100644
--- a/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
+++ b/quickstep/src/com/android/quickstep/util/TaskKeyLruCache.java
@@ -20,6 +20,7 @@
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 
 import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.function.Predicate;
 
 /**
@@ -117,7 +118,7 @@
         }
 
         @Override
-        protected boolean removeEldestEntry(Entry<Integer, TaskKeyLruCache.Entry<V>> eldest) {
+        protected boolean removeEldestEntry(Map.Entry<Integer, TaskKeyLruCache.Entry<V>> eldest) {
             return size() > mMaxSize;
         }
     }
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index cceb872..f676091 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -18,6 +18,10 @@
 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.quickstep.util.RecentsOrientedState.postDisplayRotation;
 import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
@@ -26,16 +30,17 @@
 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;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
@@ -50,26 +55,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 +99,15 @@
     // Cached calculations
     private boolean mLayoutValid = false;
     private int mOrientationStateId;
+    private StagedSplitBounds mStagedSplitBounds;
+    private boolean mDrawsBelowRecents;
+    private boolean mIsGridTask;
+    private int mTaskRectTranslationX;
+    private int mTaskRectTranslationY;
 
     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 +143,27 @@
         if (mDp == null) {
             return 1;
         }
-        mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect,
-                mOrientationState.getOrientationHandler());
-        return mOrientationState.getFullScreenScaleAndPivot(mTaskRect, mDp, mPivot);
+        if (mIsGridTask) {
+            mSizeStrategy.calculateGridTaskSize(mContext, mDp, mTaskRect,
+                    mOrientationState.getOrientationHandler());
+        } else {
+            mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect);
+        }
+
+        Rect 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
+            fullTaskSize = new Rect(mTaskRect);
+            mOrientationState.getOrientationHandler()
+                    .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition);
+            mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
+        } else {
+            fullTaskSize = mTaskRect;
+        }
+        fullTaskSize.offset(mTaskRectTranslationX, mTaskRectTranslationY);
+        return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot);
     }
 
     /**
@@ -147,8 +171,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;
     }
 
     /**
@@ -175,6 +215,21 @@
     }
 
     /**
+     * Sets whether the task is part of overview grid and not being focused.
+     */
+    public void setIsGridTask(boolean isGridTask) {
+        mIsGridTask = isGridTask;
+    }
+
+    /**
+     * Apply translations on TaskRect's starting location.
+     */
+    public void setTaskRectTranslation(int taskRectTranslationX, int taskRectTranslationY) {
+        mTaskRectTranslationX = taskRectTranslationX;
+        mTaskRectTranslationY = taskRectTranslationY;
+    }
+
+    /**
      * Adds animation for all the components corresponding to transition from an app to overview.
      */
     public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
@@ -230,12 +285,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 +313,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;
@@ -276,20 +332,20 @@
         mMatrix.postTranslate(insets.left, insets.top);
         mMatrix.postScale(scale, scale);
 
-        // Apply TaskView matrix: translate, scroll
+        // Apply TaskView matrix: taskRect, translate
         mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
-        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+        mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
                 taskPrimaryTranslation.value);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
                 taskSecondaryTranslation.value);
-        mOrientationState.getOrientationHandler().set(
+        mOrientationState.getOrientationHandler().setPrimary(
                 mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
 
         // Apply RecentsView matrix
         mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
                 recentsViewSecondaryTranslation.value);
-        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+        mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
                 recentsViewPrimaryTranslation.value);
         applyWindowToHomeRotation(mMatrix);
 
@@ -300,6 +356,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
@@ -309,8 +383,7 @@
                 .withWindowCrop(mTmpCropRect)
                 .withCornerRadius(getCurrentCornerRadius());
 
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mIsForLiveTile
-                && params.getRecentsSurface() != null) {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && 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/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
new file mode 100644
index 0000000..dc97dd6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.Hotseat;
+import com.android.launcher3.Launcher;
+
+/**
+ * Animation that moves hotseat icons from center to the sides (final position)
+ */
+public class UnfoldMoveFromCenterHotseatAnimator extends BaseUnfoldMoveFromCenterAnimator {
+
+    private final Launcher mLauncher;
+
+    public UnfoldMoveFromCenterHotseatAnimator(Launcher launcher, WindowManager windowManager) {
+        super(windowManager);
+        mLauncher = launcher;
+    }
+
+    @Override
+    protected void onPrepareViewsForAnimation() {
+        Hotseat hotseat = mLauncher.getHotseat();
+
+        ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
+        disableClipping(hotseat);
+
+        for (int i = 0; i < hotseatIcons.getChildCount(); i++) {
+            View child = hotseatIcons.getChildAt(i);
+            registerViewForAnimation(child);
+        }
+    }
+
+    @Override
+    public void onTransitionFinished() {
+        restoreClipping(mLauncher.getHotseat());
+        super.onTransitionFinished();
+    }
+}
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..3d72398
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.WindowManager;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Workspace;
+
+/**
+ * Animation that moves launcher icons and widgets from center to the sides (final position)
+ */
+public class UnfoldMoveFromCenterWorkspaceAnimator extends BaseUnfoldMoveFromCenterAnimator {
+
+    private final Launcher mLauncher;
+
+    public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) {
+        super(windowManager);
+        mLauncher = launcher;
+    }
+
+    @Override
+    protected void onPrepareViewsForAnimation() {
+        Workspace workspace = mLauncher.getWorkspace();
+
+        // 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);
+                        registerViewForAnimation(child);
+                    }
+                });
+
+        disableClipping(workspace);
+    }
+
+    @Override
+    public void onTransitionFinished() {
+        restoreClipping(mLauncher.getWorkspace());
+        mLauncher.getWorkspace().forEachVisiblePage(page -> restoreClipping((CellLayout) page));
+        super.onTransitionFinished();
+    }
+}
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/AllAppsEduView.java b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
index e9d7c3c..d79b318 100644
--- a/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
+++ b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
@@ -35,6 +35,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -58,6 +59,7 @@
     private Launcher mLauncher;
     private AllAppsEduTouchController mTouchController;
 
+    @Nullable
     private AnimatorSet mAnimation;
 
     private GradientDrawable mCircle;
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index b9a9006..4f2ed4c 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -21,6 +21,7 @@
 import android.util.FloatProperty;
 import android.widget.Button;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.touch.PagedOrientationHandler;
 
@@ -39,10 +40,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 +67,7 @@
     private float mGridTranslationPrimary;
     private float mGridScrollOffset;
     private float mScrollOffsetPrimary;
+    private float mSplitSelectScrollOffsetPrimary;
 
     private int mSidePadding;
 
@@ -97,6 +113,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 +146,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 +169,10 @@
         mScrollOffsetPrimary = scrollOffsetPrimary;
     }
 
+    public void setSplitSelectScrollOffsetPrimary(float splitSelectScrollOffsetPrimary) {
+        mSplitSelectScrollOffsetPrimary = splitSelectScrollOffsetPrimary;
+    }
+
     public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
         float scrollAdjustment = 0;
         if (fullscreenEnabled) {
@@ -155,6 +182,7 @@
             scrollAdjustment += mGridTranslationPrimary + mGridScrollOffset;
         }
         scrollAdjustment += mScrollOffsetPrimary;
+        scrollAdjustment += mSplitSelectScrollOffsetPrimary;
         return scrollAdjustment;
     }
 
@@ -218,6 +246,9 @@
      * Get the Y translation that is set in the original layout position, before scrolling.
      */
     private float getOriginalTranslationY() {
-        return mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx / 2.0f;
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        return deviceProfile.overviewShowAsGrid
+                ? deviceProfile.overviewRowSpacing
+                : deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 7c8041c..e5664c6 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -19,6 +19,7 @@
 import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
 import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.CENTER_HORIZONTAL;
+import static android.view.Gravity.START;
 
 import static com.android.launcher3.Utilities.prefixTextWithIcon;
 import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
@@ -38,25 +39,51 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import com.android.systemui.shared.recents.model.Task;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.time.Duration;
 import java.util.Locale;
 
 @TargetApi(Build.VERSION_CODES.Q)
 public final class DigitalWellBeingToast {
+
+    private static final float THRESHOLD_LEFT_ICON_ONLY = 0.4f;
+    private static final float THRESHOLD_RIGHT_ICON_ONLY = 0.6f;
+
+    /** Will span entire width of taskView with full text */
+    private static final int SPLIT_BANNER_FULLSCREEN = 0;
+    /** Used for grid task view, only showing icon and time */
+    private static final int SPLIT_GRID_BANNER_LARGE = 1;
+    /** Used for grid task view, only showing icon */
+    private static final int SPLIT_GRID_BANNER_SMALL = 2;
+    @IntDef(value = {
+            SPLIT_BANNER_FULLSCREEN,
+            SPLIT_GRID_BANNER_LARGE,
+            SPLIT_GRID_BANNER_SMALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SPLIT_BANNER_CONFIG{}
+
     static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
     static final int MINUTE_MS = 60000;
 
@@ -69,10 +96,20 @@
     private Task mTask;
     private boolean mHasLimit;
     private long mAppRemainingTimeMs;
+    @Nullable
     private View mBanner;
     private ViewOutlineProvider mOldBannerOutlineProvider;
     private float mBannerOffsetPercentage;
-    private float mVerticalOffset = 0f;
+    /**
+     * Clips rect provided by {@link #mOldBannerOutlineProvider} when in the model state to
+     * hide this banner as the taskView scales up and down
+     */
+    private float mModalOffset = 0f;
+    @Nullable
+    private StagedSplitBounds mStagedSplitBounds;
+    private int mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
+    private float mSplitOffsetTranslationY;
+    private float mSplitOffsetTranslationX;
 
     public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) {
         mActivity = activity;
@@ -101,7 +138,7 @@
     }
 
     public String getText() {
-        return getText(mAppRemainingTimeMs);
+        return getText(mAppRemainingTimeMs, false /* forContentDesc */);
     }
 
     public boolean hasLimit() {
@@ -136,6 +173,31 @@
         });
     }
 
+    public void setSplitConfiguration(StagedSplitBounds stagedSplitBounds) {
+        mStagedSplitBounds = stagedSplitBounds;
+        if (mStagedSplitBounds == null ||
+                !mActivity.getDeviceProfile().overviewShowAsGrid ||
+                mTaskView.isFocusedTask()) {
+            mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
+            return;
+        }
+
+        // For portrait grid only height of task changes, not width. So we keep the text the same
+        if (!mActivity.getDeviceProfile().isLandscape) {
+            mSplitBannerConfig = SPLIT_GRID_BANNER_LARGE;
+            return;
+        }
+
+        // For landscape grid, for 30% width we only show icon, otherwise show icon and time
+        if (mTask.key.id == mStagedSplitBounds.leftTopTaskId) {
+            mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ?
+                    SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
+        } else {
+            mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ?
+                    SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
+        }
+    }
+
     private String getReadableDuration(
             Duration duration,
             FormatWidth formatWidthHourAndMinute,
@@ -179,30 +241,33 @@
                 .formatMeasures(new Measure(0, MeasureUnit.MINUTE));
     }
 
-    private String getReadableDuration(
-            Duration duration,
-            FormatWidth formatWidthHourAndMinute,
-            @StringRes int durationLessThanOneMinuteStringId) {
-        return getReadableDuration(
-                duration,
-                formatWidthHourAndMinute,
-                durationLessThanOneMinuteStringId,
-                /* forceFormatWidth= */ false);
-    }
-
-    private String getRoundedUpToMinuteReadableDuration(long remainingTime) {
+    /**
+     * Returns text to show for the banner depending on {@link #mSplitBannerConfig}
+     * If {@param forContentDesc} is {@code true}, this will always return the full
+     * string corresponding to {@link #SPLIT_BANNER_FULLSCREEN}
+     */
+    private String getText(long remainingTime, boolean forContentDesc) {
         final Duration duration = Duration.ofMillis(
                 remainingTime > MINUTE_MS ?
                         (remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
                         remainingTime);
-        return getReadableDuration(
-                duration, FormatWidth.NARROW, R.string.shorter_duration_less_than_one_minute);
-    }
+        String readableDuration = getReadableDuration(duration,
+                FormatWidth.NARROW,
+                R.string.shorter_duration_less_than_one_minute,
+                false /* forceFormatWidth */);
+        if (forContentDesc || mSplitBannerConfig == SPLIT_BANNER_FULLSCREEN) {
+            return mActivity.getString(
+                    R.string.time_left_for_app,
+                    readableDuration);
+        }
 
-    private String getText(long remainingTime) {
-        return mActivity.getString(
-                R.string.time_left_for_app,
-                getRoundedUpToMinuteReadableDuration(remainingTime));
+        if (mSplitBannerConfig == SPLIT_GRID_BANNER_SMALL) {
+            // show no text
+            return "";
+        } else { // SPLIT_GRID_BANNER_LARGE
+            // only show time
+            return readableDuration;
+        }
     }
 
     public void openAppUsageSettings(View view) {
@@ -230,11 +295,11 @@
                 mActivity.getString(
                         R.string.task_contents_description_with_remaining_time,
                         task.titleDescription,
-                        getText(appRemainingTimeMs)) :
+                        getText(appRemainingTimeMs, true /* forContentDesc */)) :
                 task.titleDescription;
     }
 
-    private void replaceBanner(View view) {
+    private void replaceBanner(@Nullable View view) {
         resetOldBanner();
         setBanner(view);
     }
@@ -248,7 +313,7 @@
         }
     }
 
-    private void setBanner(View view) {
+    private void setBanner(@Nullable View view) {
         mBanner = view;
         if (view != null) {
             setupAndAddBanner();
@@ -259,10 +324,18 @@
     private void setupAndAddBanner() {
         FrameLayout.LayoutParams layoutParams =
                 (FrameLayout.LayoutParams) mBanner.getLayoutParams();
-        layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
         layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams)
                 mTaskView.getThumbnail().getLayoutParams()).bottomMargin;
-        mBanner.setTranslationY(mBannerOffsetPercentage * mBanner.getHeight());
+        PagedOrientationHandler orientationHandler = mTaskView.getPagedOrientationHandler();
+        Pair<Float, Float> translations = orientationHandler
+                .setDwbLayoutParamsAndGetTranslations(mTaskView.getMeasuredWidth(),
+                        mTaskView.getMeasuredHeight(), mStagedSplitBounds, deviceProfile,
+                        mTaskView.getThumbnails(), mTask.key.id, mBanner);
+        mSplitOffsetTranslationX = translations.first;
+        mSplitOffsetTranslationY = translations.second;
+        updateTranslationY();
+        updateTranslationX();
         mTaskView.addView(mBanner);
     }
 
@@ -272,7 +345,9 @@
             @Override
             public void getOutline(View view, Outline outline) {
                 mOldBannerOutlineProvider.getOutline(view, outline);
-                outline.offset(0, Math.round(-view.getTranslationY() + mVerticalOffset));
+                float verticalTranslation = -view.getTranslationY() + mModalOffset
+                        + mSplitOffsetTranslationY;
+                outline.offset(0, Math.round(verticalTranslation));
             }
         });
         mBanner.setClipToOutline(true);
@@ -280,13 +355,33 @@
 
     void updateBannerOffset(float offsetPercentage, float verticalOffset) {
         if (mBanner != null && mBannerOffsetPercentage != offsetPercentage) {
-            mVerticalOffset = verticalOffset;
+            mModalOffset = verticalOffset;
             mBannerOffsetPercentage = offsetPercentage;
-            mBanner.setTranslationY(offsetPercentage * mBanner.getHeight() + mVerticalOffset);
+            updateTranslationY();
             mBanner.invalidateOutline();
         }
     }
 
+    private void updateTranslationY() {
+        if (mBanner == null) {
+            return;
+        }
+
+        mBanner.setTranslationY(
+                (mBannerOffsetPercentage * mBanner.getHeight()) +
+                        mModalOffset +
+                        mSplitOffsetTranslationY
+        );
+    }
+
+    private void updateTranslationX() {
+        if (mBanner == null) {
+            return;
+        }
+
+        mBanner.setTranslationX(mSplitOffsetTranslationX);
+    }
+
     void setBannerColorTint(int color, float amount) {
         if (mBanner == null) {
             return;
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..18ab3bb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -0,0 +1,251 @@
+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 androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.InsettableFrameLayout;
+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 BaseDraggingActivity mActivity;
+    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, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FloatingTaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mActivity = BaseActivity.fromContext(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);
+    }
+
+    private void init(StatefulActivity launcher, TaskView originalView, RectF positionOut) {
+        mStartingPosition = positionOut;
+        updateInitialPositionForView(originalView);
+        final InsettableFrameLayout.LayoutParams lp =
+                (InsettableFrameLayout.LayoutParams) getLayoutParams();
+
+        mSplitPlaceholderView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
+        positionOut.round(mOutline);
+        setPivotX(0);
+        setPivotY(0);
+
+        // Copy bounds of exiting thumbnail into ImageView
+        TaskThumbnailView thumbnail = originalView.getThumbnail();
+        mImageView.setImageBitmap(thumbnail.getThumbnail());
+        mImageView.setVisibility(VISIBLE);
+
+        mOrientationHandler = originalView.getRecentsView().getPagedOrientationHandler();
+        mSplitPlaceholderView.setIconView(originalView.getIconView(),
+                launcher.getDeviceProfile().overviewTaskIconDrawableSizePx);
+        mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
+    }
+
+    /**
+     * Configures and returns a an instance of {@link FloatingTaskView} initially matching the
+     * appearance of {@code originalView}.
+     */
+    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.init(launcher, originalView, positionOut);
+        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(mActivity.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 = position.left - mStartingPosition.left;
+        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);
+    }
+
+    public void updateOrientationHandler(PagedOrientationHandler orientationHandler) {
+        mOrientationHandler = orientationHandler;
+        mSplitPlaceholderView.getIconView().setRotation(mOrientationHandler.getDegreesRotated());
+    }
+
+    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(mActivity.getDeviceProfile().widthPx - Math.round(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 = (int) pos.left;
+        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 = mActivity.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/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
index 1548268..c3b166f 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
@@ -27,6 +27,8 @@
 import android.view.ViewOutlineProvider;
 import android.widget.RemoteViews.RemoteViewOutlineProvider;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.RoundedCornerEnforcement;
 
@@ -42,7 +44,9 @@
     private final DrawableProperties mForegroundProperties = new DrawableProperties();
     private final DrawableProperties mBackgroundProperties = new DrawableProperties();
 
+    @Nullable
     private Drawable mOriginalForeground;
+    @Nullable
     private Drawable mOriginalBackground;
     private float mFinalRadius;
     private float mInitialOutlineRadius;
@@ -50,7 +54,7 @@
     private boolean mIsUsingFallback;
     private View mSourceView;
 
-    FloatingWidgetBackgroundView(Context context, AttributeSet attrs, int defStyleAttr) {
+    FloatingWidgetBackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setOutlineProvider(new ViewOutlineProvider() {
             @Override
@@ -161,8 +165,10 @@
 
     /** Stores and modifies a drawable's properties through an animation. */
     private static class DrawableProperties {
+        @Nullable
         private Drawable mDrawable;
         private float mOriginalRadius;
+        @Nullable
         private float[] mOriginalRadii;
         private final float[] mTmpRadii = new float[8];
 
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index 88b11a0..8a5f42a 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -30,6 +30,8 @@
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.FrameLayout;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -55,10 +57,14 @@
     private LauncherAppWidgetHostView mAppWidgetView;
     private View mAppWidgetBackgroundView;
     private RectF mBackgroundPosition;
+    @Nullable
     private GhostView mForegroundOverlayView;
 
+    @Nullable
     private Runnable mEndRunnable;
+    @Nullable
     private Runnable mFastFinishRunnable;
+    @Nullable
     private Runnable mOnTargetChangeRunnable;
     private boolean mAppTargetIsTranslucent;
 
@@ -68,11 +74,11 @@
         this(context, null);
     }
 
-    public FloatingWidgetView(Context context, AttributeSet attrs) {
+    public FloatingWidgetView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public FloatingWidgetView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public FloatingWidgetView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mLauncher = Launcher.getLauncher(context);
         mListenerView = new ListenerView(context, attrs);
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..7e4f9d0
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -0,0 +1,285 @@
+package com.android.quickstep.views;
+
+import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
+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.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.HashMap;
+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 {
+
+    @Nullable
+    private Task mSecondaryTask;
+    private TaskThumbnailView mSnapshotView2;
+    private IconView mIconView2;
+    @Nullable
+    private CancellableTask<ThumbnailData> mThumbnailLoadRequest2;
+    @Nullable
+    private CancellableTask mIconLoadRequest2;
+    private final float[] mIcon2CenterCoords = new float[2];
+    private TransformingTouchDelegate mIcon2TouchDelegate;
+    @Nullable private StagedSplitBounds mSplitBoundsConfig;
+    private final DigitalWellBeingToast mDigitalWellBeingToast2;
+
+
+    public GroupedTaskView(Context context) {
+        this(context, null);
+    }
+
+    public GroupedTaskView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public GroupedTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mDigitalWellBeingToast2 = new DigitalWellBeingToast(mActivity, this);
+    }
+
+    @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,
+            @Nullable StagedSplitBounds splitBoundsConfig) {
+        super.bind(primary, orientedState);
+        mSecondaryTask = secondary;
+        mTaskIdContainer[1] = secondary.key.id;
+        mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
+                mIconView2, 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);
+                            mDigitalWellBeingToast2.initialize(mSecondaryTask);
+                            mDigitalWellBeingToast2.setSplitConfiguration(mSplitBoundsConfig);
+                            mDigitalWellBeingToast.setSplitConfiguration(mSplitBoundsConfig);
+                        });
+            }
+        } else {
+            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+                mSnapshotView2.setThumbnail(null, null);
+                // Reset the task thumbnail reference as well (it will be fetched from the cache or
+                // reloaded next time we need it)
+                mSecondaryTask.thumbnail = null;
+            }
+            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+                setIcon(mIconView2, null);
+            }
+        }
+    }
+
+    public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) {
+        mSplitBoundsConfig = stagedSplitBounds;
+        invalidate();
+    }
+
+    public float getSplitRatio() {
+        if (mSplitBoundsConfig != null) {
+            return mSplitBoundsConfig.appsStackedVertically
+                    ? mSplitBoundsConfig.topTaskPercent : mSplitBoundsConfig.leftTaskPercent;
+        }
+        return DEFAULT_SPLIT_RATIO;
+    }
+
+    @Override
+    public boolean offerTouchToChildren(MotionEvent event) {
+        computeAndSetIconTouchDelegate(mIconView2, mIcon2CenterCoords, mIcon2TouchDelegate);
+        if (mIcon2TouchDelegate.onTouchEvent(event)) {
+            return true;
+        }
+
+        return super.offerTouchToChildren(event);
+    }
+
+    @Override
+    protected void cancelPendingLoadTasks() {
+        super.cancelPendingLoadTasks();
+        if (mThumbnailLoadRequest2 != null) {
+            mThumbnailLoadRequest2.cancel();
+            mThumbnailLoadRequest2 = null;
+        }
+        if (mIconLoadRequest2 != null) {
+            mIconLoadRequest2.cancel();
+            mIconLoadRequest2 = null;
+        }
+    }
+
+    @Nullable
+    @Override
+    public RunnableList launchTaskAnimated() {
+        if (mTask == null || mSecondaryTask == null) {
+            return null;
+        }
+
+        RunnableList endCallback = new RunnableList();
+        RecentsView recentsView = getRecentsView();
+        // Callbacks run from remote animation when recents animation not currently running
+        recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
+                success -> endCallback.executeAllAndDestroy(),
+                false /* freezeTaskList */);
+
+        // Callbacks get run from recentsView for case when recents animation already running
+        recentsView.addSideTaskLaunchCallback(endCallback);
+        return endCallback;
+    }
+
+    @Override
+    public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+        getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
+                STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
+                getSplitRatio());
+    }
+
+    @Override
+    void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
+        super.refreshThumbnails(thumbnailDatas);
+        if (mSecondaryTask != null && thumbnailDatas != null) {
+            final ThumbnailData thumbnailData = thumbnailDatas.get(mSecondaryTask.key.id);
+            if (thumbnailData != null) {
+                mSnapshotView2.setThumbnail(mSecondaryTask, thumbnailData);
+                return;
+            }
+        }
+
+        mSnapshotView2.refresh();
+    }
+
+    @Override
+    public 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;
+
+        getPagedOrientationHandler().setSplitIconParams(mIconView, mIconView2,
+                taskIconHeight, mSnapshotView.getMeasuredWidth(), mSnapshotView.getMeasuredHeight(),
+                isRtl, deviceProfile, mSplitBoundsConfig);
+    }
+
+    @Override
+    protected void updateSnapshotRadius() {
+        super.updateSnapshotRadius();
+        mSnapshotView2.setFullscreenParams(mCurrentFullscreenParams);
+    }
+
+    @Override
+    protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
+        super.setIconAndDimTransitionProgress(progress, invert);
+        // Value set by super call
+        float scale = mIconView.getAlpha();
+        mIconView2.setAlpha(scale);
+        mDigitalWellBeingToast2.updateBannerOffset(1f - scale,
+                mCurrentFullscreenParams.mCurrentDrawnInsets.top
+                        + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
+    }
+
+    @Override
+    public void setColorTint(float amount, int tintColor) {
+        super.setColorTint(amount, tintColor);
+        mIconView2.setIconColorTint(tintColor, amount);
+        mSnapshotView2.setDimAlpha(amount);
+        mDigitalWellBeingToast2.setBannerColorTint(tintColor, amount);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/IconView.java b/quickstep/src/com/android/quickstep/views/IconView.java
index 5b0ade0..5895c05 100644
--- a/quickstep/src/com/android/quickstep/views/IconView.java
+++ b/quickstep/src/com/android/quickstep/views/IconView.java
@@ -17,10 +17,14 @@
 
 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 androidx.annotation.Nullable;
+
 import com.android.launcher3.Utilities;
 
 /**
@@ -29,7 +33,9 @@
  */
 public class IconView extends View {
 
+    @Nullable
     private Drawable mDrawable;
+    private int mDrawableWidth, mDrawableHeight;
 
     public IconView(Context context) {
         super(context);
@@ -43,27 +49,57 @@
         super(context, attrs, defStyleAttr);
     }
 
-    public void setDrawable(Drawable d) {
+    /**
+     * Sets a {@link Drawable} to be displayed.
+     */
+    public void setDrawable(@Nullable Drawable d) {
         if (mDrawable != null) {
             mDrawable.setCallback(null);
         }
         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);
+    }
+
+    @Nullable
     public Drawable getDrawable() {
         return mDrawable;
     }
 
+    public int getDrawableWidth() {
+        return mDrawableWidth;
+    }
+
+    public int getDrawableHeight() {
+        return mDrawableHeight;
+    }
+
     @Override
     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..e0395ea 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -24,22 +24,21 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.Surface;
-import android.widget.FrameLayout;
 
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
 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,40 +47,22 @@
 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);
     }
 
-    public LauncherRecentsView(Context context, AttributeSet attrs) {
+    public LauncherRecentsView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public LauncherRecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
         mActivity.getStateManager().addStateListener(this);
     }
 
     @Override
-    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+    public void init(OverviewActionsView actionsView,
+            SplitSelectStateController splitPlaceholderView) {
         super.init(actionsView, splitPlaceholderView);
         setContentAlpha(0);
     }
@@ -89,6 +70,7 @@
     @Override
     public void startHome() {
         mActivity.getStateManager().goToState(NORMAL);
+        AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
     }
 
     @Override
@@ -123,8 +105,14 @@
             // Clean-up logic that occurs when recents is no longer in use/visible.
             reset();
         }
-        setOverlayEnabled(finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK);
+        boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
+        setOverlayEnabled(isOverlayEnabled);
         setFreezeViewVisibility(false);
+
+        if (isOverlayEnabled) {
+            runActionOnRemoteHandles(remoteTargetHandle ->
+                    remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
+        }
     }
 
     @Override
@@ -146,73 +134,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 +146,7 @@
         } else {
             if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
                 mActivity.getStateManager().goToState(LauncherState.OVERVIEW);
+                resetModalVisuals();
             }
         }
     }
@@ -242,19 +164,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);
     }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        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();
-        }
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 8c115e5..0294828 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -16,16 +16,16 @@
 
 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;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -33,7 +33,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 +54,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 +81,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 Button mSplitButton;
 
     @ActionsHiddenFlags
     private int mHiddenFlags;
@@ -88,11 +91,10 @@
     @ActionsDisabledFlags
     protected int mDisabledFlags;
 
+    @Nullable
     protected T mCallbacks;
 
-    private float mModalness;
-    private float mModalTransformY;
-
+    @Nullable
     protected DeviceProfile mDp;
 
     public OverviewActionsView(Context context) {
@@ -112,13 +114,10 @@
     @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 +135,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 +179,6 @@
         } else {
             mDisabledFlags &= ~disabledFlags;
         }
-        //
         boolean isEnabled = (mDisabledFlags & ~DISABLED_ROTATED) == 0;
         LayoutUtils.setViewEnabled(this, isEnabled);
     }
@@ -197,10 +195,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);
     }
@@ -223,30 +217,27 @@
     public void setDp(DeviceProfile dp) {
         mDp = dp;
         updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
+
+        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+                dp.isVerticalBarLayout() ? 0 : dp.overviewActionsButtonSpacing,
+                ViewGroup.LayoutParams.MATCH_PARENT);
+        params.weight = dp.isVerticalBarLayout() ? 1 : 0;
+        findViewById(R.id.action_split_space).setLayoutParams(params);
+
         requestLayout();
+
+        mSplitButton.setCompoundDrawablesWithIntrinsicBounds(
+                (dp.isLandscape ? R.drawable.ic_split_horizontal : R.drawable.ic_split_vertical),
+                0, 0, 0);
     }
 
-    /**
-     * 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 +252,7 @@
             return dp.overviewActionsMarginThreeButtonPx;
         }
 
-        return dp.overviewActionsMarginGesturePx;
+        return dp.overviewActionsTopMarginGesturePx;
     }
 
     /** Get the bottom margin associated with the action buttons in Overview. */
@@ -277,6 +268,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..7218cd7 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,19 @@
 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.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 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 +82,17 @@
 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.Pair;
 import android.util.SparseBooleanArray;
-import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -96,10 +104,11 @@
 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.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 import androidx.core.graphics.ColorUtils;
@@ -112,7 +121,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 +134,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.StagePosition;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TranslateEdgeEffect;
 import com.android.launcher3.util.ViewPool;
@@ -143,11 +152,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.GroupTask;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitScreenBounds;
@@ -155,6 +167,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;
@@ -168,7 +181,9 @@
 import com.android.wm.shell.pip.IPipAnimationListener;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Consumer;
 
 /**
@@ -180,6 +195,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 +257,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 +341,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,25 +379,41 @@
     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;
+    @Nullable
     protected RecentsAnimationController mRecentsAnimationController;
+    @Nullable
     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.
+
+    @Nullable
+    protected RemoteTargetHandle[] mRemoteTargetHandles;
     protected final Rect mLastComputedTaskSize = new Rect();
     protected final Rect mLastComputedGridSize = new Rect();
     protected final Rect mLastComputedGridTaskSize = new Rect();
     // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
+    @Nullable
     protected Float mLastComputedTaskStartPushOutDistance = null;
+    @Nullable
     protected Float mLastComputedTaskEndPushOutDistance = null;
     protected boolean mEnableDrawingLiveTile = false;
     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 +422,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 +438,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,17 +458,21 @@
     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.
+    @Nullable
     protected GestureState.GestureEndTarget mCurrentGestureEndTarget;
 
     // TODO(b/187528071): Remove these and replace with a real scrim.
     private float mColorTint;
     private final int mTintingColor;
+    @Nullable
     private ObjectAnimator mTintingAnimator;
 
     private int mOverScrollShift = 0;
+    private long mScrollLastHapticTimestamp;
 
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -434,7 +489,7 @@
             }
 
             // Remove the task immediately from the task list
-            TaskView taskView = getTaskView(taskId);
+            TaskView taskView = getTaskViewByTaskId(taskId);
             if (taskView != null) {
                 removeView(taskView);
             }
@@ -456,7 +511,7 @@
                 return;
             }
 
-            TaskView taskView = getTaskView(taskId);
+            TaskView taskView = getTaskViewByTaskId(taskId);
             if (taskView == null) {
                 return;
             }
@@ -489,13 +544,19 @@
     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;
+    @Nullable
+    private Task[] mTmpRunningTasks;
+    protected int mFocusedTaskViewId = -1;
 
-    private boolean mRunningTaskIconScaledDown = false;
+    private boolean mTaskIconScaledDown = false;
     private boolean mRunningTaskShowScreenshot = false;
 
     private boolean mOverviewStateEnabled;
@@ -506,7 +567,9 @@
     private int mDownX;
     private int mDownY;
 
+    @Nullable
     private PendingAnimation mPendingAnimation;
+    @Nullable
     private LayoutTransition mLayoutTransition;
 
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -522,6 +585,7 @@
 
     // Keeps track of task id whose visual state should not be reset
     private int mIgnoreResetTaskId = -1;
+    protected boolean mLoadPlanEverApplied;
 
     // Variables for empty state
     private final Drawable mEmptyIcon;
@@ -530,21 +594,34 @@
     private final Point mLastMeasureSize = new Point();
     private final int mEmptyMessagePadding;
     private boolean mShowEmptyMessage;
+    @Nullable
     private OnEmptyMessageUpdatedListener mOnEmptyMessageUpdatedListener;
+    @Nullable
     private Layout mEmptyTextLayout;
 
     /**
      * 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)}
      */
+    @Nullable
     private TaskView mSplitHiddenTaskView;
+    @Nullable
+    private TaskView mSecondSplitHiddenTaskView;
+    @Nullable
+    private StagedSplitBounds mSplitBoundsConfig;
+    private final Toast mSplitToast = Toast.makeText(getContext(),
+            R.string.toast_split_select_app, Toast.LENGTH_SHORT);
+    private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
+            R.string.toast_split_app_unsupported, 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
@@ -553,10 +630,18 @@
      * this doesn't get adjusted to reflect the new child count after the taskView is dismissed/
      * removed from recentsView
      */
-    private int mSplitHiddenTaskViewIndex;
+    private int mSplitHiddenTaskViewIndex = -1;
+    @Nullable
+    private FloatingTaskView mFirstFloatingTaskView;
+    @Nullable
+    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.
+     */
+    @Nullable
+    private TaskView mMovingTaskView;
+
     private OverviewActionsView mActionsView;
 
     private MultiWindowModeChangedListener mMultiWindowModeChangedListener =
@@ -574,12 +659,14 @@
                 }
             };
 
+    @Nullable
     private RunnableList mSideTaskLaunchCallback;
+    @Nullable
+    private TaskLaunchListener mTaskLaunchListener;
 
-    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
+    public RecentsView(Context context, @Nullable 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 +675,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 +687,13 @@
         mClearAllButton.setOnClickListener(this::dismissAllTasks);
         mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
                 10 /* initial size */);
+        mGroupedTaskViewPool = new ViewPool<>(context, this,
+                R.layout.task_grouped, 20 /* max size */, 10 /* 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 +720,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);
     }
 
@@ -665,7 +750,7 @@
 
             int primarySize = mOrientationHandler.getPrimaryValue(getWidth(), getHeight());
             int scroll = OverScroll.dampedScroll(getUndampedOverScrollShift(), primarySize);
-            mOrientationHandler.set(canvas, CANVAS_TRANSLATE, scroll);
+            mOrientationHandler.setPrimary(canvas, CANVAS_TRANSLATE, scroll);
 
             if (mOverScrollShift != scroll) {
                 mOverScrollShift = scroll;
@@ -682,7 +767,7 @@
             super.dispatchDraw(canvas);
         }
         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
-                && mLiveTileParams.getTargetSet() != null) {
+                && mRemoteTargetHandles != null) {
             redrawLiveTile();
         }
     }
@@ -720,13 +805,18 @@
     }
 
     @Override
+    @Nullable
     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;
@@ -735,7 +825,7 @@
     @Override
     public void onTaskIconChanged(String pkg, UserHandle user) {
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView tv = getTaskViewAt(i);
+            TaskView tv = requireTaskViewAt(i);
             Task task = tv.getTask();
             if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
                     && task.key.userId == user.getIdentifier()) {
@@ -751,8 +841,9 @@
      * Update the thumbnail of the task.
      * @param refreshNow Refresh immediately if it's true.
      */
+    @Nullable
     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 +856,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 +878,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 +897,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 +912,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 +939,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 +956,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,20 +978,30 @@
         }
     }
 
+    /**
+     * 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();
-            if (targets != null && targets.findTask(taskId) != null) {
-                launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
-                        targets.nonApps);
-            }
+        int runningTaskViewId = getTaskViewIdFromTaskId(taskId);
+        if (mRunningTaskViewId == -1 ||
+                mRunningTaskViewId != runningTaskViewId ||
+                mRemoteTargetHandles == null) {
+            return;
+        }
+
+        TransformParams params = mRemoteTargetHandles[0].getTransformParams();
+        RemoteAnimationTargets targets = params.getTargetSet();
+        if (targets != null && targets.findTask(taskId) != null) {
+            launchSideTaskInLiveTileMode(taskId, targets.apps, targets.wallpapers,
+                    targets.nonApps);
         }
     }
 
     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 +1035,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 +1046,53 @@
         }
     }
 
+    public boolean isTaskViewFullyVisible(TaskView tv) {
+        if (showAsGrid()) {
+            int screenStart = mOrientationHandler.getPrimaryScroll(this);
+            int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
+            return isTaskViewFullyWithinBounds(tv, screenStart, screenEnd);
+        } else {
+            // For now, just check if it's the active task
+            return indexOfChild(tv) == getNextPage();
+        }
+    }
+
+    @Nullable
+    private TaskView getLastGridTaskView() {
+        return getLastGridTaskView(getTopRowIdArray(), getBottomRowIdArray());
+    }
+
+    @Nullable
+    private TaskView getLastGridTaskView(IntArray topRowIdArray, IntArray bottomRowIdArray) {
+        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 +1103,37 @@
                 && taskEnd <= end);
     }
 
-    public TaskView getTaskView(int taskId) {
+    private boolean isTaskViewFullyWithinBounds(TaskView tv, int start, int end) {
+        int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment(
+                showAsFullscreen(), showAsGrid());
+        int taskSize = (int) (mOrientationHandler.getMeasuredSize(tv) * tv.getSizeAdjustment(
+                showAsFullscreen()));
+        int taskEnd = taskStart + taskSize;
+        return taskStart >= start && taskEnd <= end;
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Returns a {@link TaskView} that has taskId matching {@code taskId} or null if no match.
+     */
+    @Nullable
+    public TaskView getTaskViewByTaskId(int taskId) {
+        if (taskId == -1) {
+            return null;
+        }
+
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
-            if (taskView.hasTaskId(taskId)) {
+            TaskView taskView = requireTaskViewAt(i);
+            int[] taskIds = taskView.getTaskIds();
+            if (taskIds[0] == taskId || taskIds[1] == taskId) {
                 return taskView;
             }
         }
@@ -959,10 +1147,8 @@
         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()) {
-                cancelSplitSelect(false);
-            }
+            mTmpRunningTasks = null;
+            mSplitBoundsConfig = null;
         }
         updateLocusId();
     }
@@ -996,13 +1182,19 @@
     }
 
     @Override
+    protected float getSignificantMoveThreshold() {
+        return mActivity.getDeviceProfile().isTablet ? SIGNIFICANT_MOVE_THRESHOLD_TABLET
+                : super.getSignificantMoveThreshold();
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         super.onTouchEvent(ev);
 
         if (showAsGrid()) {
             int taskCount = getTaskViewCount();
             for (int i = 0; i < taskCount; i++) {
-                TaskView taskView = getTaskViewAt(i);
+                TaskView taskView = requireTaskViewAt(i);
                 if (isTaskViewVisible(taskView) && taskView.offerTouchToChildren(ev)) {
                     // Keep consuming events to pass to delegate
                     return true;
@@ -1065,7 +1257,7 @@
     @Override
     protected void onNotSnappingToPageInFreeScroll() {
         int finalPos = mScroller.getFinalX();
-        if (!showAsGrid() && finalPos > mMinScroll && finalPos < mMaxScroll) {
+        if (finalPos > mMinScroll && finalPos < mMaxScroll) {
             int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
             int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
 
@@ -1079,6 +1271,19 @@
                             ? mMaxScroll
                             : getScrollForPage(mNextPage);
 
+            if (showAsGrid()) {
+                if (isSplitSelectionActive()) {
+                    return;
+                }
+                TaskView taskView = getTaskViewAt(mNextPage);
+                // Only snap to fully visible focused task.
+                if (taskView == null
+                        || !taskView.isFocusedTask()
+                        || !isTaskViewFullyVisible(taskView)) {
+                    return;
+                }
+            }
+
             mScroller.setFinalX(pageSnapped);
             // Ensure the scroll/snap doesn't happen too fast;
             int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
@@ -1090,6 +1295,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,20 +1321,57 @@
         }
     }
 
-    protected void applyLoadPlan(ArrayList<Task> tasks) {
-        if (mPendingAnimation != null) {
-            mPendingAnimation.addEndListener(success -> applyLoadPlan(tasks));
+    /**
+     * 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;
         }
 
-        if (tasks == null || tasks.isEmpty()) {
+        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<GroupTask> taskGroups) {
+        if (mPendingAnimation != null) {
+            mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
+            return;
+        }
+
+        mLoadPlanEverApplied = true;
+        if (taskGroups == null || taskGroups.isEmpty()) {
             removeTasksViewsAndClearAllButton();
             onTaskStackUpdated();
             return;
         }
 
         int currentTaskId = -1;
-        TaskView currentTaskView = getTaskViewAtByAbsoluteIndex(mCurrentPage);
+        TaskView currentTaskView = getTaskViewAt(mCurrentPage);
         if (currentTaskView != null) {
             currentTaskId = currentTaskView.getTask().key.id;
         }
@@ -1119,44 +1380,81 @@
         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) {
-            if (indexOfChild(mClearAllButton) != -1) {
-                removeView(mClearAllButton);
-            }
-            for (int i = getTaskViewCount(); i < requiredTaskCount; i++) {
-                addView(mTaskViewPool.getView());
-            }
-            while (getTaskViewCount() > requiredTaskCount) {
-                removeView(getChildAt(getChildCount() - 1));
-            }
-            if (requiredTaskCount > 0) {
-                addView(mClearAllButton);
+        // Save running task ID if it exists before rebinding all taskViews, otherwise the task from
+        // the runningTaskView currently bound could get assigned to another TaskView
+        int runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId)[0];
+        int focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId)[0];
+
+        // Removing views sets the currentPage to 0, so we save this and restore it after
+        // the new set of views are added
+        int previousCurrentPage = mCurrentPage;
+        removeAllViews();
+
+        // Add views as children based on whether it's grouped or single task
+        for (int i = taskGroups.size() - 1; i >= 0; i--) {
+            GroupTask groupTask = taskGroups.get(i);
+            boolean hasMultipleTasks = groupTask.hasMultipleTasks();
+            TaskView taskView = getTaskViewFromPool(hasMultipleTasks);
+            addView(taskView);
+
+            if (hasMultipleTasks) {
+                boolean firstTaskIsLeftTopTask =
+                        groupTask.mStagedSplitBounds.leftTopTaskId == groupTask.task1.key.id;
+                Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
+                Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
+                ((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
+                        groupTask.mStagedSplitBounds);
+            } else {
+                taskView.bind(groupTask.task1, mOrientationState);
             }
         }
-
-        // 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);
-            final TaskView taskView = (TaskView) getChildAt(pageIndex);
-            taskView.bind(task, mOrientationState);
+        if (!taskGroups.isEmpty()) {
+            addView(mClearAllButton);
         }
+
+        boolean settlingOnNewTask = mNextPage != INVALID_PAGE;
+        if (settlingOnNewTask) {
+            // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
+            mCurrentPage = previousCurrentPage;
+        } else {
+            setCurrentPage(previousCurrentPage);
+        }
+
+        // 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) {
+                mRunningTaskViewId = newRunningTaskView.getTaskViewId();
+            } else {
+                mRunningTaskViewId = -1;
+            }
+        }
 
         int targetPage = -1;
-        if (mNextPage == INVALID_PAGE) {
+        if (!settlingOnNewTask) {
             // 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));
+                targetPage = indexOfChild(requireTaskViewAt(0));
             }
         } else if (currentTaskId != -1) {
-            currentTaskView = getTaskView(currentTaskId);
+            currentTaskView = getTaskViewByTaskId(currentTaskId);
             if (currentTaskView != null) {
                 targetPage = indexOfChild(currentTaskView);
             }
@@ -1165,7 +1463,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.
@@ -1186,7 +1485,7 @@
 
     private void removeTasksViewsAndClearAllButton() {
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            removeView(getTaskViewAt(i));
+            removeView(requireTaskViewAt(i));
         }
         if (indexOfChild(mClearAllButton) != -1) {
             removeView(mClearAllButton);
@@ -1194,13 +1493,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();
@@ -1208,23 +1531,26 @@
 
     public void resetTaskVisuals() {
         for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            TaskView taskView = getTaskViewAt(i);
-            if (mIgnoreResetTaskId != taskView.getTask().key.id) {
+            TaskView taskView = requireTaskViewAt(i);
+            if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
                 taskView.resetViewTransforms();
+                taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
                 taskView.setStableAlpha(mContentAlpha);
                 taskView.setFullscreenProgress(mFullscreenProgress);
                 taskView.setModalness(mTaskModalness);
             }
         }
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            // 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;
-
+            // resetTaskVisuals is called at the end of dismiss animation which could update
+            // primary and secondary translation of the live tile cut out. We will need to do so
+            // here accordingly.
+            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.
             if (!mRunningTaskShowScreenshot) {
@@ -1235,11 +1561,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);
@@ -1251,7 +1572,7 @@
         mFullscreenProgress = fullscreenProgress;
         int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
-            getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
+            requireTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
         }
         mClearAllButton.setFullscreenProgress(fullscreenProgress);
 
@@ -1279,13 +1600,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 +1634,13 @@
                 || !mOrientationHandler.equals(oldOrientationHandler)) {
             // Changed orientations, update controllers so they intercept accordingly.
             mActivity.getDragLayer().recreateControllers();
+            onOrientationChanged();
         }
 
         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();
@@ -1329,6 +1653,15 @@
         setCurrentPage(mCurrentPage);
     }
 
+    private void onOrientationChanged() {
+        // If overview is in modal state when rotate, reset it to overview state without running
+        // animation.
+        setModalStateEnabled(false);
+        if (isSplitSelectionActive()) {
+            onRotateInSplitSelectionState();
+        }
+    }
+
     // Update task size and padding that are dependent on DeviceProfile and insets.
     private void updateSizeAndPadding() {
         DeviceProfile dp = mActivity.getDeviceProfile();
@@ -1346,36 +1679,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;
@@ -1383,25 +1708,23 @@
 
         float accumulatedTranslationX = 0;
         for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(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 +1732,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 +1763,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 +1799,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 +1827,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;
@@ -1536,7 +1854,7 @@
 
         // Update the task data for the in/visible children
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             Task task = taskView.getTask();
             int index = indexOfChild(taskView);
             boolean visible;
@@ -1546,8 +1864,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 +1902,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 +1917,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 +1929,12 @@
 
     public abstract void startHome();
 
-    /** `true` if there is a +1 space available in overview. */
-    public boolean hasRecentsExtraCard() {
-        return false;
-    }
-
     public void reset() {
         setCurrentTask(-1);
+        mCurrentPageScrollDiff = 0;
         mIgnoreResetTaskId = -1;
         mTaskListChangeId = -1;
-        mFocusedTaskId = -1;
+        mFocusedTaskViewId = -1;
 
         if (mRecentsAnimationController != null) {
             if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
@@ -1622,8 +1945,12 @@
             }
         }
         setEnableDrawingLiveTile(false);
-        mLiveTileParams.setTargetSet(null);
-        mLiveTileTaskViewSimulator.setDrawsBelowRecents(true);
+        runActionOnRemoteHandles(remoteTargetHandle -> {
+            remoteTargetHandle.getTransformParams().setTargetSet(null);
+            remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
+        });
+        resetFromSplitSelectionState();
+        mSplitSelectStateController.resetState();
 
         // 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 +1964,72 @@
         });
     }
 
-    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);
+    }
+
+    @Nullable
+    private TaskView getTaskViewFromTaskViewId(int taskViewId) {
+        if (taskViewId == -1) {
+            return null;
+        }
+
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView taskView = requireTaskViewAt(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 +2037,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 +2053,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 +2064,7 @@
         setEnableFreeScroll(false);
         setEnableDrawingLiveTile(false);
         setRunningTaskHidden(true);
-        setRunningTaskIconScaledDown(true);
+        setTaskIconScaledDown(true);
     }
 
     /**
@@ -1704,14 +2072,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;
         }
@@ -1729,7 +2095,7 @@
         int runningIndex = getCurrentPage();
         AnimatorSet as = new AnimatorSet();
         for (int i = 0; i < getTaskViewCount(); i++) {
-            View taskView = getTaskViewAt(i);
+            View taskView = requireTaskViewAt(i);
             if (runningIndex == i && taskView.getAlpha() != 0) {
                 continue;
             }
@@ -1738,10 +2104,9 @@
         return as;
     }
 
-
     private void updateChildTaskOrientations() {
         for (int i = 0; i < getTaskViewCount(); i++) {
-            getTaskViewAt(i).setOrientationState(mOrientationState);
+            requireTaskViewAt(i).setOrientationState(mOrientationState);
         }
         TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mActivity, TYPE_TASK_MENU);
         if (tv != null) {
@@ -1753,21 +2118,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 +2167,28 @@
             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) {
+            TaskView primaryTaskView = getTaskViewByTaskId(runningTaskInfos[0].taskId);
+            TaskView secondaryTaskView = getTaskViewByTaskId(runningTaskInfos[1].taskId);
+            int leftTopTaskViewId =
+                    (primaryTaskView == null) ? -1 : primaryTaskView.getTaskViewId();
+            int rightBottomTaskViewId =
+                    (secondaryTaskView == null) ? -1 : secondaryTaskView.getTaskViewId();
+            // Add a new stub view if both taskIds don't match any taskViews
+            return leftTopTaskViewId != rightBottomTaskViewId || leftTopTaskViewId == -1;
+        }
+        RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
+        return runningTaskInfo != null && getTaskViewByTaskId(runningTaskInfo.taskId) == null;
     }
 
     /**
@@ -1815,71 +2197,89 @@
      * 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 (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);
+        reloadIfNeeded();
     }
 
     /**
      * 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 +2303,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++) {
+                requireTaskViewAt(i).setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
+            }
         }
     }
 
@@ -1928,65 +2320,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 = requireTaskViewAt(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,13 +2388,15 @@
         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();
         }
         for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
+            TaskView taskView = requireTaskViewAt(i);
             int taskWidthAndSpacing = taskView.getLayoutParams().width + mPageSpacing;
             // Evenly distribute tasks between rows unless rearranging due to task dismissal, in
             // which case keep tasks in their respective rows. For the running task, don't join
@@ -2036,15 +2428,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;
@@ -2052,7 +2461,7 @@
                         if (j == focusedTaskIndex) {
                             continue;
                         }
-                        widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+                        widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
                     }
 
                     float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
@@ -2063,7 +2472,7 @@
                     bottomSet.add(i);
 
                     // Move into bottom row.
-                    taskView.setGridTranslationY(heightOffset + taskGridVerticalDiff);
+                    taskView.setGridTranslationY(mTopBottomRowHeightDiff + mTaskGridVerticalDiff);
 
                     // Move horizontally into empty space.
                     float widthOffset = 0;
@@ -2071,7 +2480,7 @@
                         if (j == focusedTaskIndex) {
                             continue;
                         }
-                        widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+                        widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
                     }
 
                     float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
@@ -2087,20 +2496,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 +2536,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 +2546,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 = requireTaskViewAt(i);
+            taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX
+                    + snappedTaskNonGridScrollAdjustment);
+        }
+
         mClearAllButton.setGridTranslationPrimary(
                 clearAllTotalTranslationX - snappedTaskGridTranslationX);
         mClearAllButton.setGridScrollOffset(
@@ -2167,13 +2576,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));
     }
 
     /**
@@ -2190,7 +2599,7 @@
         mGridProgress = gridProgress;
 
         for (int i = 0; i < taskCount; i++) {
-            getTaskViewAt(i).setGridProgress(gridProgress);
+            requireTaskViewAt(i).setGridProgress(gridProgress);
         }
         mClearAllButton.setGridProgress(gridProgress);
     }
@@ -2245,61 +2654,78 @@
             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));
+            });
+        }
+        anim.setFloat(taskView, VIEW_ALPHA, 0,
+                clampToProgress(isOnGridBottomRow(taskView) ? 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 +2736,183 @@
             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 = requireTaskViewAt(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]);
+            }
+        }
+
+        announceForAccessibility(getResources().getString(R.string.task_view_closed));
+
+        float dismissTranslationInterpolationEnd = 1;
+        boolean closeGapBetweenClearAll = false;
+        boolean isClearAllHidden = isClearAllHidden();
+        boolean snapToLastTask = false;
+        boolean isLandscapeSplit =
+                mActivity.getDeviceProfile().isLandscape && isSplitSelectionActive();
+        boolean isSplitPlaceholderFirstInGrid = isSplitPlaceholderFirstInGrid();
+        boolean isSplitPlaceholderLastInGrid = isSplitPlaceholderLastInGrid();
+        TaskView lastGridTaskView = showAsGrid ? getLastGridTaskView() : null;
+        int currentPageScroll = getScrollForPage(mCurrentPage);
+        int lastGridTaskScroll = getScrollForPage(indexOfChild(lastGridTaskView));
+        boolean currentPageSnapsToEndOfGrid = currentPageScroll == lastGridTaskScroll;
+        if (lastGridTaskView != null && lastGridTaskView.isVisibleToUser()) {
+            // 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.
+                        snapToLastTask = true;
+                    }
+                } else {
+                    // If only focused task will be left, snap to focused task instead.
+                    longGridRowWidthDiff += getSnapToFocusedTaskScrollDiff(isClearAllHidden);
+                }
+            }
+            if (mClearAllButton.getAlpha() != 0f && isLandscapeSplit) {
+                // ClearAllButton will not be available in split select, snap to last task instead.
+                snapToLastTask = true;
+            }
+            if (snapToLastTask) {
+                longGridRowWidthDiff += getSnapToLastTaskScrollDiff();
+                if (isSplitPlaceholderLastInGrid) {
+                    // Shift all the tasks to make space for split placeholder.
+                    longGridRowWidthDiff += mIsRtl ? mSplitPlaceholderSize : -mSplitPlaceholderSize;
+                }
+            } else if (isLandscapeSplit && currentPageSnapsToEndOfGrid) {
+                // Use last task as reference point for scroll diff and snapping calculation as it's
+                // the only invariant point in landscape split screen.
+                snapToLastTask = true;
+            }
+
+            // 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 = requireTaskViewAt(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 +2921,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 +2942,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 +2950,79 @@
                             && 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;
+                    if (isFocusedTaskDismissed && nextFocusedTaskView == null) {
+                        // Moves less if focused task is not in scroll position.
+                        int focusedTaskScroll = getScrollForPage(dismissedIndex);
+                        int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+                        int focusedTaskScrollDiff = primaryScroll - focusedTaskScroll;
+                        primaryTranslation +=
+                                mIsRtl ? focusedTaskScrollDiff : -focusedTaskScrollDiff;
+                        if (isSplitPlaceholderFirstInGrid) {
+                            // Moves less if split placeholder is at the start.
+                            primaryTranslation +=
+                                    mIsRtl ? -mSplitPlaceholderSize : mSplitPlaceholderSize;
+                        }
+                    }
+
+                    anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
+                            mIsRtl ? primaryTranslation : -primaryTranslation,
+                            clampToProgress(LINEAR, animationStartProgress,
+                                    dismissTranslationInterpolationEnd));
                 }
             }
         }
@@ -2405,15 +3033,19 @@
 
         // 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;
+        final boolean finalSnapToLastTask = snapToLastTask;
+        final boolean finalIsFocusedTaskDismissed = isFocusedTaskDismissed;
         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 {
@@ -2423,53 +3055,199 @@
 
             @SuppressWarnings("WrongCall")
             private void onEnd(boolean success) {
+                // Reset task translations as they may have updated via animations in
+                // createTaskDismissAnimation
+                resetTaskVisuals();
+
                 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);
                         }
                     }
 
-                    // Reset task translations as they may have updated via animations in
-                    // createTaskDismissAnimation
-                    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 (finalSnapToLastTask) {
+                                // Last task will be determined after removing dismissed task.
+                                pageToSnapTo = -1;
+                            } else if (taskCount > 2) {
+                                pageToSnapTo = indexOfChild(mClearAllButton);
+                            } 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);
+                            boolean calculateScrollDiff = true;
+                            if (snappedTaskView != null && !finalSnapToLastTask) {
+                                if (snappedTaskView.getTaskViewId() == mFocusedTaskViewId) {
+                                    if (finalNextFocusedTaskView != null) {
+                                        taskViewIdToSnapTo =
+                                                finalNextFocusedTaskView.getTaskViewId();
+                                    } else if (dismissedTaskViewId != mFocusedTaskViewId) {
+                                        taskViewIdToSnapTo = mFocusedTaskViewId;
+                                    } else {
+                                        // Won't focus next task in split select, so snap to the
+                                        // first task.
+                                        pageToSnapTo = 0;
+                                        calculateScrollDiff = false;
+                                    }
+                                } 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) {
+                            if (calculateScrollDiff) {
+                                int primaryScroll = mOrientationHandler.getPrimaryScroll(
+                                        RecentsView.this);
+                                int currentPageScroll = getScrollForPage(mCurrentPage);
+                                mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+                                // Compensate for coordinate shift by split placeholder.
+                                if (isSplitPlaceholderFirstInGrid && !finalSnapToLastTask) {
+                                    mCurrentPageScrollDiff +=
+                                            mIsRtl ? -mSplitPlaceholderSize : mSplitPlaceholderSize;
+                                } else if (isSplitPlaceholderLastInGrid && finalSnapToLastTask) {
+                                    mCurrentPageScrollDiff +=
+                                            mIsRtl ? mSplitPlaceholderSize : -mSplitPlaceholderSize;
+                                }
+                            }
+                        }
+                    } else if (dismissedIndex < pageToSnapTo || pageToSnapTo == taskCount - 1) {
+                        pageToSnapTo--;
+                    }
+                    boolean isHomeTaskDismissed = dismissedTaskView == getHomeTaskView();
+                    removeViewInLayout(dismissedTaskView);
+                    mTopRowIdSet.remove(dismissedTaskViewId);
+
+                    if (taskCount == 1) {
                         removeViewInLayout(mClearAllButton);
-                        startHome();
+                        if (isHomeTaskDismissed) {
+                            updateEmptyMessage();
+                        } else {
+                            startHome();
+                        }
                     } else {
-                        snapToPageImmediately(pageToSnapTo);
+                        // Update focus task and its size.
+                        if (finalIsFocusedTaskDismissed && 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 = requireTaskViewAt(highestVisibleTaskIndex);
+
+                                boolean shouldRebalance;
+                                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();
+                                }
+                            }
+
+                            IntArray topRowIdArray = getTopRowIdArray();
+                            IntArray bottomRowIdArray = getBottomRowIdArray();
+                            if (finalSnapToLastTask) {
+                                // If snapping to last task, find the last task after dismissal.
+                                pageToSnapTo = indexOfChild(
+                                        getLastGridTaskView(topRowIdArray, bottomRowIdArray));
+                            } else if (taskViewIdToSnapTo != -1) {
+                                // If snapping to another page due to indices rearranging, find
+                                // the new index after dismissal & rearrange using the task view id.
+                                pageToSnapTo = indexOfChild(
+                                        getTaskViewFromTaskViewId(taskViewIdToSnapTo));
+                                if (!currentPageSnapsToEndOfGrid) {
+                                    // If it wasn't snapped to one of the last pages, but is now
+                                    // snapped to last pages, we'll need to compensate for the
+                                    // offset from the page's scroll to its visual position.
+                                    mCurrentPageScrollDiff += getOffsetFromScrollPosition(
+                                            pageToSnapTo, topRowIdArray, bottomRowIdArray);
+                                }
+                            }
+                        }
+                        pageBeginTransition();
+                        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,55 +3255,111 @@
         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 = requireTaskViewAt(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 = requireTaskViewAt(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}
+     * Returns {@code true} if one of the task thumbnails would intersect/overlap with the
+     * {@link #mFirstFloatingTaskView}.
      */
-    public boolean shouldShiftThumbnailsForSplitSelect(@SplitConfigurationOptions.StagePosition
-            int stagePosition) {
-        if (!mActivity.getDeviceProfile().isTablet) {
-            // Never enough space on phones
-            return true;
-        } else if (!mActivity.getDeviceProfile().isLandscape) {
-            return false;
-        }
-
-        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());
-        } else {
-            splitBounds.set(0, 0, (int) (placeholderSize), getHeight());
-        }
-        Rect taskBounds = new Rect();
-        int taskCount = getTaskViewCount();
-        for (int i = 0; i < taskCount; i++) {
-            TaskView taskView = getTaskViewAt(i);
-            if (taskView == mSplitHiddenTaskView && 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
-                continue;
-            }
-            taskView.getBoundsOnScreen(taskBounds);
-            if (Rect.intersects(taskBounds, splitBounds)) {
-                return true;
-            }
-        }
-        return false;
+    public boolean shouldShiftThumbnailsForSplitSelect() {
+        return !mActivity.getDeviceProfile().isTablet || !mActivity.getDeviceProfile().isLandscape;
     }
 
     protected void onDismissAnimationEnds() {
+        AccessibilityManagerCompat.sendDismissAnimationEndsEventToTest(getContext());
     }
 
     public PendingAnimation createAllTasksDismissAnimation(long duration) {
@@ -2536,7 +3370,7 @@
 
         int count = getTaskViewCount();
         for (int i = 0; i < count; i++) {
-            addDismissedTaskAnimations(getTaskViewAt(i), duration, anim);
+            addDismissedTaskAnimations(requireTaskViewAt(i), duration, anim);
         }
 
         mPendingAnimation = anim;
@@ -2578,7 +3412,7 @@
 
     @UiThread
     private void dismissTask(int taskId) {
-        TaskView taskView = getTaskView(taskId);
+        TaskView taskView = getTaskViewByTaskId(taskId);
         if (taskView == null) {
             return;
         }
@@ -2587,7 +3421,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 +3491,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) {
+            TaskView child = requireTaskViewAt(i);
+            int[] childTaskIds = child.getTaskIds();
+            if (!mRunningTaskTileHidden ||
+                    (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) {
                 child.setStableAlpha(alpha);
             }
         }
@@ -2704,6 +3541,7 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         updateRecentsRotation();
+        onOrientationChanged();
     }
 
     /**
@@ -2730,22 +3568,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 +3591,16 @@
      */
     @Nullable
     public TaskView getTaskViewAt(int index) {
-        return getTaskViewAtByAbsoluteIndex(index + mTaskViewStartIndex);
+        View child = getChildAt(index);
+        return child instanceof TaskView ? (TaskView) child : null;
     }
 
-    @Nullable
-    private TaskView getTaskViewAtByAbsoluteIndex(int index) {
-        if (index < getChildCount() && index >= 0) {
-            View child = getChildAt(index);
-            return child instanceof TaskView ? (TaskView) child : null;
-        }
-        return null;
+    /**
+     * A version of {@link #getTaskViewAt} when the caller is sure about the input index.
+     */
+    @NonNull
+    private TaskView requireTaskViewAt(int index) {
+        return Objects.requireNonNull(getTaskViewAt(index));
     }
 
     public void setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener listener) {
@@ -2788,6 +3626,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 +3648,9 @@
         mLastComputedTaskStartPushOutDistance = null;
         mLastComputedTaskEndPushOutDistance = null;
         updatePageOffsets();
+        runActionOnRemoteHandles(
+                remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
+                        .setScroll(getScrollOffset()));
         setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
@@ -2811,8 +3660,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 +3714,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 +3735,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));
@@ -2962,16 +3819,24 @@
     protected void setTaskViewsResistanceTranslation(float translation) {
         mTaskViewsSecondaryTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView task = getTaskViewAt(i);
+            TaskView task = requireTaskViewAt(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++) {
+            requireTaskViewAt(i).updateSnapshotRadius();
+        }
     }
 
     protected void setTaskViewsPrimarySplitTranslation(float translation) {
         mTaskViewsPrimarySplitTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView task = getTaskViewAt(i);
+            TaskView task = requireTaskViewAt(i);
             task.getPrimarySplitTranslationProperty().set(task, translation);
         }
     }
@@ -2979,12 +3844,73 @@
     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 = requireTaskViewAt(i);
+            if (taskView == mSplitHiddenTaskView) {
+                continue;
+            }
+            taskView.getSecondarySplitTranslationProperty().set(taskView, translation);
         }
     }
 
     /**
+     * Apply scroll offset to children of RecentsView when entering split select.
+     */
+    public void applySplitPrimaryScrollOffset() {
+        float taskSplitScrollOffsetPrimary = 0f;
+        float clearAllSplitScrollOffsetPrimar = 0f;
+        if (isSplitPlaceholderFirstInGrid()) {
+            taskSplitScrollOffsetPrimary = mSplitPlaceholderSize;
+        } else if (isSplitPlaceholderLastInGrid()) {
+            clearAllSplitScrollOffsetPrimar = -mSplitPlaceholderSize;
+        }
+
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            requireTaskViewAt(i).setSplitScrollOffsetPrimary(taskSplitScrollOffsetPrimary);
+        }
+        mClearAllButton.setSplitSelectScrollOffsetPrimary(clearAllSplitScrollOffsetPrimar);
+    }
+
+    /**
+     * Returns if split placeholder is at the beginning of RecentsView. Always returns {@code false}
+     * if RecentsView is in portrait or RecentsView isn't shown as grid.
+     */
+    private boolean isSplitPlaceholderFirstInGrid() {
+        if (!mActivity.getDeviceProfile().isLandscape || !showAsGrid()
+                || !isSplitSelectionActive()) {
+            return false;
+        }
+        @StagePosition int position = mSplitSelectStateController.getActiveSplitStagePosition();
+        return mIsRtl
+                ? position == STAGE_POSITION_BOTTOM_OR_RIGHT
+                : position == STAGE_POSITION_TOP_OR_LEFT;
+    }
+
+    /**
+     * Returns if split placeholder is at the end of RecentsView. Always returns {@code false} if
+     * RecentsView is in portrait or RecentsView isn't shown as grid.
+     */
+    private boolean isSplitPlaceholderLastInGrid() {
+        if (!mActivity.getDeviceProfile().isLandscape || !showAsGrid()
+                || !isSplitSelectionActive()) {
+            return false;
+        }
+        @StagePosition int position = mSplitSelectStateController.getActiveSplitStagePosition();
+        return mIsRtl
+                ? position == STAGE_POSITION_TOP_OR_LEFT
+                : position == STAGE_POSITION_BOTTOM_OR_RIGHT;
+    }
+
+    /**
+     * Reset scroll offset on children of RecentsView when exiting split select.
+     */
+    public void resetSplitPrimaryScrollOffset() {
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            requireTaskViewAt(i).setSplitScrollOffsetPrimary(0);
+        }
+        mClearAllButton.setSplitSelectScrollOffsetPrimary(0);
+    }
+
+    /**
      * Resets the visuals when exit modal state.
      */
     public void resetModalVisuals() {
@@ -2994,134 +3920,77 @@
         }
     }
 
-    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);
-    }
-
-    public PendingAnimation cancelSplitSelect(boolean animate) {
-        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
-        SplitPositionOption splitOption = splitController.getActiveSplitPositionOption();
-        Rect initialBounds = splitController.getInitialBounds();
-        splitController.resetState();
+        mSplitToast.cancel();
+        if (!taskView.getTask().isDockable) {
+            // Task not split screen supported
+            mSplitUnsupportedToast.show();
+            return;
+        }
+        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 pendingAnim = new PendingAnimation(duration);
-        if (!animate) {
-            resetFromSplitSelectionState();
-            return pendingAnim;
-        }
+        PendingAnimation pendingAnimation = new PendingAnimation(duration);
 
-        addViewInLayout(mSplitHiddenTaskView, mSplitHiddenTaskViewIndex,
-                mSplitHiddenTaskView.getLayoutParams());
-        mSplitHiddenTaskView.setAlpha(0);
-        int[] oldScroll = new int[getChildCount()];
-        getPageScrolls(oldScroll, false,
-                view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
+        int halfDividerSize = getResources()
+                .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+        mOrientationHandler.getFinalSplitPlaceholderBounds(halfDividerSize,
+                mActivity.getDeviceProfile(),
+                mSplitSelectStateController.getActiveSplitStagePosition(), firstTaskEndingBounds,
+                secondTaskEndingBounds);
 
-        int[] newScroll = new int[getChildCount()];
-        getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
+        mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
+        mFirstFloatingTaskView.addAnimation(pendingAnimation,
+                new RectF(firstTaskStartingBounds), firstTaskEndingBounds, mFirstFloatingTaskView,
+                false /*fadeWithThumbnail*/);
 
-        boolean needsCurveUpdates = false;
-        for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) {
-            View child = getChildAt(i);
-            if (child == mSplitHiddenTaskView) {
-                TaskView taskView = (TaskView) child;
-
-                int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitOption,
-                        mActivity.getDeviceProfile());
-                FloatProperty<TaskView> dismissingTaskViewTranslate;
-                Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
-                        taskView.getRight(), taskView.getBottom());
-                int distanceDelta = 0;
-                if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE) {
-                    dismissingTaskViewTranslate = taskView
-                            .getSecondaryDissmissTranslationProperty();
-                    distanceDelta = initialBounds.top - hiddenBounds.top;
-                    taskView.layout(initialBounds.left, hiddenBounds.top, initialBounds.right,
-                            hiddenBounds.bottom);
-                } else {
-                    dismissingTaskViewTranslate = taskView
-                            .getPrimaryDismissTranslationProperty();
-                    distanceDelta = initialBounds.left - hiddenBounds.left;
-                    taskView.layout(hiddenBounds.left, initialBounds.top, hiddenBounds.right,
-                            initialBounds.bottom);
-                    if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE) {
-                        distanceDelta *= -1;
-                    }
-                }
-                pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView,
-                        dismissingTaskViewTranslate,
-                        distanceDelta));
-                pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1));
-            } else {
-                // If insertion is on last index (furthest from clear all), we directly add the view
-                // else we translate all views to the right of insertion index further right,
-                // ignore views to left
-                if (showAsGrid()) {
-                    // TODO(b/186800707) handle more elegantly for grid
-                    continue;
-                }
-                int scrollDiff = newScroll[i] - oldScroll[i];
-                if (scrollDiff != 0) {
-                    FloatProperty translationProperty = child instanceof TaskView
-                            ? ((TaskView) child).getPrimaryDismissTranslationProperty()
-                            : mOrientationHandler.getPrimaryViewTranslate();
-
-                    ResourceProvider rp = DynamicResource.provider(mActivity);
-                    SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END)
-                            .setDampingRatio(
-                                    rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio))
-                            .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness));
-                    pendingAnim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff)
-                            .setDuration(duration), ACCEL, sp);
-                    needsCurveUpdates = true;
-                }
-            }
-        }
-
-        if (needsCurveUpdates) {
-            pendingAnim.addOnFrameCallback(this::updateCurveProperties);
-        }
-
-        pendingAnim.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                // TODO(b/186800707) Figure out how to undo for grid view
-                //  Need to handle cases where dismissed task is
-                //  * Top Row
-                //  * Bottom Row
-                //  * Focused Task
-                updateGridProperties();
-                resetFromSplitSelectionState();
-            }
-        });
-
-        return pendingAnim;
+        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();
     }
 
-    private void resetFromSplitSelectionState() {
-        mSplitHiddenTaskView.setTranslationY(0);
-        if (!showAsGrid()) {
-            // TODO(b/186800707)
+    /** TODO(b/181707736) More gracefully handle exiting split selection state */
+    protected void resetFromSplitSelectionState() {
+        if (mSplitHiddenTaskViewIndex == -1) {
+            return;
+        }
+        if (!mActivity.getDeviceProfile().overviewShowAsGrid) {
             int pageToSnapTo = mCurrentPage;
             if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
                 pageToSnapTo += 1;
@@ -3132,8 +4001,56 @@
         }
         onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
         resetTaskVisuals();
-        mSplitHiddenTaskView = null;
         mSplitHiddenTaskViewIndex = -1;
+        if (mSplitHiddenTaskView != null) {
+            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;
+        }
+    }
+
+    /**
+     * Returns how much additional translation there should be for each of the child TaskViews.
+     * Note that the translation can be its primary or secondary dimension.
+     */
+    public float getSplitSelectTranslation() {
+        int splitPosition = getSplitPlaceholder().getActiveSplitStagePosition();
+        if (!shouldShiftThumbnailsForSplitSelect()) {
+            return 0f;
+        }
+        PagedOrientationHandler orientationHandler = getPagedOrientationHandler();
+        int direction = orientationHandler.getSplitTranslationDirectionFactor(
+                splitPosition, mActivity.getDeviceProfile());
+        return mActivity.getResources().getDimension(R.dimen.split_placeholder_size) * direction;
+    }
+
+    protected void onRotateInSplitSelectionState() {
+        mOrientationHandler.getInitialSplitPlaceholderBounds(mSplitPlaceholderSize,
+                mActivity.getDeviceProfile(),
+                mSplitSelectStateController.getActiveSplitStagePosition(), mTempRect);
+        mTempRectF.set(mTempRect);
+        // TODO(194414938) set correct corner radius
+        mFirstFloatingTaskView.updateOrientationHandler(mOrientationHandler);
+        mFirstFloatingTaskView.update(mTempRectF, /*progress=*/1f, /*windowRadius=*/0f);
+
+        PagedOrientationHandler orientationHandler = getPagedOrientationHandler();
+        Pair<FloatProperty, FloatProperty> taskViewsFloat =
+                orientationHandler.getSplitSelectTaskOffset(
+                        TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+                        mActivity.getDeviceProfile());
+        taskViewsFloat.first.set(this, getSplitSelectTranslation());
+        taskViewsFloat.second.set(this, 0f);
+
+        applySplitPrimaryScrollOffset();
     }
 
     private void updateDeadZoneRects() {
@@ -3150,8 +4067,8 @@
         mTaskViewDeadZoneRect.setEmpty();
         int count = getTaskViewCount();
         if (count > 0) {
-            final View taskView = getTaskViewAt(0);
-            getTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
+            final View taskView = requireTaskViewAt(0);
+            requireTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
             mTaskViewDeadZoneRect.union(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
                     taskView.getBottom());
         }
@@ -3226,12 +4143,16 @@
             anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex),
                     mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation));
             int runningTaskIndex = recentsView.getRunningTaskIndex();
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && runningTaskIndex != -1
-                    && runningTaskIndex != taskIndex) {
-                anim.play(ObjectAnimator.ofFloat(
-                        recentsView.getLiveTileTaskViewSimulator().taskPrimaryTranslation,
-                        AnimatedFloat.VALUE,
-                        primaryTranslation));
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get()
+                    && runningTaskIndex != -1
+                    && runningTaskIndex != taskIndex
+                    && recentsView.getRemoteTargetHandles() != null) {
+                for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
+                    anim.play(ObjectAnimator.ofFloat(
+                            remoteHandle.getTaskViewSimulator().taskPrimaryTranslation,
+                            AnimatedFloat.VALUE,
+                            primaryTranslation));
+                }
             }
 
             int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
@@ -3311,11 +4232,22 @@
         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 -> {
             if (isSuccess) {
+                if (tv.getTaskIds()[1] != -1) {
+                    // TODO(b/194414938): make this part of the animations instead.
+                    TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
+                            mRemoteTargetHandles[0].getTransformParams().getTargetSet().nonApps,
+                            true /*shown*/, (dividerAnimator) -> {
+                                dividerAnimator.start();
+                                dividerAnimator.end();
+                            });
+                }
                 if (ENABLE_QUICKSTEP_LIVE_TILE.get() && tv.isRunningTask()) {
                     finishRecentsAnimation(false /* toRecents */, null);
                     onTaskLaunchAnimationEnd(true /* success */);
@@ -3344,6 +4276,7 @@
     @Override
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
+        updateCurrentTaskActionsVisibility();
         loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
         updateEnabledOverlays();
     }
@@ -3402,39 +4335,79 @@
     }
 
     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) {
+        mSplitSelectStateController.setRecentsAnimationRunning(true);
+        if (recentsAnimationTargets == null || recentsAnimationTargets.apps.length == 0) {
+            return;
+        }
+
+        RemoteTargetGluer gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
+        mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(recentsAnimationTargets);
+        mSplitBoundsConfig = gluer.getStagedSplitBounds();
+        // 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 -> {
+            final TransformParams params = remoteTargetHandle.getTransformParams();
             if (mSyncTransactionApplier != null) {
-                recentsAnimationTargets.addReleaseCheck(mSyncTransactionApplier);
+                params.setSyncTransactionApplier(mSyncTransactionApplier);
+                params.getTargetSet().addReleaseCheck(mSyncTransactionApplier);
             }
-            mLiveTileTaskViewSimulator.setPreview(
-                    recentsAnimationTargets.apps[recentsAnimationTargets.apps.length - 1]);
-            mLiveTileParams.setTargetSet(recentsAnimationTargets);
+
+            TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator();
+            tvs.setOrientationState(mOrientationState);
+            tvs.setDp(mActivity.getDeviceProfile());
+            tvs.recentsViewScale.value = 1;
+        });
+
+        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);
         }
     }
 
-    public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
+    /** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
+    public void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
+        if (mRemoteTargetHandles == null) {
+            return;
+        }
+
+        for (RemoteTargetHandle handle : mRemoteTargetHandles) {
+            consumer.accept(handle);
+        }
+    }
+
+    /**
+     * Finish recents animation.
+     */
+    public void finishRecentsAnimation(boolean toRecents, @Nullable Runnable onFinishComplete) {
         finishRecentsAnimation(toRecents, true /* shouldPip */, onFinishComplete);
     }
 
     public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
-            Runnable onFinishComplete) {
+            @Nullable Runnable onFinishComplete) {
+        // TODO(b/197232424#comment#10) Move this back into onRecentsAnimationComplete(). Maybe?
+        cleanupRemoteTargets();
         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
@@ -3481,6 +4454,7 @@
         // taps on QSB (3) user goes back to Overview and launch the most recent task.
         setCurrentTask(-1);
         mRecentsAnimationController = null;
+        mSplitSelectStateController.setRecentsAnimationRunning(false);
         executeSideTaskLaunchCallback();
     }
 
@@ -3492,45 +4466,55 @@
     }
 
     /**
-     * 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 int computeMinScroll() {
-        if (getTaskViewCount() > 0) {
-            if (mIsRtl) {
-                // If we aren't showing the clear all button, use the rightmost task as the min
-                // scroll.
-                return getScrollForPage(mDisallowScrollToClearAll ? indexOfChild(
-                        getTaskViewAt(getTaskViewCount() - 1)) : indexOfChild(mClearAllButton));
-            } else {
-                TaskView focusedTaskView = showAsGrid() ? getFocusedTaskView() : null;
-                return getScrollForPage(focusedTaskView != null ? indexOfChild(focusedTaskView)
-                        : mTaskViewStartIndex);
-            }
+    protected void updateMinAndMaxScrollX() {
+        super.updateMinAndMaxScrollX();
+        if (DEBUG) {
+            Log.d(TAG, "updateMinAndMaxScrollX - mMinScroll: " + mMinScroll);
+            Log.d(TAG, "updateMinAndMaxScrollX - mMaxScroll: " + mMaxScroll);
         }
-        return super.computeMinScroll();
+    }
+
+    @Override
+    protected int computeMinScroll() {
+        if (getTaskViewCount() <= 0) {
+            return super.computeMinScroll();
+        }
+
+        return getScrollForPage(mIsRtl ? getLastViewIndex() : getFirstViewIndex());
     }
 
     @Override
     protected int computeMaxScroll() {
-        if (getTaskViewCount() > 0) {
-            if (mIsRtl) {
-                TaskView focusedTaskView = showAsGrid() ? getFocusedTaskView() : null;
-                return getScrollForPage(focusedTaskView != null ? indexOfChild(focusedTaskView)
-                        : mTaskViewStartIndex);
-            } else {
-                // If we aren't showing the clear all button, use the leftmost task as the min
-                // scroll.
-                return getScrollForPage(mDisallowScrollToClearAll ? indexOfChild(
-                        getTaskViewAt(getTaskViewCount() - 1)) : indexOfChild(mClearAllButton));
-            }
+        if (getTaskViewCount() <= 0) {
+            return super.computeMaxScroll();
         }
-        return super.computeMaxScroll();
+
+        return getScrollForPage(mIsRtl ? getFirstViewIndex() : getLastViewIndex());
+    }
+
+    private int getFirstViewIndex() {
+        TaskView focusedTaskView = mShowAsGridLastOnLayout ? getFocusedTaskView() : null;
+        return focusedTaskView != null ? indexOfChild(focusedTaskView) : 0;
+    }
+
+    private int getLastViewIndex() {
+        return mDisallowScrollToClearAll
+                ? mShowAsGridLastOnLayout
+                    ? indexOfChild(getLastGridTaskView())
+                    : getTaskViewCount() - 1
+                : indexOfChild(mClearAllButton);
     }
 
     /**
@@ -3557,22 +4541,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 = requireTaskViewAt(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 +4593,7 @@
 
     @Override
     protected int getChildVisibleSize(int index) {
-        final TaskView taskView = getTaskViewAtByAbsoluteIndex(index);
+        final TaskView taskView = getTaskViewAt(index);
         if (taskView == null) {
             return super.getChildVisibleSize(index);
         }
@@ -3628,21 +4628,67 @@
                     overScrollShift, getUndampedOverScrollShift());
         }
         return getScrollForPage(pageIndex) - mOrientationHandler.getPrimaryScroll(this)
-                + overScrollShift;
+                + overScrollShift + getOffsetFromScrollPosition(pageIndex);
     }
 
     /**
-     * Returns how many pixels the task is offset on the currently laid out secondary axis
-     * according to {@link #mGridProgress}.
+     * Returns how many pixels the page is offset from its scroll position.
      */
-    public float getGridTranslationSecondary(int pageIndex) {
-        TaskView taskView = getTaskViewAtByAbsoluteIndex(pageIndex);
+    private int getOffsetFromScrollPosition(int pageIndex) {
+        return getOffsetFromScrollPosition(pageIndex, getTopRowIdArray(), getBottomRowIdArray());
+    }
+
+    private int getOffsetFromScrollPosition(
+            int pageIndex, IntArray topRowIdArray, IntArray bottomRowIdArray) {
+        if (!showAsGrid()) {
+            return 0;
+        }
+
+        TaskView taskView = getTaskViewAt(pageIndex);
         if (taskView == null) {
             return 0;
         }
 
-        return mOrientationHandler.getSecondaryValue(taskView.getGridTranslationX(),
-                taskView.getGridTranslationY());
+        TaskView lastGridTaskView = getLastGridTaskView(topRowIdArray, bottomRowIdArray);
+        if (lastGridTaskView == null) {
+            return 0;
+        }
+
+        if (getScrollForPage(pageIndex) != getScrollForPage(indexOfChild(lastGridTaskView))) {
+            return 0;
+        }
+
+        // Check distance from lastGridTaskView to taskView.
+        int lastGridTaskViewPosition =
+                getPositionInRow(lastGridTaskView, topRowIdArray, bottomRowIdArray);
+        int taskViewPosition = getPositionInRow(taskView, topRowIdArray, bottomRowIdArray);
+        int gridTaskSizeAndSpacing = mLastComputedGridTaskSize.width() + mPageSpacing;
+        int positionDiff = gridTaskSizeAndSpacing * (lastGridTaskViewPosition - taskViewPosition);
+
+        int lastTaskEnd = (mIsRtl
+                ? mLastComputedGridSize.left
+                : mLastComputedGridSize.right)
+                + (mIsRtl ? mPageSpacing : -mPageSpacing);
+        int taskEnd = lastTaskEnd + (mIsRtl ? positionDiff : -positionDiff);
+        int normalTaskEnd = mIsRtl
+                ? mLastComputedGridTaskSize.left
+                : mLastComputedGridTaskSize.right;
+        return taskEnd - normalTaskEnd;
+    }
+
+    private int getPositionInRow(
+            TaskView taskView, IntArray topRowIdArray, IntArray bottomRowIdArray) {
+        int position = topRowIdArray.indexOf(taskView.getTaskViewId());
+        return position != -1 ? position : bottomRowIdArray.indexOf(taskView.getTaskViewId());
+    }
+
+    /**
+     * @return true if the task in on the top of the grid
+     */
+    public boolean isOnGridBottomRow(TaskView taskView) {
+        return showAsGrid()
+                && !mTopRowIdSet.contains(taskView.getTaskViewId())
+                && taskView.getTaskViewId() != mFocusedTaskViewId;
     }
 
     public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
@@ -3677,8 +4723,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++) {
+            requireTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
         }
     }
 
@@ -3692,6 +4738,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,23 +4764,49 @@
             }
             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();
+    public void switchToScreenshot(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas,
+            Runnable onFinishRunnable) {
+        final TaskView taskView = getRunningTaskView();
         if (taskView != null) {
             taskView.setShowScreenshot(true);
-            if (thumbnailData != null) {
-                taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
-            } else {
-                taskView.getThumbnail().refresh();
-            }
+            taskView.refreshThumbnails(thumbnailDatas);
             ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
         } else {
             onFinishRunnable.run();
@@ -3751,10 +4824,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
@@ -3806,7 +4878,7 @@
         mColorTint = tintAmount;
 
         for (int i = 0; i < getTaskViewCount(); i++) {
-            getTaskViewAt(i).setColorTint(mColorTint, mTintingColor);
+            requireTaskViewAt(i).setColorTint(mColorTint, mTintingColor);
         }
 
         Drawable scrimBg = mActivity.getScrimView().getBackground();
@@ -3837,9 +4909,8 @@
                 && mCurrentGestureEndTarget != GestureState.GestureEndTarget.RECENTS;
     }
 
-    public boolean shouldShowOverviewActionsForState(STATE_TYPE state) {
-        return !state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())
-                || getFocusedTaskView() != null;
+    public void cleanupRemoteTargets() {
+        mRemoteTargetHandles = null;
     }
 
     /**
@@ -3876,13 +4947,70 @@
     }
 
     @Override
+    public boolean scrollLeft() {
+        if (!showAsGrid()) {
+            return super.scrollLeft();
+        }
+
+        int targetPage = getNextPage();
+        if (targetPage >= 0) {
+            // Find the next page that is not fully visible.
+            TaskView taskView = getTaskViewAt(targetPage);
+            while ((taskView == null || isTaskViewFullyVisible(taskView)) && targetPage - 1 >= 0) {
+                taskView = getTaskViewAt(--targetPage);
+            }
+            // Target a scroll where targetPage is on left of screen but still fully visible.
+            int lastTaskEnd = (mIsRtl
+                    ? mLastComputedGridSize.left
+                    : mLastComputedGridSize.right)
+                    + (mIsRtl ? mPageSpacing : -mPageSpacing);
+            int normalTaskEnd = mIsRtl
+                    ? mLastComputedGridTaskSize.left
+                    : mLastComputedGridTaskSize.right;
+            int targetScroll = getScrollForPage(targetPage) + normalTaskEnd - lastTaskEnd;
+            // Find a page that is close to targetScroll while not over it.
+            while (targetPage - 1 >= 0
+                    && (mIsRtl
+                    ? getScrollForPage(targetPage - 1) < targetScroll
+                    : getScrollForPage(targetPage - 1) > targetScroll)) {
+                targetPage--;
+            }
+            snapToPage(targetPage);
+            return true;
+        }
+
+        return mAllowOverScroll;
+    }
+
+    @Override
+    public boolean scrollRight() {
+        if (!showAsGrid()) {
+            return super.scrollRight();
+        }
+
+        int targetPage = getNextPage();
+        if (targetPage < getChildCount()) {
+            // Find the next page that is not fully visible.
+            TaskView taskView = getTaskViewAt(targetPage);
+            while ((taskView != null && isTaskViewFullyVisible(taskView))
+                    && targetPage + 1 < getChildCount()) {
+                taskView = getTaskViewAt(++targetPage);
+            }
+            snapToPage(targetPage);
+            return true;
+        }
+        return mAllowOverScroll;
+    }
+
+    @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
         dispatchScrollChanged();
     }
 
     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();
         }
@@ -3890,10 +5018,13 @@
 
     private static class PinnedStackAnimationListener<T extends BaseActivity> extends
             IPipAnimationListener.Stub {
+        @Nullable
         private T mActivity;
+        @Nullable
         private RecentsView mRecentsView;
 
-        public void setActivityAndRecentsView(T activity, RecentsView recentsView) {
+        public void setActivityAndRecentsView(@Nullable T activity,
+                @Nullable RecentsView recentsView) {
             mActivity = activity;
             mRecentsView = recentsView;
         }
@@ -3944,4 +5075,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..04a5761 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,27 @@
                 }
             };
 
-    private SplitSelectStateController mSplitController;
-    private IconView mIcon;
+    @Nullable
+    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..853a023 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -17,6 +17,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;
@@ -32,25 +34,27 @@
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver.OnScrollChangedListener;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 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.
@@ -65,8 +69,10 @@
 
     private BaseDraggingActivity mActivity;
     private TextView mTaskName;
+    @Nullable
     private AnimatorSet mOpenCloseAnimator;
     private TaskView mTaskView;
+    private TaskIdAttributeContainer mTaskContainer;
     private LinearLayout mOptionLayout;
 
     public TaskMenuView(Context context, AttributeSet attrs) {
@@ -131,7 +137,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 +146,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 +154,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 +188,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;
         }
@@ -187,27 +213,27 @@
 
     @Override
     public void onScrollChanged() {
-        RecentsView rv = mTaskView.getRecentsView();
+        RecentsView rv = mActivity.getOverviewPanel();
         setPosition(mTaskView.getX() - rv.getScrollX(), mTaskView.getY() - rv.getScrollY(),
                 rv.getOverScrollShift());
     }
 
     /** @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,10 +245,8 @@
         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()) {
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
                 RecentsView recentsView = mTaskView.getRecentsView();
                 recentsView.switchToScreenshot(null,
                         () -> recentsView.finishRecentsAnimation(true /* toRecents */,
@@ -235,23 +259,27 @@
         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);
+        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 +288,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 +312,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() {
@@ -317,13 +345,4 @@
         return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect);
     }
 
-    public View findMenuItemByText(String text) {
-        for (int i = mOptionLayout.getChildCount() - 1; i >= 0; --i) {
-            final ViewGroup menuOptionView = (ViewGroup) mOptionLayout.getChildAt(i);
-            if (text.equals(menuOptionView.<TextView>findViewById(R.id.text).getText())) {
-                return menuOptionView;
-            }
-        }
-        return null;
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
new file mode 100644
index 0000000..06a5793
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -0,0 +1,344 @@
+/*
+ * 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.views
+
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.content.Context
+import android.graphics.Rect
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.RectShape
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.BaseDraggingActivity
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.InsettableFrameLayout
+import com.android.launcher3.R
+import com.android.launcher3.popup.ArrowPopup
+import com.android.launcher3.popup.RoundedArrowDrawable
+import com.android.launcher3.popup.SystemShortcut
+import com.android.launcher3.util.Themes
+import com.android.quickstep.KtR
+import com.android.quickstep.TaskOverlayFactory
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
+
+class TaskMenuViewWithArrow<T : BaseDraggingActivity> : ArrowPopup<T> {
+    companion object {
+        const val TAG = "TaskMenuViewWithArrow"
+
+        fun showForTask(
+            taskContainer: TaskIdAttributeContainer,
+            alignSecondRow: Boolean = false
+        ): Boolean {
+            val activity = BaseDraggingActivity
+                .fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
+            val taskMenuViewWithArrow = activity.layoutInflater
+                .inflate(
+                    KtR.layout.task_menu_with_arrow,
+                    activity.dragLayer,
+                    false
+                ) as TaskMenuViewWithArrow<*>
+
+            return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignSecondRow)
+        }
+    }
+
+    constructor(context: Context) : super(context)
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
+    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
+        context,
+        attrs,
+        defStyleAttr
+    )
+
+    init {
+        clipToOutline = true
+
+        shouldScaleArrow = true
+        // This synchronizes the arrow and menu to open at the same time
+        OPEN_CHILD_FADE_START_DELAY = OPEN_FADE_START_DELAY
+        OPEN_CHILD_FADE_DURATION = OPEN_FADE_DURATION
+        CLOSE_FADE_START_DELAY = CLOSE_CHILD_FADE_START_DELAY
+        CLOSE_FADE_DURATION = CLOSE_CHILD_FADE_DURATION
+    }
+
+    private var alignSecondRow: Boolean = false
+    private val extraSpaceForSecondRowAlignment: Int
+        get() = if (alignSecondRow) optionMeasuredHeight else 0
+    private val menuWidth = context.resources.getDimensionPixelSize(R.dimen.task_menu_width_grid)
+
+    private lateinit var taskView: TaskView
+    private lateinit var optionLayout: LinearLayout
+    private lateinit var taskContainer: TaskIdAttributeContainer
+
+    private var optionMeasuredHeight = 0
+    private val arrowHorizontalPadding: Int
+        get() = if (taskView.isFocusedTask)
+            resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding)
+        else
+            0
+
+    private var iconView: IconView? = null
+    private var scrim: View? = null
+    private val scrimAlpha = 0.8f
+
+    override fun isOfType(type: Int): Boolean = type and TYPE_TASK_MENU != 0
+
+    override fun getTargetObjectLocation(outPos: Rect?) {
+        popupContainer.getDescendantRectRelativeToSelf(taskContainer.iconView, outPos)
+    }
+
+    override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
+        if (ev?.action == MotionEvent.ACTION_DOWN) {
+            if (!popupContainer.isEventOverView(this, ev)) {
+                close(true)
+                return true
+            }
+        }
+        return false
+    }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        optionLayout = findViewById(KtR.id.menu_option_layout)
+    }
+
+    private fun populateAndShowForTask(
+        taskContainer: TaskIdAttributeContainer,
+        alignSecondRow: Boolean
+    ): Boolean {
+        if (isAttachedToWindow) {
+            return false
+        }
+
+        taskView = taskContainer.taskView
+        this.taskContainer = taskContainer
+        this.alignSecondRow = alignSecondRow
+        if (!populateMenu()) return false
+        addScrim()
+        show()
+        return true
+    }
+
+    private fun addScrim() {
+        scrim = View(context).apply {
+            layoutParams = FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT
+            )
+            setBackgroundColor(Themes.getAttrColor(context, R.attr.overviewScrimColor))
+            alpha = 0f
+        }
+        popupContainer.addView(scrim)
+    }
+
+    /** @return true if successfully able to populate task view menu, false otherwise
+     */
+    private fun populateMenu(): Boolean {
+        // Icon may not be loaded
+        if (taskContainer.task.icon == null) return false
+
+        addMenuOptions()
+        return true
+    }
+
+    private fun addMenuOptions() {
+        // Add the options
+        TaskOverlayFactory
+            .getEnabledShortcuts(taskView, mActivityContext.deviceProfile, taskContainer)
+            .forEach { this.addMenuOption(it) }
+
+        // Add the spaces between items
+        val divider = ShapeDrawable(RectShape())
+        divider.paint.color = resources.getColor(android.R.color.transparent)
+        val dividerSpacing = resources.getDimension(KtR.dimen.task_menu_spacing).toInt()
+        optionLayout.showDividers = SHOW_DIVIDER_MIDDLE
+
+        // Set the orientation, which makes the menu show
+        val recentsView: RecentsView<*, *> = mActivityContext.getOverviewPanel()
+        val orientationHandler = recentsView.pagedOrientationHandler
+        val deviceProfile: DeviceProfile = mActivityContext.deviceProfile
+        orientationHandler.setTaskOptionsMenuLayoutOrientation(
+            deviceProfile,
+            optionLayout,
+            dividerSpacing,
+            divider
+        )
+    }
+
+    private fun addMenuOption(menuOption: SystemShortcut<*>) {
+        val menuOptionView = mActivityContext.layoutInflater.inflate(
+            KtR.layout.task_view_menu_option, this, false
+        ) as LinearLayout
+        menuOption.setIconAndLabelFor(
+            menuOptionView.findViewById(R.id.icon),
+            menuOptionView.findViewById(R.id.text)
+        )
+        val lp = menuOptionView.layoutParams as LayoutParams
+        lp.width = menuWidth
+        menuOptionView.setOnClickListener { view: View? -> menuOption.onClick(view) }
+        optionLayout.addView(menuOptionView)
+    }
+
+    override fun assignMarginsAndBackgrounds(viewGroup: ViewGroup) {
+        assignMarginsAndBackgrounds(
+            this,
+            Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface)
+        )
+    }
+
+    override fun onCreateOpenAnimation(anim: AnimatorSet) {
+        scrim?.let {
+            anim.play(
+                ObjectAnimator.ofFloat(it, View.ALPHA, 0f, scrimAlpha)
+                    .setDuration(OPEN_DURATION.toLong())
+            )
+        }
+    }
+
+    override fun onCreateCloseAnimation(anim: AnimatorSet) {
+        scrim?.let {
+            anim.play(
+                ObjectAnimator.ofFloat(it, View.ALPHA, scrimAlpha, 0f)
+                    .setDuration(CLOSE_DURATION.toLong())
+            )
+        }
+    }
+
+    override fun closeComplete() {
+        super.closeComplete()
+        popupContainer.removeView(scrim)
+        popupContainer.removeView(iconView)
+    }
+
+    /**
+     * Copy the iconView from taskView to dragLayer so it can stay on top of the scrim.
+     * It needs to be called after [getTargetObjectLocation] because [mTempRect] needs to be
+     * populated.
+     */
+    private fun copyIconToDragLayer(insets: Rect) {
+        iconView = IconView(context).apply {
+            layoutParams = FrameLayout.LayoutParams(
+                taskContainer.iconView.width,
+                taskContainer.iconView.height
+            )
+            x = mTempRect.left.toFloat() - insets.left
+            y = mTempRect.top.toFloat() - insets.top
+            drawable = taskContainer.iconView.drawable
+            setDrawableSize(
+                taskContainer.iconView.drawableWidth,
+                taskContainer.iconView.drawableHeight
+            )
+        }
+
+        popupContainer.addView(iconView)
+    }
+
+    /**
+     * Orients this container to the left or right of the given icon, aligning with the first option
+     * or second.
+     *
+     * These are the preferred orientations, in order (RTL prefers right-aligned over left):
+     * - Right and first option aligned
+     * - Right and second option aligned
+     * - Left and first option aligned
+     * - Left and second option aligned
+     *
+     * So we always align right if there is enough horizontal space
+     */
+    override fun orientAboutObject() {
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
+        // Needed for offsets later
+        optionMeasuredHeight = optionLayout.getChildAt(0).measuredHeight
+        val extraHorizontalSpace = (mArrowHeight + mArrowOffsetVertical + arrowHorizontalPadding)
+
+        val widthWithArrow = measuredWidth + paddingLeft + paddingRight + extraHorizontalSpace
+        getTargetObjectLocation(mTempRect)
+        val dragLayer: InsettableFrameLayout = popupContainer
+        val insets = dragLayer.insets
+
+        copyIconToDragLayer(insets)
+
+        // Put this menu to the right of the icon if there is space,
+        // which means the arrow is left aligned with the menu
+        val rightAlignedMenuStartX = mTempRect.left - widthWithArrow
+        val leftAlignedMenuStartX = mTempRect.right + extraHorizontalSpace
+        mIsLeftAligned = if (mIsRtl) {
+            rightAlignedMenuStartX + insets.left < 0
+        } else {
+            leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left <
+                    dragLayer.width - insets.right
+        }
+
+        var menuStartX = if (mIsLeftAligned) leftAlignedMenuStartX else rightAlignedMenuStartX
+
+        // Offset y so that the arrow and row are center-aligned with the original icon.
+        val iconHeight = mTempRect.height()
+        val yOffset = (optionMeasuredHeight - iconHeight) / 2
+        var menuStartY = mTempRect.top - yOffset - extraSpaceForSecondRowAlignment
+
+        // Insets are added later, so subtract them now.
+        menuStartX -= insets.left
+        menuStartY -= insets.top
+
+        x = menuStartX.toFloat()
+        y = menuStartY.toFloat()
+
+        val lp = layoutParams as FrameLayout.LayoutParams
+        val arrowLp = mArrow.layoutParams as FrameLayout.LayoutParams
+        lp.gravity = Gravity.TOP
+        arrowLp.gravity = lp.gravity
+    }
+
+    override fun addArrow() {
+        popupContainer.addView(mArrow)
+        mArrow.x = getArrowX()
+        mArrow.y = y + (optionMeasuredHeight / 2) - (mArrowHeight / 2) +
+                extraSpaceForSecondRowAlignment
+
+        updateArrowColor()
+
+        // This is inverted (x = height, y = width) because the arrow is rotated
+        mArrow.pivotX = if (mIsLeftAligned) 0f else mArrowHeight.toFloat()
+        mArrow.pivotY = 0f
+    }
+
+    private fun getArrowX(): Float {
+        return if (mIsLeftAligned)
+            x - mArrowHeight
+        else
+            x + measuredWidth + mArrowOffsetVertical
+    }
+
+    override fun updateArrowColor() {
+        mArrow.background = RoundedArrowDrawable(
+            mArrowWidth.toFloat(),
+            mArrowHeight.toFloat(),
+            mArrowPointRadius.toFloat(),
+            mIsLeftAligned,
+            mArrowColor
+        )
+        elevation = mElevation
+        mArrow.elevation = mElevation
+    }
+
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index c70596d..d833877 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -44,27 +44,24 @@
 import android.view.Surface;
 import android.view.View;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.core.graphics.ColorUtils;
 
 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);
 
@@ -82,6 +79,7 @@
             };
 
     private final BaseActivity mActivity;
+    @Nullable
     private TaskOverlay mOverlay;
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -94,25 +92,27 @@
     private final PreviewPositionHelper mPreviewPositionHelper = new PreviewPositionHelper();
     private TaskView.FullscreenDrawParams mFullscreenParams;
 
+    @Nullable
     private Task mTask;
+    @Nullable
     private ThumbnailData mThumbnailData;
+    @Nullable
     protected BitmapShader mBitmapShader;
 
     /** How much this thumbnail is dimmed, 0 not dimmed at all, 1 totally dimmed. */
     private float mDimAlpha = 0f;
 
     private boolean mOverlayEnabled;
-    private OverviewScreenshotActions mOverviewScreenshotActionsPlugin;
 
     public TaskThumbnailView(Context context) {
         this(context, null);
     }
 
-    public TaskThumbnailView(Context context, AttributeSet attrs) {
+    public TaskThumbnailView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public TaskThumbnailView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mPaint.setFilterBitmap(true);
         mBackgroundPaint.setColor(Color.WHITE);
@@ -146,37 +146,47 @@
      *                   upon swipe up so that a usable screenshot is accessible immediately when
      *                   recents animation needs to be finished / cancelled.
      */
-    public void setThumbnail(Task task, ThumbnailData thumbnailData, boolean refreshNow) {
+    public void setThumbnail(@Nullable Task task, @Nullable ThumbnailData thumbnailData,
+            boolean refreshNow) {
         mTask = task;
+        boolean thumbnailWasNull = mThumbnailData == null;
         mThumbnailData =
                 (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
         if (refreshNow) {
-            refresh();
+            refresh(thumbnailWasNull && mThumbnailData != null);
         }
     }
 
     /** See {@link #setThumbnail(Task, ThumbnailData, boolean)} */
-    public void setThumbnail(Task task, ThumbnailData thumbnailData) {
+    public void setThumbnail(@Nullable Task task, @Nullable ThumbnailData thumbnailData) {
         setThumbnail(task, thumbnailData, true /* refreshNow */);
     }
 
     /** Updates the shader, paint, matrix to redraw. */
     public void refresh() {
+        refresh(false);
+    }
+
+    /**
+     * Updates the shader, paint, matrix to redraw.
+     * @param shouldRefreshOverlay whether to re-initialize overlay
+     */
+    private void refresh(boolean shouldRefreshOverlay) {
         if (mThumbnailData != null && mThumbnailData.thumbnail != null) {
             Bitmap bm = mThumbnailData.thumbnail;
             bm.prepareToDraw();
             mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
             mPaint.setShader(mBitmapShader);
             updateThumbnailMatrix();
+            if (shouldRefreshOverlay) {
+                refreshOverlay();
+            }
         } else {
             mBitmapShader = null;
             mThumbnailData = null;
             mPaint.setShader(null);
             getTaskOverlay().reset();
         }
-        if (mOverviewScreenshotActionsPlugin != null) {
-            mOverviewScreenshotActionsPlugin.setupActions(getTaskView(), getThumbnail(), mActivity);
-        }
         updateThumbnailPaintFilter();
     }
 
@@ -214,10 +224,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 +237,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 +278,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;
     }
@@ -325,14 +307,15 @@
             }
         }
 
-        // Draw the background in all cases, except when the thumbnail data is opaque
+        // Always draw the background since the snapshots might be translucent or partially empty
+        // (For example, tasks been reparented out of dismissing split root when drag-to-dismiss
+        // split screen).
+        canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
+
         final boolean drawBackgroundOnly = mTask == null || mTask.isLocked || mBitmapShader == null
                 || mThumbnailData == null;
-        if (drawBackgroundOnly || mThumbnailData.isTranslucent) {
-            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
-            if (drawBackgroundOnly) {
-                return;
-            }
+        if (drawBackgroundOnly) {
+            return;
         }
 
         canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
@@ -408,6 +391,10 @@
         return Utilities.makeColorTintingColorFilter(mDimColor, dimAmount);
     }
 
+    /**
+     * Returns current thumbnail or null if none is set.
+     */
+    @Nullable
     public Bitmap getThumbnail() {
         if (mThumbnailData == null) {
             return null;
@@ -431,7 +418,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 +440,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 +461,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) {
@@ -479,15 +479,36 @@
                 float availableHeight = surfaceHeight
                         - (thumbnailClipHint.top + thumbnailClipHint.bottom);
 
-                if (isRotated) {
-                    float canvasAspect = canvasWidth / (float) canvasHeight;
-                    float availableAspect = availableHeight / availableWidth;
+                float canvasAspect = canvasWidth / (float) canvasHeight;
+                float availableAspect = isRotated
+                        ? availableHeight / availableWidth
+                        : availableWidth / availableHeight;
+                boolean isAspectLargelyDifferent = Utilities.isRelativePercentDifferenceGreaterThan(
+                        canvasAspect, availableAspect, 0.1f);
+                if (isRotated && isAspectLargelyDifferent) {
                     // Do not rotate thumbnail if it would not improve fit
-                    if (Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
-                            availableAspect, 0.1f)) {
-                        isRotated = false;
-                        isOrientationDifferent = false;
+                    isRotated = false;
+                    isOrientationDifferent = false;
+                }
+
+                if (isAspectLargelyDifferent) {
+                    // Crop letterbox insets if insets isn't already clipped
+                    if (!TaskView.clipLeft(dp)) {
+                        thumbnailClipHint.left = thumbnailData.letterboxInsets.left;
                     }
+                    if (!TaskView.clipRight(dp)) {
+                        thumbnailClipHint.right = thumbnailData.letterboxInsets.right;
+                    }
+                    if (!TaskView.clipTop(dp)) {
+                        thumbnailClipHint.top = thumbnailData.letterboxInsets.top;
+                    }
+                    if (!TaskView.clipBottom(dp)) {
+                        thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom;
+                    }
+                    availableWidth = surfaceWidth
+                            - (thumbnailClipHint.left + thumbnailClipHint.right);
+                    availableHeight = surfaceHeight
+                            - (thumbnailClipHint.top + thumbnailClipHint.bottom);
                 }
 
                 final float targetW, targetH;
@@ -498,30 +519,25 @@
                     targetW = canvasWidth;
                     targetH = canvasHeight;
                 }
-                float canvasAspect = targetW / targetH;
+                float targetAspect = targetW / targetH;
 
                 // Update the clipHint such that
                 //   > the final clipped position has same aspect ratio as requested by canvas
-                //   > the clipped region is within the task insets if possible
-                //   > the clipped region is not scaled up when drawing. If that is not possible
-                //     while staying within the taskInsets, move outside the insets.
+                //   > first fit the width and crop the extra height
+                //   > if that will leave empty space, fit the height and crop the width instead
                 float croppedWidth = availableWidth;
-                if (croppedWidth < targetW) {
-                    croppedWidth = Math.min(targetW, surfaceWidth);
-                }
-
-                float croppedHeight = croppedWidth / canvasAspect;
+                float croppedHeight = croppedWidth / targetAspect;
                 if (croppedHeight > availableHeight) {
                     croppedHeight = availableHeight;
                     if (croppedHeight < targetH) {
                         croppedHeight = Math.min(targetH, surfaceHeight);
                     }
-                    croppedWidth = croppedHeight * canvasAspect;
+                    croppedWidth = croppedHeight * targetAspect;
 
                     // One last check in case the task aspect radio messed up something
                     if (croppedWidth > surfaceWidth) {
                         croppedWidth = surfaceWidth;
-                        croppedHeight = croppedWidth / canvasAspect;
+                        croppedHeight = croppedWidth / targetAspect;
                     }
                 }
 
@@ -565,7 +581,7 @@
                         -thumbnailClipHint.left * scale,
                         -thumbnailClipHint.top * scale);
             } else {
-                setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds);
+                setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds, dp);
             }
 
             final float widthWithInsets;
@@ -610,7 +626,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 +652,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..d046fef 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,8 @@
 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_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
@@ -46,6 +39,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.annotation.IdRes;
 import android.app.ActivityOptions;
 import android.content.Context;
 import android.content.Intent;
@@ -58,7 +52,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;
@@ -70,6 +63,7 @@
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
@@ -77,7 +71,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 +80,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;
@@ -101,16 +96,22 @@
 import com.android.quickstep.util.CancellableTask;
 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.recents.model.ThumbnailData;
 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.HashMap;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.stream.Stream;
 
 /**
  * A task in the Recents view.
@@ -118,6 +119,7 @@
 public class TaskView extends FrameLayout implements Reusable {
 
     private static final String TAG = TaskView.class.getSimpleName();
+    private static final boolean DEBUG = false;
 
     public static final int FLAG_UPDATE_ICON = 1;
     public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
@@ -132,20 +134,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 +178,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 +187,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 +308,71 @@
                 }
             };
 
-    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;
-    private final DigitalWellBeingToast mDigitalWellBeingToast;
+    @Nullable
+    protected Task mTask;
+    protected TaskThumbnailView mSnapshotView;
+    protected IconView mIconView;
+    protected final DigitalWellBeingToast mDigitalWellBeingToast;
     private float mFullscreenProgress;
     private float mGridProgress;
-    private float mFullscreenScale = 1;
-    private final FullscreenDrawParams mCurrentFullscreenParams;
-    private final StatefulActivity mActivity;
+    private float mNonGridScale = 1;
+    private float mDismissScale = 1;
+    protected final FullscreenDrawParams mCurrentFullscreenParams;
+    protected final StatefulActivity mActivity;
 
     // Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
     private float mDismissTranslationX;
@@ -356,37 +382,47 @@
     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;
 
+    @Nullable
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
     private float mFocusTransitionProgress = 1;
     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
+    @Nullable
     private CancellableTask mThumbnailLoadRequest;
+    @Nullable
     private CancellableTask mIconLoadRequest;
 
     private boolean mEndQuickswitchCuj;
 
-    private View mContextualChipWrapper;
     private final float[] mIconCenterCoords = new float[2];
-    private final float[] mChipCenterCoords = new float[2];
 
     private boolean mIsClickableAsLiveTile = true;
 
@@ -394,11 +430,11 @@
         this(context, null);
     }
 
-    public TaskView(Context context, AttributeSet attrs) {
+    public TaskView(Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public TaskView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mActivity = StatefulActivity.fromContext(context);
         setOnClickListener(this::onClick);
@@ -411,19 +447,36 @@
         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();
-        ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
+        return getItemInfo(mTask);
+    }
+
+    protected WorkspaceItemInfo getItemInfo(@Nullable Task task) {
         WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
         stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
         stubInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
+        if (task == null) {
+            return stubInfo;
+        }
+
+        ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
         stubInfo.user = componentKey.user;
         stubInfo.intent = new Intent().setComponent(componentKey.componentName);
         stubInfo.title = task.title;
-        stubInfo.screenId = getRecentsView().indexOfChild(this);
+        if (getRecentsView() != null) {
+            stubInfo.screenId = getRecentsView().indexOfChild(this);
+        }
         return stubInfo;
     }
 
@@ -441,45 +494,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 +527,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 +545,55 @@
     public void bind(Task task, RecentsOrientedState orientedState) {
         cancelPendingLoadTasks();
         mTask = task;
+        mTaskIdContainer[0] = mTask.key.id;
+        mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView,
+                mIconView, STAGE_POSITION_UNDEFINED);
         mSnapshotView.bind(task);
         setOrientationState(orientedState);
     }
 
+    public TaskIdAttributeContainer[] getTaskIdAttributeContainers() {
+        return mTaskIdAttributeContainer;
+    }
+
+    @Nullable
     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 boolean containsMultipleTasks() {
+        return mTaskIdContainer[1] != -1;
     }
 
     public TaskThumbnailView getThumbnail() {
         return mSnapshotView;
     }
 
+    void refreshThumbnails(@Nullable HashMap<Integer, ThumbnailData> thumbnailDatas) {
+        if (mTask != null && thumbnailDatas != null) {
+            final ThumbnailData thumbnailData = thumbnailDatas.get(mTask.key.id);
+            if (thumbnailData != null) {
+                mSnapshotView.setThumbnail(mTask, thumbnailData);
+                return;
+            }
+        }
+
+        mSnapshotView.refresh();
+    }
+
+    /** 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,7 +602,12 @@
         if (getTask() == null) {
             return;
         }
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
+        if (confirmSecondSplitSelectApp()) {
+            return;
+        }
+        RecentsView recentsView = getRecentsView();
+        RemoteTargetHandle[] remoteTargetHandles = recentsView.mRemoteTargetHandles;
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask() && remoteTargetHandles != null) {
             if (!mIsClickableAsLiveTile) {
                 return;
             }
@@ -553,12 +620,28 @@
             }
 
             mIsClickableAsLiveTile = false;
-            RecentsView recentsView = getRecentsView();
-            final RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
+            RemoteAnimationTargets targets;
+            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);
+                targets = new RemoteAnimationTargets(apps, wallpapers,
+                        topLeftParams.getTargetSet().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,49 +653,61 @@
                     recentsView.getDepthController());
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
-                public void onAnimationStart(Animator animator) {
-                    recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
+                public void onAnimationStart(Animator animation) {
+                    recentsView.runActionOnRemoteHandles(
+                            (Consumer<RemoteTargetHandle>) remoteTargetHandle ->
+                                    remoteTargetHandle
+                                            .getTaskViewSimulator()
+                                            .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 = getRecentsView().isSplitSelectionActive();
+        if (isSelectingSecondSplitApp) {
             getRecentsView().confirmSplitSelect(this);
-        } else {
-            launchTaskAnimated();
         }
+        return isSelectingSecondSplitApp;
     }
 
     /**
      * Starts the task associated with this view and animates the startup.
      * @return CompletionStage to indicate the animation completion or null if the launch failed.
      */
+    @Nullable
     public RunnableList launchTaskAnimated() {
         if (mTask != null) {
             TestLogging.recordEvent(
                     TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
             ActivityOptionsWrapper opts =  mActivity.getActivityLaunchOptions(this, null);
+            opts.options.setLaunchDisplayId(getRootViewDisplayId());
             if (ActivityManagerWrapper.getInstance()
                     .startActivityFromRecents(mTask.key, opts.options)) {
-                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && getRecentsView().getRunningTaskId() != -1) {
+                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 +738,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 +792,7 @@
             if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
                 mIconLoadRequest = iconCache.updateIconInBackground(mTask,
                         (task) -> {
-                            setIcon(task.icon);
+                            setIcon(mIconView, task.icon);
                             mDigitalWellBeingToast.initialize(mTask);
                         });
             }
@@ -708,16 +804,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 +824,60 @@
         }
     }
 
-    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) {
+        TaskIdAttributeContainer menuContainer =
+                mTaskIdAttributeContainer[iconView == mIconView ? 0 : 1];
+        if (mActivity.getDeviceProfile().overviewShowAsGrid) {
+            boolean alignSecondRow = getRecentsView().isOnGridBottomRow(menuContainer.getTaskView())
+                    && mActivity.getDeviceProfile().isLandscape;
+            return TaskMenuViewWithArrow.Companion.showForTask(menuContainer, alignSecondRow);
+        } else {
+            return TaskMenuView.showForTask(menuContainer);
+        }
+    }
+
+    protected void setIcon(IconView iconView, @Nullable 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,45 +887,35 @@
         LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
         DeviceProfile deviceProfile = mActivity.getDeviceProfile();
         snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
-        int taskIconMargin = deviceProfile.overviewTaskMarginPx;
+        boolean isGridTask = isGridTask();
         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) {
+    /**
+     * Returns whether the task is part of overview grid and not being focused.
+     */
+    public boolean isGridTask() {
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        return deviceProfile.overviewShowAsGrid && !isFocusedTask();
+    }
+
+    protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
         if (invert) {
             progress = 1 - progress;
         }
@@ -814,11 +926,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 +962,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 +991,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 +999,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 +1023,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 +1055,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 +1082,11 @@
         mSplitSelectTranslationY = y;
         applyTranslationY();
     }
+
+    public void setSplitScrollOffsetPrimary(float splitSelectScrollOffsetPrimary) {
+        mSplitSelectScrollOffsetPrimary = splitSelectScrollOffsetPrimary;
+    }
+
     private void setDismissTranslationX(float x) {
         mDismissTranslationX = x;
         applyTranslationX();
@@ -1030,23 +1117,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 +1145,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 +1168,7 @@
     public float getSizeAdjustment(boolean fullscreenEnabled) {
         float sizeAdjustment = 1;
         if (fullscreenEnabled) {
-            sizeAdjustment *= mFullscreenScale;
+            sizeAdjustment *= mNonGridScale;
         }
         return sizeAdjustment;
     }
@@ -1100,7 +1180,7 @@
 
     private void applyTranslationX() {
         setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
-                + mSplitSelectTranslationX + getPersistentTranslationX());
+                + mSplitSelectTranslationX + mGridEndTranslationX + getPersistentTranslationX());
     }
 
     private void applyTranslationY() {
@@ -1113,9 +1193,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 +1202,7 @@
      */
     public float getPersistentTranslationY() {
         return mBoxTranslationY
-                + getFullscreenTrans(mFullscreenTranslationY)
-                + getNonFullscreenTrans(mNonFullscreenTranslationY)
+                + getNonGridTrans(mNonGridTranslationY)
                 + getGridTrans(mGridTranslationY);
     }
 
@@ -1159,24 +1236,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,9 +1309,14 @@
                         getContext().getText(R.string.accessibility_close)));
 
         final Context context = getContext();
-        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
-                mActivity.getDeviceProfile())) {
-            info.addAction(s.createAccessibilityAction(context));
+        for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) {
+            if (taskContainer == null) {
+                continue;
+            }
+            for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
+                    mActivity.getDeviceProfile(), taskContainer)) {
+                info.addAction(s.createAccessibilityAction(context));
+            }
         }
 
         if (mDigitalWellBeingToast.hasLimit()) {
@@ -1275,11 +1347,16 @@
             return true;
         }
 
-        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
-                mActivity.getDeviceProfile())) {
-            if (s.hasHandlerForAction(action)) {
-                s.onClick(this);
-                return true;
+        for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) {
+            if (taskContainer == null) {
+                continue;
+            }
+            for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
+                    mActivity.getDeviceProfile(), taskContainer)) {
+                if (s.hasHandlerForAction(action)) {
+                    s.onClick(this);
+                    return true;
+                }
             }
         }
 
@@ -1312,37 +1389,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();
     }
 
+    protected 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 +1418,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 +1469,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 +1509,7 @@
 
     public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
         AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
-        getRecentsView().initiateSplitSelect(this, splitPositionOption);
+        getRecentsView().initiateSplitSelect(this, splitPositionOption.stagePosition);
     }
 
     /**
@@ -1479,6 +1521,11 @@
         mDigitalWellBeingToast.setBannerColorTint(tintColor, amount);
     }
 
+
+    private int getRootViewDisplayId() {
+        return getRootView().getDisplay().getDisplayId();
+    }
+
     /**
      * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
      */
@@ -1495,7 +1542,7 @@
 
         public FullscreenDrawParams(Context context) {
             mCornerRadius = TaskCornerRadius.get(context);
-            mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
+            mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context);
 
             mCurrentDrawnCornerRadius = mCornerRadius;
         }
@@ -1503,20 +1550,25 @@
         /**
          * Sets the progress in range [0, 1]
          */
-        public void setProgress(float fullscreenProgress, float parentScale, int previewWidth,
-                DeviceProfile dp, PreviewPositionHelper pph) {
+        public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale,
+                int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) {
             mFullscreenProgress = fullscreenProgress;
-            RectF insets = pph.getInsetsToDrawInFullscreen();
+
+            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.
@@ -1524,6 +1576,57 @@
                 mScale = previewWidth / (previewWidth + currentInsetsLeft + currentInsetsRight);
             }
         }
+    }
 
+    public class TaskIdAttributeContainer {
+        private final TaskThumbnailView mThumbnailView;
+        private final Task mTask;
+        private final IconView mIconView;
+        /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
+        private @SplitConfigurationOptions.StagePosition int mStagePosition;
+        @IdRes
+        private final int mA11yNodeId;
+
+        public TaskIdAttributeContainer(Task task, TaskThumbnailView thumbnailView,
+                IconView iconView, int stagePosition) {
+            this.mTask = task;
+            this.mThumbnailView = thumbnailView;
+            this.mIconView = iconView;
+            this.mStagePosition = stagePosition;
+            this.mA11yNodeId = (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) ?
+                    R.id.split_bottomRight_appInfo : R.id.split_topLeft_appInfo;
+        }
+
+        public TaskThumbnailView getThumbnailView() {
+            return mThumbnailView;
+        }
+
+        public Task getTask() {
+            return mTask;
+        }
+
+        public WorkspaceItemInfo getItemInfo() {
+            return TaskView.this.getItemInfo(mTask);
+        }
+
+        public TaskView getTaskView() {
+            return TaskView.this;
+        }
+
+        public IconView getIconView() {
+            return mIconView;
+        }
+
+        public int getStagePosition() {
+            return mStagePosition;
+        }
+
+        void setStagePosition(@SplitConfigurationOptions.StagePosition int stagePosition) {
+            this.mStagePosition = stagePosition;
+        }
+
+        public int getA11yNodeId() {
+            return mA11yNodeId;
+        }
     }
 }
diff --git a/quickstep/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/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
new file mode 100644
index 0000000..ba1a60d
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -0,0 +1,159 @@
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
+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.TaskbarNavButtonController.SCREEN_PIN_LONG_PRESS_THRESHOLD;
+import static com.android.quickstep.OverviewCommandHelper.TYPE_HOME;
+import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TouchInteractionService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TaskbarNavButtonControllerTest {
+
+    private final static int DISPLAY_ID = 2;
+
+    @Mock
+    SystemUiProxy mockSystemUiProxy;
+    @Mock
+    TouchInteractionService mockService;
+    @Mock
+    OverviewCommandHelper mockCommandHelper;
+    @Mock
+    Handler mockHandler;
+
+    private TaskbarNavButtonController mNavButtonController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mockService.getDisplayId()).thenReturn(DISPLAY_ID);
+        when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper);
+        mNavButtonController = new TaskbarNavButtonController(mockService,
+                mockSystemUiProxy, mockHandler);
+    }
+
+    @Test
+    public void testPressBack() {
+        mNavButtonController.onButtonClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(1)).onBackPressed();
+    }
+
+    @Test
+    public void testPressImeSwitcher() {
+        mNavButtonController.onButtonClick(BUTTON_IME_SWITCH);
+        verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed();
+    }
+
+    @Test
+    public void testPressA11yShortClick() {
+        mNavButtonController.onButtonClick(BUTTON_A11Y);
+        verify(mockSystemUiProxy, times(1))
+                .notifyAccessibilityButtonClicked(DISPLAY_ID);
+    }
+
+    @Test
+    public void testPressA11yLongClick() {
+        mNavButtonController.onButtonLongClick(BUTTON_A11Y);
+        verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked();
+    }
+
+    @Test
+    public void testLongPressHome() {
+        mNavButtonController.onButtonLongClick(BUTTON_HOME);
+        verify(mockSystemUiProxy, times(1)).startAssistant(any());
+    }
+
+    @Test
+    public void testPressHome() {
+        mNavButtonController.onButtonClick(BUTTON_HOME);
+        verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
+    }
+
+    @Test
+    public void testPressRecents() {
+        mNavButtonController.onButtonClick(BUTTON_RECENTS);
+        verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
+    }
+
+    @Test
+    public void testPressRecentsWithScreenPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonClick(BUTTON_RECENTS);
+        verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
+    }
+
+    @Test
+    public void testLongPressBackRecentsNotPinned() {
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+    }
+
+    @Test
+    public void testLongPressBackRecentsPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(1)).stopScreenPinning();
+    }
+
+    @Test
+    public void testLongPressBackRecentsTooLongPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        try {
+            Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+    }
+
+    @Test
+    public void testLongPressBackRecentsMultipleAttemptPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        try {
+            Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(0)).stopScreenPinning();
+
+        // Try again w/in threshold
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        verify(mockSystemUiProxy, times(1)).stopScreenPinning();
+    }
+
+    @Test
+    public void testLongPressHomeScreenPinned() {
+        mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
+        mNavButtonController.onButtonLongClick(BUTTON_HOME);
+        verify(mockSystemUiProxy, times(0)).startAssistant(any());
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index 7c463a7..189dff8 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..de6740d
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/NavigationBarRotationContextTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.systemui.shared.rotation.RotationButton;
+import com.android.systemui.shared.rotation.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, 0, 0, 0, 0,
+                () -> 0);
+        mRotationButtonController.setRotationButton(rotationButton, null);
+        // 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.onNavigationBarWindowVisibilityChange(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.onNavigationBarWindowVisibilityChange(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.onNavigationBarWindowVisibilityChange(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/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index 79ddf7a..4e49716 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -30,70 +30,76 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.util.LooperExecutor;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.quickstep.util.GroupTask;
 import com.android.systemui.shared.system.KeyguardManagerCompat;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 @SmallTest
 public class RecentTasksListTest {
 
-    private ActivityManagerWrapper mockActivityManagerWrapper;
+    @Mock
+    private SystemUiProxy mockSystemUiProxy;
 
     // Class under test
     private RecentTasksList mRecentTasksList;
 
     @Before
     public void setup() {
+        MockitoAnnotations.initMocks(this);
         LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class);
         KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class);
-        mockActivityManagerWrapper = mock(ActivityManagerWrapper.class);
         mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat,
-                mockActivityManagerWrapper);
+                mockSystemUiProxy);
     }
 
     @Test
-    public void onTaskRemoved_doesNotFetchTasks() {
-        mRecentTasksList.onTaskRemoved(0);
-        verify(mockActivityManagerWrapper, times(0))
-                .getRecentTasks(anyInt(), anyInt());
-    }
-
-    @Test
-    public void onTaskStackChanged_doesNotFetchTasks() {
-        mRecentTasksList.onTaskStackChanged();
-        verify(mockActivityManagerWrapper, times(0))
+    public void onRecentTasksChanged_doesNotFetchTasks() {
+        mRecentTasksList.onRecentTasksChanged();
+        verify(mockSystemUiProxy, times(0))
                 .getRecentTasks(anyInt(), anyInt());
     }
 
     @Test
     public void loadTasksInBackground_onlyKeys_noValidTaskDescription() {
-        ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
-        when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
-                .thenReturn(Collections.singletonList(recentTaskInfo));
+        GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
+                new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo(), null);
+        when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+                .thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
 
-        List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, true);
+        List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
+                true);
 
         assertEquals(1, taskList.size());
-        assertNull(taskList.get(0).taskDescription.getLabel());
+        assertNull(taskList.get(0).task1.taskDescription.getLabel());
+        assertNull(taskList.get(0).task2.taskDescription.getLabel());
     }
 
     @Test
     public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() {
         String taskDescription = "Wheeee!";
-        ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
-        recentTaskInfo.taskDescription = new ActivityManager.TaskDescription(taskDescription);
-        when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
-                .thenReturn(Collections.singletonList(recentTaskInfo));
+        ActivityManager.RecentTaskInfo task1 = new ActivityManager.RecentTaskInfo();
+        task1.taskDescription = new ActivityManager.TaskDescription(taskDescription);
+        ActivityManager.RecentTaskInfo task2 = new ActivityManager.RecentTaskInfo();
+        task2.taskDescription = new ActivityManager.TaskDescription();
+        GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
+                task1, task2, null);
+        when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
+                .thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
 
-        List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, false);
+        List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
+                false);
 
         assertEquals(1, taskList.size());
-        assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel());
+        assertEquals(taskDescription, taskList.get(0).task1.taskDescription.getLabel());
+        assertNull(taskList.get(0).task2.taskDescription.getLabel());
     }
 }
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..237e426 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;
@@ -32,24 +31,30 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.AllApps;
 import com.android.launcher3.tapl.Background;
 import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
 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.tapl.TestHelpers;
 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;
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TaplTestsQuickstep extends AbstractQuickStepTest {
+
+    private static final String APP_NAME = "LauncherTestApp";
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -145,13 +150,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 +165,7 @@
     @Test
     @NavigationModeSwitch
     @PortraitLandscape
+    @ScreenRecord // b/195673272
     public void testOverviewActions() throws Exception {
         // Experimenting for b/165029151:
         final Overview overview = mLauncher.pressHome().switchToOverview();
@@ -174,7 +177,6 @@
         OverviewActions actionsView =
                 mLauncher.pressHome().switchToOverview().getOverviewActions();
         actionsView.clickAndDismissScreenshot();
-        actionsView.clickAndDismissShare();
     }
 
     private int getCurrentOverviewPage(Launcher launcher) {
@@ -185,6 +187,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
@@ -262,7 +272,9 @@
         background.quickSwitchToPreviousAppSwipeLeft();
         assertTrue("The 2nd app we should have quick switched to is not running",
                 isTestActivityRunning(3));
-        getAndAssertBackground();
+
+        background = getAndAssertBackground();
+        background.switchToOverview();
     }
 
     private boolean isTestActivityRunning(int activityNumber) {
@@ -281,4 +293,96 @@
                 isTestActivityRunning(2));
         getAndAssertBackground();
     }
+
+    // TODO(b/204830798): test with all navigation modes(add @NavigationModeSwitch annotation)
+    //  after the bug resolved.
+    @Ignore("b/205027405")
+    @Test
+    @PortraitLandscape
+    @ScreenRecord
+    public void testPressBack() throws Exception {
+        mLauncher.getWorkspace().switchToAllApps();
+        mLauncher.pressBack();
+        mLauncher.getWorkspace();
+        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+
+        AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+        try {
+            allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
+        } finally {
+            allApps.unfreeze();
+        }
+        mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
+        mLauncher.pressBack();
+        mLauncher.getWorkspace();
+        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testOverviewForTablet() throws Exception {
+        // TODO(b/210158657): Re-enable for OOP
+        if (!mLauncher.isTablet() || !TestHelpers.isInLauncherProcess()) {
+            return;
+        }
+        for (int i = 2; i <= 14; i++) {
+            startTestActivity(i);
+        }
+
+        Overview overview = mLauncher.pressHome().switchToOverview();
+        executeOnLauncher(
+                launcher -> assertTrue("Don't have at least 13 tasks",
+                        getTaskCount(launcher) >= 13));
+
+        // 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/accent_ripple_color.xml b/res/color-night-v31/accent_ripple_color.xml
new file mode 100644
index 0000000..cb149d6
--- /dev/null
+++ b/res/color-night-v31/accent_ripple_color.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_accent1_300"/>
+</selector>
\ No newline at end of file
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/res/color-night/accent_ripple_color.xml b/res/color-night/accent_ripple_color.xml
new file mode 100644
index 0000000..4a37b00
--- /dev/null
+++ b/res/color-night/accent_ripple_color.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#53BCAC"/>
+</selector>
\ No newline at end of file
diff --git a/res/color-v31/accent_ripple_color.xml b/res/color-v31/accent_ripple_color.xml
new file mode 100644
index 0000000..a996228
--- /dev/null
+++ b/res/color-v31/accent_ripple_color.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_accent2_50"/>
+</selector>
\ No newline at end of file
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/color/accent_ripple_color.xml b/res/color/accent_ripple_color.xml
new file mode 100644
index 0000000..697f415
--- /dev/null
+++ b/res/color/accent_ripple_color.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#CDFAF1"/>
+</selector>
\ No newline at end of file
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/res/drawable/all_apps_tabs_background.xml b/res/drawable/all_apps_tabs_background.xml
index aea2e7a..8471cd4 100644
--- a/res/drawable/all_apps_tabs_background.xml
+++ b/res/drawable/all_apps_tabs_background.xml
@@ -13,23 +13,36 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:enterFadeDuration="100">
-    <item
-        android:id="@+id/unselected"
-        android:state_selected="false">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/accent_ripple_color">
+
+    <item android:id="@android:id/mask">
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-            <solid android:color="@color/all_apps_tabs_background" />
+            <solid android:color="@color/accent_ripple_color" />
         </shape>
     </item>
 
-    <item
-        android:id="@+id/selected"
-        android:state_selected="true">
-        <shape android:shape="rectangle">
-            <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-            <solid android:color="@color/all_apps_tab_background_selected" />
-        </shape>
+    <item>
+        <selector android:enterFadeDuration="100">
+            <item
+                android:id="@+id/unselected"
+                android:state_selected="false">
+                <shape android:shape="rectangle">
+                    <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+                    <solid android:color="@color/all_apps_tabs_background" />
+                </shape>
+            </item>
+
+            <item
+                android:id="@+id/selected"
+                android:state_selected="true">
+                <shape android:shape="rectangle">
+                    <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
+                    <solid android:color="@color/all_apps_tab_background_selected" />
+                </shape>
+            </item>
+        </selector>
     </item>
-</selector>
\ No newline at end of file
+
+</ripple>
\ No newline at end of file
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/ic_split_horizontal.xml b/res/drawable/ic_split_horizontal.xml
new file mode 100644
index 0000000..ee710d0
--- /dev/null
+++ b/res/drawable/ic_split_horizontal.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="16dp"
+    android:viewportWidth="20"
+    android:viewportHeight="16">
+  <path
+      android:pathData="M18,14L13,14L13,2L18,2L18,14ZM20,14L20,2C20,0.9 19.1,-0 18,-0L13,-0C11.9,-0 11,0.9 11,2L11,14C11,15.1 11.9,16 13,16L18,16C19.1,16 20,15.1 20,14ZM7,14L2,14L2,2L7,2L7,14ZM9,14L9,2C9,0.9 8.1,-0 7,-0L2,-0C0.9,-0 -0,0.9 -0,2L-0,14C-0,15.1 0.9,16 2,16L7,16C8.1,16 9,15.1 9,14Z"
+      android:fillColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_split_left.xml b/res/drawable/ic_split_left.xml
new file mode 100644
index 0000000..fc9f699
--- /dev/null
+++ b/res/drawable/ic_split_left.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="16dp"
+    android:viewportWidth="20"
+    android:viewportHeight="16">
+  <path
+      android:pathData="M-0,2L-0,14C-0,15.1 0.9,16 2,16L7,16C8.1,16 9,15.1 9,14L9,2C9,0.9 8.1,-0 7,-0L2,-0C0.9,-0 -0,0.9 -0,2ZM13,2L18,2L18,14L13,14L13,2ZM11,2L11,14C11,15.1 11.9,16 13,16L18,16C19.1,16 20,15.1 20,14L20,2C20,0.9 19.1,-0 18,-0L13,-0C11.9,-0 11,0.9 11,2Z"
+      android:fillColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_split_right.xml b/res/drawable/ic_split_right.xml
new file mode 100644
index 0000000..cc15622
--- /dev/null
+++ b/res/drawable/ic_split_right.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="16dp"
+    android:viewportWidth="20"
+    android:viewportHeight="16">
+  <path
+      android:pathData="M20,14L20,2C20,0.9 19.1,-0 18,-0L13,-0C11.9,-0 11,0.9 11,2L11,14C11,15.1 11.9,16 13,16L18,16C19.1,16 20,15.1 20,14ZM7,14L2,14L2,2L7,2L7,14ZM9,14L9,2C9,0.9 8.1,-0 7,-0L2,-0C0.9,-0 -0,0.9 -0,2L-0,14C-0,15.1 0.9,16 2,16L7,16C8.1,16 9,15.1 9,14Z"
+      android:fillColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_split_top.xml b/res/drawable/ic_split_top.xml
new file mode 100644
index 0000000..f8c15bd
--- /dev/null
+++ b/res/drawable/ic_split_top.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="20dp"
+    android:viewportWidth="16"
+    android:viewportHeight="20">
+  <path
+      android:pathData="M14,0H2C0.9,0 0,0.9 0,2V7C0,8.1 0.9,9 2,9H14C15.1,9 16,8.1 16,7V2C16,0.9 15.1,0 14,0ZM14,13V18H2V13H14ZM14,11H2C0.9,11 0,11.9 0,13V18C0,19.1 0.9,20 2,20H14C15.1,20 16,19.1 16,18V13C16,11.9 15.1,11 14,11Z"
+      android:fillColor="#000000"/>
+</vector>
diff --git a/res/drawable/ic_split_vertical.xml b/res/drawable/ic_split_vertical.xml
new file mode 100644
index 0000000..9bc9785
--- /dev/null
+++ b/res/drawable/ic_split_vertical.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,4V9H6V4H18ZM18,2H6C4.9,2 4,2.9 4,4V9C4,10.1 4.9,11 6,11H18C19.1,11 20,10.1 20,9V4C20,2.9 19.1,2 18,2ZM18,15V20H6V15H18ZM18,13H6C4.9,13 4,13.9 4,15V20C4,21.1 4.9,22 6,22H18C19.1,22 20,21.1 20,20V15C20,13.9 19.1,13 18,13Z"
+      android:fillColor="#000000"/>
+</vector>
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..7f9f63e 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -42,10 +42,10 @@
         <include layout="@layout/floating_header_content" />
 
         <include layout="@layout/all_apps_personal_work_tabs" />
+
     </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/drawable/personal_work_tabs_ripple.xml b/res/layout/all_apps_icon_twoline.xml
similarity index 62%
copy from res/drawable/personal_work_tabs_ripple.xml
copy to res/layout/all_apps_icon_twoline.xml
index 2e57b80..54c7147 100644
--- a/res/drawable/personal_work_tabs_ripple.xml
+++ b/res/layout/all_apps_icon_twoline.xml
@@ -13,10 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-    <shape android:shape="rectangle">
-        <solid android:color="@android:color/transparent" />
-        <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
-    </shape>
-</ripple>
\ No newline at end of file
+<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    style="@style/BaseIcon.AllApps"
+    android:id="@+id/icon"
+    android:singleLine="false"
+    android:lines="2"
+    android:inputType="textMultiLine"
+    launcher:iconDisplay="all_apps"
+    launcher:centerVertically="true" />
+
diff --git a/res/layout/floating_split_select_view.xml b/res/layout/floating_split_select_view.xml
new file mode 100644
index 0000000..8d47f4e
--- /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:colorPrimary"
+        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/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index b27b505..0f6fc6c 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -33,6 +33,14 @@
             android:layout_height="match_parent"
             android:importantForAccessibility="no"
             android:layout_gravity="fill"/>
+
+        <ImageView
+            android:id="@+id/widget_badge"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:importantForAccessibility="no"
+            android:layout_gravity="end|bottom"
+            android:layout_margin="@dimen/profile_badge_margin"/>
     </com.android.launcher3.widget.WidgetCellPreview>
 
     <!-- The name of the widget. -->
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/all_set_page_bg.json b/res/raw/all_set_page_bg.json
new file mode 100644
index 0000000..9705837
--- /dev/null
+++ b/res/raw/all_set_page_bg.json
@@ -0,0 +1 @@
+{"v":"5.7.8","fr":24,"ip":0,"op":72,"w":2472,"h":5352,"nm":"3Second_MAIN_Welcome","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 60","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[1508,1364,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"PinkFlower","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":72,"s":[56]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.07,"y":0.986},"o":{"x":0.167,"y":0.167},"t":0,"s":[1505.832,1379.455,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.773,"y":0.01},"t":38,"s":[1505.832,575,0],"to":[0,0,0],"ti":[0,0,0]},{"t":72,"s":[1505.832,1379.455,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-3514.717,-358.642,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[75.615,-96.908],[89.338,-70.276],[111.99,-50.668],[111.764,-20.709],[122.709,7.18],[108.586,33.602],[105.316,63.383],[80.533,80.216],[63.797,105.066],[34.03,108.453],[7.663,122.679],[-20.269,111.845],[-50.226,112.189],[-69.924,89.614],[-96.61,75.997],[-103.56,46.854],[-120.861,22.395],[-113.472,-6.639],[-117.425,-36.337],[-97.389,-58.612],[-87.087,-86.745],[-58.996,-97.158],[-36.8,-117.281],[-7.086,-113.445],[21.918,-120.948],[46.446,-103.744]],"o":[[-75.615,96.909],[-89.338,70.276],[-111.99,50.668],[-111.764,20.709],[-122.709,-7.18],[-108.586,-33.602],[-105.316,-63.383],[-80.533,-80.216],[-63.797,-105.066],[-34.03,-108.453],[-7.663,-122.679],[20.269,-111.845],[50.226,-112.188],[69.924,-89.614],[96.61,-75.997],[103.56,-46.854],[120.861,-22.395],[113.472,6.64],[117.425,36.337],[97.389,58.612],[87.088,86.745],[58.995,97.158],[36.8,117.281],[7.087,113.445],[-21.918,120.948],[-46.446,103.744]],"v":[[733.209,572.105],[531.711,675.932],[383.354,847.313],[156.685,845.606],[-54.323,928.412],[-254.235,821.562],[-479.555,796.823],[-606.913,609.309],[-794.927,482.691],[-820.554,257.47],[-928.191,57.981],[-846.217,-153.353],[-848.817,-380.013],[-678.021,-529.044],[-574.99,-730.949],[-354.499,-783.537],[-169.439,-914.435],[50.234,-858.532],[274.928,-888.434],[443.46,-736.847],[656.313,-658.903],[735.094,-446.359],[887.344,-278.426],[858.327,-53.616],[915.095,165.835],[784.928,351.409]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.839215686275,0.439215686275,0.388235294118,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.839215746113,0.439215716194,0.388235324037,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[-3509.952,-363.731],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":288,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse_Bottom","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.248]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-56]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.172]},"t":38,"s":[-38]},{"t":72,"s":[-56]}],"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.032]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1720]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.022]},"t":38,"s":[1544]},{"t":72,"s":[1720]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.07],"y":[1.034]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[4069]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.719],"y":[0.024]},"t":38,"s":[3872]},{"t":72,"s":[4069]}],"ix":4}},"a":{"a":0,"k":[164.438,1433.781,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3079.125,4685.989],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.305882352941,0.309803921569,0.321568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.882353001015,0.894118006089,0.886274988511,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":true},{"ty":"tr","p":{"a":0,"k":[164.438,1481.781],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":240,"st":0,"bm":0}],"markers":[]}
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..18fa12f 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Kortpad is nie beskikbaar nie"</string>
     <string name="home_screen" msgid="5629429142036709174">"Tuis"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Verdeelde skerm"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Verdeel bo"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Verdeel links"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Verdeel regs"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Programinligting vir %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Raak en hou om \'n legstuk te skuif."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dubbeltik en hou om \'n legstuk te skuif of gebruik gepasmaakte handelinge."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..f75e7e1 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"አቋራጭ አይገኝም"</string>
     <string name="home_screen" msgid="5629429142036709174">"መነሻ"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"የተከፈለ ማያ ገጽ"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"ወደ ላይ ክፈል"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"ወደ ግራ ክፈል"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"ወደ ቀኝ ክፈል"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"የመተግበሪያ መረጃ ለ%1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ምግብርን ለማንቀሳቀስ ይንኩ እና ይያዙ።"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ምግብርን ለማንቀሳቀስ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ ያድርጉ እና ይያዙ።"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..8571d46 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"الاختصار غير متاح"</string>
     <string name="home_screen" msgid="5629429142036709174">"الشاشة الرئيسية"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"تقسيم الشاشة"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"تقسيم للأعلى"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"تقسيم لليسار"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"تقسيم لليمين"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"‏معلومات تطبيق %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"انقر مع الاستمرار لنقل أداة"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"انقر مرتين مع تثبيت إصبعك لنقل أداة أو استخدام الإجراءات المخصّصة."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,22 +40,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 +61,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 +90,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..5dc35cb 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"শ্বৰ্টকাট নাই"</string>
     <string name="home_screen" msgid="5629429142036709174">"গৃহ স্ক্ৰীন"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"বিভাজিত স্ক্ৰীন"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"একেবাৰে ওপৰৰফালে বিভাজন কৰক"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"বাওঁফালে বিভাজন কৰক"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"সোঁফালে বিভাজন কৰক"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$sৰ বাবে এপৰ তথ্য"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ৱিজেট স্থানান্তৰ কৰিবলৈ টিপি ধৰি ৰাখক।"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"কোনো ৱিজেট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক অথবা কাষ্টম কাৰ্য ব্যৱহাৰ কৰক।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..c2ca56d 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Qısayol əlçatan deyil"</string>
     <string name="home_screen" msgid="5629429142036709174">"Əsas səhifə"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekran bölünməsi"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Yuxarı ayırın"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Sola ayırın"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Sağa ayırın"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ilə bağlı tətbiq məlumatı"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Vidceti daşımaq üçün toxunub saxlayın."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Vidceti daşımaq üçün iki dəfə toxunub saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,7 @@
     <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>
+    <string name="all_apps_label" msgid="5015784846527570951">"Bütün tətbiqlər"</string>
     <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 +90,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..b10ae18 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Prečica nije dostupna"</string>
     <string name="home_screen" msgid="5629429142036709174">"Početni ekran"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podeljeni ekran"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Podeli u vrhu"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Podeli levo"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Podeli desno"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji za: %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Dodirnite i zadržite radi pomeranja vidžeta."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite da biste pomerali vidžet ili koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
@@ -36,16 +40,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 +61,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 +90,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..3d6bde4 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Ярлык недаступны"</string>
     <string name="home_screen" msgid="5629429142036709174">"Галоўны экран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Падзелены экран"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Падзяліць уверсе"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Падзяліць злева"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Падзяліць справа"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Інфармацыя пра праграму для: %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Націсніце і ўтрымлівайце віджэт для перамяшчэння."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Дакраніцеся двойчы і ўтрымлівайце, каб перамясціць віджэт або выкарыстоўваць спецыяльныя дзеянні."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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..661c710 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Няма достъп до прекия път"</string>
     <string name="home_screen" msgid="5629429142036709174">"Начален екран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделен екран"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Разделяне в горната част"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Разделяне в лявата част"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Разделяне в дясната част"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Информация за приложението за %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Докоснете и задръжте за преместване на приспособление"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Докоснете двукратно и задръжте за преместване на приспособление или използвайте персонал. действия."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..299a014 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"শর্টকাটগুলি অনুপলব্ধ"</string>
     <string name="home_screen" msgid="5629429142036709174">"হোম"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"স্প্লিট স্ক্রিন"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"স্ক্রিনের উপরের দিকে স্প্লিট করুন"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"স্ক্রিনের বাঁদিকে স্প্লিট করুন"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"স্ক্রিনের ডানদিকে স্প্লিট করুন"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-এর জন্য অ্যাপ সম্পর্কিত তথ্য"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"কোনও উইজেট সরাতে সেটি টাচ করে ধরে রাখুন।"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"একটি উইজেট সরাতে বা কাস্টম অ্যাকশন ব্যবহার করতে ডবল ট্যাপ করে ধরে রাখুন।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..50b2b3c 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Prečica nije dostupna"</string>
     <string name="home_screen" msgid="5629429142036709174">"Početni ekran"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podijeljeni ekran"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Podijeli nagore"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Podijeli ulijevo"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Podijeli udesno"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Dodirnite i zadržite da pomjerite vidžet."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite da pomjerite vidžet ili da koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,16 +40,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 +61,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 +90,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..a0f03c8 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"La drecera no està disponible"</string>
     <string name="home_screen" msgid="5629429142036709174">"Inici"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Divideix a la part superior"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Divideix a l\'esquerra"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Divideix a la dreta"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informació de l\'aplicació %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Fes doble toc i mantén premut per moure un widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Fes doble toc i mantén premut per moure un widget o per utilitzar accions personalitzades."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..a92e936 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Zkratka není k dispozici"</string>
     <string name="home_screen" msgid="5629429142036709174">"Domů"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdělená obrazovka"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Rozdělit nahoře"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Rozdělit vlevo"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Rozdělit vpravo"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informace o aplikaci %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Klepnutím a podržením přesunete widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvojitým klepnutím a podržením přesunete widget, případně použijte vlastní akce."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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..ee339c4 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Genvejen er ikke tilgængelig"</string>
     <string name="home_screen" msgid="5629429142036709174">"Startskærm"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Opdel skærm"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Vis øverst"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Vis i venstre side"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Vis i højre side"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinfo for %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Hold en widget nede for at flytte den."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tryk to gange, og hold en widget nede for at flytte den eller bruge tilpassede handlinger."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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 794ca4e..1ddd652 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Verknüpfung nicht verfügbar"</string>
     <string name="home_screen" msgid="5629429142036709174">"Startbildschirm"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Splitscreen"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Oben teilen"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Links teilen"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Rechts teilen"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"App-Info für %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Zum Verschieben des Widgets berühren und halten"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Doppeltippen und halten, um ein Widget zu bewegen oder benutzerdefinierte Aktionen zu nutzen."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,7 @@
     <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>
+    <string name="all_apps_label" msgid="5015784846527570951">"Alle Apps"</string>
     <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 +90,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..b78db22 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Η συντόμευση δεν είναι διαθέσιμη"</string>
     <string name="home_screen" msgid="5629429142036709174">"Αρχική οθόνη"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Διαχωρισμός οθόνης"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Διαχωρισμός επάνω"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Διαχωρισμός αριστερά"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Διαχωρισμός δεξιά"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Πληροφορίες εφαρμογής για %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Πατήστε παρατετ. για μετακίνηση γραφ. στοιχείου."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Πατήστε δύο φορές παρατεταμένα για μετακίνηση γραφικού στοιχείου ή χρήση προσαρμοσμένων ενεργειών."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..29a726c 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
     <string name="home_screen" msgid="5629429142036709174">"Home"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Split top"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Split left"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Split right"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap &amp; hold to move a widget or use custom actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..29a726c 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
     <string name="home_screen" msgid="5629429142036709174">"Home"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Split top"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Split left"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Split right"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap &amp; hold to move a widget or use custom actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..29a726c 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
     <string name="home_screen" msgid="5629429142036709174">"Home"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Split top"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Split left"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Split right"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap &amp; hold to move a widget or use custom actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..29a726c 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
     <string name="home_screen" msgid="5629429142036709174">"Home"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Split top"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Split left"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Split right"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Touch and hold to move a widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Double-tap &amp; hold to move a widget or use custom actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..f975d5c 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎Shortcut isn\'t available‎‏‎‎‏‎"</string>
     <string name="home_screen" msgid="5629429142036709174">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎Home‎‏‎‎‏‎"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎Split screen‎‏‎‎‏‎"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‎‎‎‏‎Split top‎‏‎‎‏‎"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎Split left‎‏‎‎‏‎"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‏‎Split right‎‏‎‎‏‎"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎App info for %1$s‎‏‎‎‏‎"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎Touch &amp; hold to move a widget.‎‏‎‎‏‎"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‏‎‎Double-tap &amp; hold to move a widget or use custom actions.‎‏‎‎‏‎"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎%1$d × %2$d‎‏‎‎‏‎"</string>
@@ -36,14 +40,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 +61,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 +90,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..314a3af 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"El acceso directo no está disponible"</string>
     <string name="home_screen" msgid="5629429142036709174">"Pantalla principal"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Dividir en la parte superior"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Dividir a la izquierda"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Dividir a la derecha"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Información de la app de %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Mantén presionado para mover un widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Presiona dos veces y mantén presionado para mover un widget o usar acciones personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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 b9dd7ac..2401054 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Acceso directo no disponible"</string>
     <string name="home_screen" msgid="5629429142036709174">"Inicio"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Dividir parte superior"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Dividir parte izquierda"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Dividir parte derecha"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Información de la aplicación %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Mantén pulsado un widget para moverlo."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toca dos veces y mantén pulsado un widget para moverlo o usar acciones personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 291080a..e483041 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Otsetee pole saadaval"</string>
     <string name="home_screen" msgid="5629429142036709174">"Avakuva"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Jagatud ekraanikuva"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Jaga üles"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Jaga vasakule"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Jaga paremale"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Rakenduse teave: %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Vidina teisaldamiseks puudutage ja hoidke all."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Vidina teisaldamiseks või kohandatud toimingute kasutamiseks topeltpuudutage ja hoidke all."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..f1cc45e 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Lasterbideak ez daude erabilgarri"</string>
     <string name="home_screen" msgid="5629429142036709174">"Hasierako pantaila"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Zatitu pantaila"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Zatitu goialdean"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Zatitu ezkerraldean"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Zatitu eskuinaldean"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s aplikazioari buruzko informazioa"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Eduki sakatuta widget bat mugitzeko."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Sakatu birritan eta eduki sakatuta widget bat mugitzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..a7b1020 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"میان‌بر دردسترس نیست"</string>
     <string name="home_screen" msgid="5629429142036709174">"صفحه اصلی"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"صفحهٔ دونیمه"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"تقسیم از بالا"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"تقسیم از چپ"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"تقسیم از راست"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"‏اطلاعات برنامه %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"برای جابه‌جا کردن ابزارک، لمس کنید و نگه دارید."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"برای جابه‌جا کردن ابزارک یا استفاده از کنش‌های سفارشی، دوضربه بزنید و نگه دارید."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..874c85d 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Pikakuvake ei ole käytettävissä."</string>
     <string name="home_screen" msgid="5629429142036709174">"Etusivu"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Jaettu näyttö"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Ylhäällä"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Vasemmalla"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Oikealla"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Sovellustiedot: %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Kosketa pitkään, niin voit siirtää widgetiä."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Kaksoisnapauta ja paina pitkään, niin voit siirtää widgetiä tai käyttää muokattuja toimintoja."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..9a77c87 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Le raccourci n\'est pas disponible"</string>
     <string name="home_screen" msgid="5629429142036709174">"Accueil"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran partagé"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Diviser dans la partie supérieure"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Diviser à gauche"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Diviser à droite"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Renseignements sur l\'appli pour %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Maintenez le doigt sur un widget pour le déplacer."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Touchez 2x un widget et maintenez le doigt dessus pour le déplacer ou utiliser des actions personnalisées."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,7 @@
     <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>
+    <string name="all_apps_label" msgid="5015784846527570951">"Toutes les applications"</string>
     <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 +90,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..f071db9 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Raccourci non disponible"</string>
     <string name="home_screen" msgid="5629429142036709174">"Accueil"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran partagé"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Affichée en haut"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Affichée à gauche"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Affichée à droite"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Infos sur l\'appli pour %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Appuyez de manière prolongée sur un widget pour le déplacer."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Appuyez deux fois et maintenez la pression pour déplacer widget ou utiliser actions personnalisées."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..3986aa4 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"O atallo non está dispoñible"</string>
     <string name="home_screen" msgid="5629429142036709174">"Inicio"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Dividir (arriba)"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Dividir (esquerda)"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Dividir (dereita)"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Información da aplicación para %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Mantén premido un widget para movelo."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toca dúas veces un widget e manteno premido para movelo ou utiliza accións personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,7 @@
     <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>
+    <string name="all_apps_label" msgid="5015784846527570951">"Todas as aplicacións"</string>
     <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 +90,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 1e6713f..fae3773 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"શૉર્ટકટ ઉપલબ્ધ નથી"</string>
     <string name="home_screen" msgid="5629429142036709174">"હોમ સ્ક્રીન"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"સ્ક્રીનને વિભાજિત કરો"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"ઉપર વિભાજિત કરો"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"ડાબે વિભાજિત કરો"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"જમણે વિભાજિત કરો"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s માટે ઍપ માહિતી"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"વિજેટ ખસેડવા ટચ કરીને થોડી વાર દબાવી રાખો."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"વિજેટ ખસેડવા બે વાર ટૅપ કરીને દબાવી રાખો અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરો."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..6290cd1 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"शॉर्टकट उपलब्ध नहीं है"</string>
     <string name="home_screen" msgid="5629429142036709174">"होम स्क्रीन"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"स्क्रीन को ऊपर के हिस्से में स्प्लिट करें"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"स्क्रीन को बाएं हिस्से में स्प्लिट करें"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"स्क्रीन को दाएं हिस्से में स्प्लिट करें"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s के लिए ऐप्लिकेशन की जानकारी"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"किसी विजेट को एक से दूसरी जगह ले जाने के लिए, उसे दबाकर रखें."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"किसी विजेट को एक से दूसरी जगह ले जाने के लिए, उस पर दो बार टैप करके दबाकर रखें या पसंद के मुताबिक कार्रवाइयां इस्तेमाल करें."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..75e47a1 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Prečac nije dostupan"</string>
     <string name="home_screen" msgid="5629429142036709174">"Početni zaslon"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podijeljeni zaslon"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Podijeli gore"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Podijeli lijevo"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Podijeli desno"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Dodirnite i zadržite da biste premjestili widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite pritisak da biste premjestili widget ili upotrijebite prilagođene radnje"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,16 +40,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 +61,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 +90,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..71a8d69 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"A gyorsparancs nem áll rendelkezésre"</string>
     <string name="home_screen" msgid="5629429142036709174">"Kezdőképernyő"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Osztott képernyő"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Osztás a képernyő tetején"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Osztás a képernyő bal oldalán"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Osztás a képernyő jobb oldalán"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Alkalmazásinformáció a következőhöz: %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Tartsa lenyomva a modult az áthelyezéshez."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Modul áthelyezéséhez koppintson duplán, tartsa nyomva az ujját, vagy használjon egyéni műveleteket."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..3d65a3e 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Դյուրանցումն անհասանելի է"</string>
     <string name="home_screen" msgid="5629429142036709174">"Հիմնական էկրան"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Տրոհել էկրանը"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Հավելվածը վերևում"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Հավելվածը ձախ կողմում"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Հավելվածը աջ կողմում"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Տեղեկություններ %1$s հավելվածի մասին"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Հպեք և պահեք՝ վիջեթ տեղափոխելու համար։"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Կրկնակի հպեք և պահեք՝ վիջեթ տեղափոխելու համար, կամ օգտվեք հատուկ գործողություններից։"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..f106774 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Pintasan tidak tersedia"</string>
     <string name="home_screen" msgid="5629429142036709174">"Layar utama"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Layar terpisah"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Pisahkan ke atas"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Pisahkan ke kiri"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Pisahkan ke kanan"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Info aplikasi untuk %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Sentuh lama untuk memindahkan widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ketuk dua kali &amp; tahan untuk memindahkan widget atau gunakan tindakan khusus."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..1397b88 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Flýtileið er ekki tiltæk"</string>
     <string name="home_screen" msgid="5629429142036709174">"Heim"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Skipta skjá"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Skipta efst"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Skipta vinstra megin"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Skipta hægra megin"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Upplýsingar um forrit fyrir %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Haltu fingri á græju til að færa hana."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ýttu tvisvar og haltu fingri á græju til að færa hana eða notaðu sérsniðnar aðgerðir."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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 708a0c5..ff0b6c0 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"La scorciatoia non è disponibile"</string>
     <string name="home_screen" msgid="5629429142036709174">"Home"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Schermo diviso"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Dividi in alto"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Dividi a sinistra"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Dividi a destra"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informazioni sull\'app %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Tocca e tieni premuto per spostare un widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tocca due volte e tieni premuto per spostare un widget o per usare le azioni personalizzate."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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="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> widget</item>
-    </plurals>
-    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
-      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> scorciatoia</item>
-      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> scorciatoie</item>
-    </plurals>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# scorciatoia}one{# 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 +61,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 +90,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="one"><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="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ha <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifiche</item>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ha # notifica}one{{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..3d0774f 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"קיצור הדרך אינו זמין"</string>
     <string name="home_screen" msgid="5629429142036709174">"בית"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"מסך מפוצל"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"פיצול למעלה"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"פיצול שמאלה"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"פיצול ימינה"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"‏פרטים על האפליקציה %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"כדי להעביר ווידג\'ט למקום אחר יש לגעת ולא להרפות."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"כדי להעביר ווידג\'ט למקום אחר או להשתמש בפעולות מותאמות אישית, יש ללחוץ פעמיים ולא להרפות."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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..f1db158 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ショートカットは使用できません"</string>
     <string name="home_screen" msgid="5629429142036709174">"ホーム"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割画面"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"上に分割"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"左に分割"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"右に分割"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s のアプリ情報"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"長押ししてウィジェットを移動してください。"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ウィジェットをダブルタップして長押ししながら移動するか、カスタム操作を使用してください。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$dx%2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..a0cbbc7 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"მალსახმობი მიუწვდომელია"</string>
     <string name="home_screen" msgid="5629429142036709174">"მთავარი გვერდი"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ეკრანის გაყოფა"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"გაყოფილი ზემოთ"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"გაყოფილი მარცხნივ"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"გაყოფილი მარჯვნივ"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-ის აპის ინფო"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"შეხებით აირჩიეთ და გეჭიროთ ვიჯეტის გადასაადგილებლად."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ორმაგი შეხებით აირჩიეთ და გეჭიროთ ვიჯეტის გადასაადგილებლად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..529a00f 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Таңбаша қолжетімді емес"</string>
     <string name="home_screen" msgid="5629429142036709174">"Негізгі экран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлу"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Жоғарыдан шығару"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Сол жақтан шығару"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Оң жақтан шығару"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s қолданбасы туралы ақпарат"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Виджетті жылжыту үшін басып тұрыңыз."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Виджетті жылжыту үшін екі рет түртіңіз де, ұстап тұрыңыз немесе арнаулы әрекеттерді пайдаланыңыз."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..f8560aa 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ផ្លូវកាត់មិនអាចប្រើបានទេ"</string>
     <string name="home_screen" msgid="5629429142036709174">"អេក្រង់ដើម"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"មុខងារ​បំបែកអេក្រង់"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"បំបែក​ខាងលើ"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"បំបែក​ខាងឆ្វេង"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"បំបែក​ខាងស្ដាំ"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"ព័ត៌មានកម្មវិធី​សម្រាប់ %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ចុចឱ្យជាប់​ដើម្បីផ្លាស់ទី​ធាតុក្រាហ្វិក​។"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ចុចពីរដង រួចសង្កត់ឱ្យជាប់ ដើម្បីផ្លាស់ទី​ធាតុក្រាហ្វិក ឬប្រើ​សកម្មភាព​តាមបំណង​។"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..9eda419 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ಶಾರ್ಟ್‌ಕಟ್ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="home_screen" msgid="5629429142036709174">"ಹೋಮ್"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"ಮೇಲಕ್ಕೆ ವಿಭಜಿಸಿ"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"ಎಡಕ್ಕೆ ವಿಭಜಿಸಿ"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"ಬಲಕ್ಕೆ ವಿಭಜಿಸಿ"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ಗಾಗಿ ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ವಿಜೆಟ್ ಸರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ವಿಜೆಟ್ ಸರಿಸಲು ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಲು ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..9859752 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"바로가기를 사용할 수 없음"</string>
     <string name="home_screen" msgid="5629429142036709174">"홈"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"화면 분할"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"위쪽으로 분할"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"왼쪽으로 분할"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"오른쪽으로 분할"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 앱 정보"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"길게 터치하여 위젯을 이동하세요."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"두 번 탭한 다음 길게 터치하여 위젯을 이동하거나 맞춤 작업을 사용하세요."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..5378ca5 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Кыска жол жок"</string>
     <string name="home_screen" msgid="5629429142036709174">"Башкы экран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлүү"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Өйдө бөлүү"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Солго бөлүү"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Оңго бөлүү"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s колдонмосу жөнүндө маалымат"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Виджетти кое бербей басып туруп жылдырыңыз."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Виджетти жылдыруу үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..e47fefd 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ບໍ່ສາມາດໃຊ້ທາງລັດໄດ້"</string>
     <string name="home_screen" msgid="5629429142036709174">"ໂຮມສະກຣີນ"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ແບ່ງໜ້າຈໍ"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"ແບ່ງເທິງ"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"ແບ່ງຊ້າຍ"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"ແບ່ງຂວາ"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"ຂໍ້ມູນແອັບສຳລັບ %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ແຕະຄ້າງໄວ້ເພື່ອຍ້າຍວິດເຈັດ."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຍ້າຍວິດເຈັດ ຫຼື ໃຊ້ຄຳສັ່ງກຳນົດເອງ."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..e4fdfc6 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Sparčiojo klavišo negalima naudoti"</string>
     <string name="home_screen" msgid="5629429142036709174">"Pagrindinis"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Išskaidyto ekrano režimas"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Išskaidyti viršun"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Išskaidyti kairėn"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Išskaidyti dešinėn"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Programos „%1$s“ informacija"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Dukart pal. ir palaik., kad perkeltumėte valdiklį."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dukart palieskite ir palaikykite, kad perkeltumėte valdiklį ar naudotumėte tinkintus veiksmus."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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..f02d4fb 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Saīsne nav pieejama."</string>
     <string name="home_screen" msgid="5629429142036709174">"Sākums"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Sadalīt ekrānu"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Sadalījums augšdaļā"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Sadalījums pa kreisi"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Sadalījums pa labi"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s: informācija par lietotni"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Lai pārvietotu logrīku, pieskarieties un turiet."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Lai pārvietotu logrīku, uz tā veiciet dubultskārienu un turiet. Varat arī veikt pielāgotas darbības."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,16 +40,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 +61,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 +90,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..0bc7043 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Кратенката не е достапна"</string>
     <string name="home_screen" msgid="5629429142036709174">"Почетен екран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Поделен екран"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Подели нагоре"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Подели налево"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Подели надесно"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Податоци за апликација за %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Допрете и задржете за да преместите виџет."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Допрете двапати и задржете за да преместите виџет или користете приспособени дејства."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..5c626db 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"കുറുക്കുവഴി ലഭ്യമല്ല"</string>
     <string name="home_screen" msgid="5629429142036709174">"ഹോം"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"സ്‌ക്രീൻ വിഭജന മോഡ്"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"മുകളിലേക്ക് വിഭജിക്കുക"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"ഇടതുഭാഗത്തേക്ക് വിഭജിക്കുക"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"വലതുഭാഗത്തേക്ക് വിഭജിക്കുക"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s എന്നതിന്റെ ആപ്പ് വിവരങ്ങൾ"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"വിജറ്റ് നീക്കാൻ സ്‌പർശിച്ച് പിടിക്കുക."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"വിജറ്റ് നീക്കാൻ ഡബിൾ ടാപ്പ് ചെയ്യൂ, ഹോൾഡ് ചെയ്യൂ അല്ലെങ്കിൽ ഇഷ്‌ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കൂ."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..e6fcefa 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Товчлол алга"</string>
     <string name="home_screen" msgid="5629429142036709174">"Нүүр"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Дэлгэцийг хуваах"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Дээш хуваах"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Зүүн тийш хуваах"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Баруун тийш хуваах"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-н аппын мэдээлэл"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Виджетийг зөөх бол хүрээд, удаан дарна уу."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Виджетийг зөөх эсвэл захиалгат үйлдлийг ашиглахын тулд хоёр товшоод, удаан дарна уу."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..34bdcd2 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"शॉर्टकट उपलब्ध नाही"</string>
     <string name="home_screen" msgid="5629429142036709174">"होम"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"सर्वात वरती स्प्लिट करा"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"डावीकडे स्प्लिट करा"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"उजवीकडे स्प्लिट करा"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s साठी ॲपशी संबंधित माहिती"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"विजेट हलवण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"विजेट हलवण्यासाठी किंवा कस्टम कृती वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..d39a213 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Pintasan tidak tersedia"</string>
     <string name="home_screen" msgid="5629429142036709174">"Rumah"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Skrin pisah"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Pisah atas"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Pisah kiri"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Pisah kanan"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Maklumat apl untuk %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Sentuh &amp; tahan untuk menggerakkan widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ketik dua kali &amp; tahan untuk menggerakkan widget atau menggunakan tindakan tersuai."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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 8ae9cc3..0d4c8fa 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ဖြတ်လမ်း မရနိုင်ပါ"</string>
     <string name="home_screen" msgid="5629429142036709174">"ပင်မစာမျက်နှာ"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"ထိပ်ပိုင်း အခွဲ"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"ဘက်ဘက် အခွဲ"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"ညာဘက် အခွဲ"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s အတွက် အက်ပ်အချက်အလက်"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ဝိဂျက်ကို ရွှေ့ရန် တို့ပြီး ဖိထားပါ။"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ဝိဂျက်ကို ရွှေ့ရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..6dff142 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Snarveien er ikke tilgjengelig"</string>
     <string name="home_screen" msgid="5629429142036709174">"Startskjerm"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Delt skjerm"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Splitt øverst"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Splitt til venstre"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Splitt til høyre"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinformasjon for %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Trykk og hold for å flytte en modul."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dobbelttrykk og hold inne for å flytte en modul eller bruke tilpassede handlinger."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..b735d02 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"सर्टकट उपलब्ध छैन"</string>
     <string name="home_screen" msgid="5629429142036709174">"होम"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रिन"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"सिरानतिर स्प्लिट गर्नुहोस्"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"बायाँतिर स्प्लिट गर्नुहोस्"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"दायाँतिर स्प्लिट गर्नुहोस्"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s का हकमा एपसम्बन्धी जानकारी"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"कुनै विजेट सार्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"कुनै विजेट सार्न वा आफ्नो रोजाइका कारबाही प्रयोग गर्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..51730a0 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Snelkoppeling is niet beschikbaar"</string>
     <string name="home_screen" msgid="5629429142036709174">"Startscherm"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Gesplitst scherm"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Gesplitst scherm boven"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Gesplitst scherm links"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Gesplitst scherm rechts"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"App-info voor %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Tik en houd vast om een widget te verplaatsen."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dubbeltik en houd vast om een widget te verplaatsen of aangepaste acties te gebruiken."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..1149e02 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ଶର୍ଟକଟ୍‌ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="home_screen" msgid="5629429142036709174">"ମୂଳପୃଷ୍ଠା"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ସ୍କ୍ରିନ‌କୁ ସ୍ପ୍ଲିଟ୍ କରନ୍ତୁ"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"ଶୀର୍ଷକୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"ବାମପତକୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"ଡାହାଣପଟକୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ପାଇଁ ଆପ ସୂଚନା"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ଏକ ୱିଜେଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ଏକ ୱିଜେଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଦୁଇଥର-ଟାପ୍ କରି ଧରି ରଖନ୍ତୁ କିମ୍ବା କଷ୍ଟମ୍ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..2d70d7f 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="home_screen" msgid="5629429142036709174">"ਮੁੱਖ ਪੰਨਾ"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"ਸਿਖਰ \'ਤੇ ਵੰਡੋ"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"ਖੱਬੇ ਪਾਸੇ ਵੰਡੋ"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"ਸੱਜੇ ਪਾਸੇ ਵੰਡੋ"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ਲਈ ਐਪ ਜਾਣਕਾਰੀ"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ਕਿਸੇ ਵਿਜੇਟ ਨੂੰ ਲਿਜਾਉਣ ਲਈ ਸਪਰਸ਼ ਕਰਕੇ ਰੱਖੋ।"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ਵਿਜੇਟ ਲਿਜਾਉਣ ਲਈ ਜਾਂ ਵਿਉਂਂਤੀਆਂ ਕਾਰਵਾਈਆਂ ਵਰਤਣ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰਕੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..f730e68 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Skrót nie jest dostępny"</string>
     <string name="home_screen" msgid="5629429142036709174">"Ekran główny"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podziel ekran"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Podziel u góry"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Podziel po lewej"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Podziel po prawej"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacje o aplikacji: %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Naciśnij i przytrzymaj, aby przenieść widżet."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Naciśnij dwukrotnie i przytrzymaj, aby przenieść widżet lub użyć działań niestandardowych."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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 d40ed83..0ddef70 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"O atalho não está disponível"</string>
     <string name="home_screen" msgid="5629429142036709174">"Página inicial"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ecrã dividido"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Divisão na parte superior"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Divisão à esquerda"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Divisão à direita"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informações da app para %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Toque sem soltar para mover um widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toque duas vezes sem soltar para mover um widget ou utilizar ações personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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="one"><xliff:g id="WIDGETS_COUNT_0">%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_0">%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(s)}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}one{# atalho(s)}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 +61,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 +90,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="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>
-      <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>
-    </plurals>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{A app {app_name} tem # notificação}one{A app {app_name} tem # notificação(ões)}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..7beedcb 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"O atalho não está disponível"</string>
     <string name="home_screen" msgid="5629429142036709174">"Início"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Tela dividida"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Dividir para cima"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Dividir para a esquerda"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Dividir para a direita"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informações do app %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Toque e mantenha pressionado para mover um widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toque duas vezes e mantenha a tela pressionada para mover um widget ou usar ações personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..14db745 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Comanda rapidă nu este disponibilă"</string>
     <string name="home_screen" msgid="5629429142036709174">"Pagina de pornire"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ecran împărțit"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Împărțiți în sus"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Împărțiți în stânga"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Împărțiți în dreapta"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informații despre aplicație pentru %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Atingeți și țineți apăsat pentru a muta un widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Atingeți de două ori și țineți apăsat pentru a muta un widget sau folosiți acțiuni personalizate."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,16 +40,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 +61,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 +90,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 a4f3c80..4e72d40 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Ярлык недоступен"</string>
     <string name="home_screen" msgid="5629429142036709174">"Главный экран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделить экран"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Приложение сверху"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Приложение слева"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Приложение справа"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Сведения о приложении \"%1$s\""</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Нажмите и удерживайте для переноса виджета."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Чтобы использовать специальные действия или перенести виджет, нажмите на него дважды и удерживайте."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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..17531bd 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"කෙටි මග ලබා ගත නොහැකිය"</string>
     <string name="home_screen" msgid="5629429142036709174">"මුල් පිටුව"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"බෙදුම් තිරය"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"ඉහළ බෙදන්න"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"වම බෙදන්න"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"දකුණ බෙදන්න"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s සඳහා යෙදුම් තතු"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"විජට් එකක් ගෙන යාමට ස්පර්ශ කර අල්ලා ගෙන සිටින්න."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"විජට් එකක් ගෙන යාමට හෝ අභිරුචි ක්‍රියා භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..3f212dc 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Skratky nie sú k dispozícii"</string>
     <string name="home_screen" msgid="5629429142036709174">"Domov"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdeliť obrazovku"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Rozdeliť hore"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Rozdeliť vľavo"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Rozdeliť vpravo"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informácie o aplikácii pre %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Pridržaním presuňte miniaplikáciu."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvojitým klepnutím a pridržaním presuňte miniaplikáciu alebo použite vlastné akcie."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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..485a525 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Bližnjica ni na voljo"</string>
     <string name="home_screen" msgid="5629429142036709174">"Začetni zaslon"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Razdeljen zaslon"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Zgornja stran razdeljenega zaslona"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Leva stran razdeljenega zaslona"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Desna stran razdeljenega zaslona"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Podatki o aplikaciji za: %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Pridržite pripomoček, da ga premaknete."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvakrat se dotaknite pripomočka in ga pridržite, da ga premaknete, ali pa uporabite dejanja po meri."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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..88ed4fa 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shkurtorja nuk është e disponueshme"</string>
     <string name="home_screen" msgid="5629429142036709174">"Ekrani bazë"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekrani i ndarë"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Ndaj lart"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Ndaj majtas"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Ndaj djathtas"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacioni i aplikacionit për %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Prek dhe mbaj shtypur një miniaplikacion për ta zhvendosur."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Trokit dy herë dhe mbaje shtypur një miniapliikacion për ta zhvendosur atë ose për të përdorur veprimet e personalizuara."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..e719db6 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Пречица није доступна"</string>
     <string name="home_screen" msgid="5629429142036709174">"Почетни екран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Подељени екран"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Подели у врху"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Подели лево"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Подели десно"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Информације о апликацији за: %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Додирните и задржите ради померања виџета."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Двапут додирните и задржите да бисте померали виџет или користите прилагођене радње."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
@@ -36,16 +40,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 +61,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 +90,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..e590ec9 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Genvägen är inte tillgänglig"</string>
     <string name="home_screen" msgid="5629429142036709174">"Startskärm"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Delad skärm"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Upptill på delad skärm"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Till vänster på delad skärm"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Till höger på delad skärm"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinformation för %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Tryck länge för att flytta en widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tryck snabbt två gånger och håll kvar för att flytta en widget eller använda anpassade åtgärder."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..fa58703 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Hakuna njia ya mkato"</string>
     <string name="home_screen" msgid="5629429142036709174">"Skrini ya kwanza"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Gawa skrini"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Gawanya sehemu ya juu"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Gawanya sehemu ya kushoto"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Gawanya sehemu ya kulia"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Maelezo ya programu ya %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Gusa na ushikilie ili usogeze wijeti."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Gusa mara mbili na ushikilie ili usogeze wijeti au utumie vitendo maalum."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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 c72fc43..2fa95f8 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ஷார்ட்கட் இல்லை"</string>
     <string name="home_screen" msgid="5629429142036709174">"முகப்பு"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"திரைப் பிரிப்பு"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"மேற்புறமாகப் பிரி"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"இடதுபுறமாகப் பிரி"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"வலதுபுறமாகப் பிரி"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$sக்கான ஆப்ஸ் தகவல்கள்"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"விட்ஜெட்டை நகர்த்தத் தொட்டுப் பிடிக்கவும்."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"விட்ஜெட்டை நகர்த்த இருமுறை தட்டிப் பிடிக்கவும் அல்லது பிரத்தியேகச் செயல்களைப் பயன்படுத்தவும்."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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 4ba895b..ce364bd 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"షార్ట్‌కట్ అందుబాటులో లేదు"</string>
     <string name="home_screen" msgid="5629429142036709174">"మొదటి ట్యాబ్"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"స్క్రీన్‌ను విభజించు"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"స్ప్లిట్ ఎగువన"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"స్ప్లిట్ ఎడమవైపు"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"స్ప్లిట్ కుడివైపు"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s కోసం యాప్ సమాచారం"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"విడ్జెట్‌ను తరలించడానికి తాకి &amp; నొక్కి ఉంచండి."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"విడ్జెట్‌ను తరలించడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కండి &amp; హోల్డ్ చేయి."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..768bff6 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"ทางลัดไม่พร้อมใช้งาน"</string>
     <string name="home_screen" msgid="5629429142036709174">"หน้าแรก"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"แบ่งหน้าจอ"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"แยกไปด้านบน"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"แยกไปทางซ้าย"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"แยกไปทางขวา"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"ข้อมูลแอปสำหรับ %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"แตะค้างไว้เพื่อย้ายวิดเจ็ต"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"แตะสองครั้งค้างไว้เพื่อย้ายวิดเจ็ตหรือใช้การดำเนินการที่กำหนดเอง"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..49465c5 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Hindi available ang shortcut"</string>
     <string name="home_screen" msgid="5629429142036709174">"Home"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Hatiin sa itaas"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Hatiin sa kaliwa"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Hatiin sa kanan"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Impormasyon ng app para sa %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Pindutin nang matagal para ilipat ang widget."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"I-double tap at pindutin nang matagal para ilipat ang widget o gumamit ng mga custom na pagkilos."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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 ec7260a..bb0e1a8 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Kısayol kullanılamıyor"</string>
     <string name="home_screen" msgid="5629429142036709174">"Ana ekran"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Bölünmüş ekran"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Üst tarafta böl"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Sol tarafta böl"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Sağ tarafta böl"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s uygulama bilgileri"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Widget\'ı taşımak için dokunup basılı tutun."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Widget\'ı taşımak veya özel işlemleri kullanmak için iki kez dokunup basılı tutun."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..998ad84 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Ярлик недоступний"</string>
     <string name="home_screen" msgid="5629429142036709174">"Головний екран"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Розділити екран"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Розділити вгорі"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Розділити зліва"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Розділити справа"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Інформація про додаток для %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Натисніть і втримуйте, щоб перемістити віджет."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Двічі натисніть і втримуйте віджет, щоб перемістити його або виконати інші дії."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,18 +40,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 +61,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 +90,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..1b4a3d1 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"شارٹ کٹ دستیاب نہیں ہے"</string>
     <string name="home_screen" msgid="5629429142036709174">"ہوم"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"اسپلٹ اسکرین"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"اوپر کی طرف تقسیم کریں"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"بائیں طرف تقسیم کریں"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"دائیں طرف تقسیم کریں"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"‏%1$s کے لیے ایپ کی معلومات"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"ویجیٹ منتقل کرنے کے لیے ٹچ کریں اور پکڑ کر رکھیں۔"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ویجیٹ کو منتقل کرنے یا حسب ضرورت کارروائیاں استعمال کرنے کے لیے دوبار تھپتھپائیں اور پکڑ کر رکھیں۔"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..8984af8 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Tezkor tugmadan foydalanib bo‘lmaydi"</string>
     <string name="home_screen" msgid="5629429142036709174">"Bosh ekran"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekranni ikkiga ajratish"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Yuqoriga ajratish"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Chapga ajratish"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Oʻngga ajratish"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ilovasi axboroti"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Vidjetni bosib turgan holatda suring."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ikki marta bosib va bosib turgan holatda vidjetni tanlang yoki maxsus amaldan foydalaning."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..6972ee3 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Lối tắt không khả dụng"</string>
     <string name="home_screen" msgid="5629429142036709174">"Màn hình chính"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Chia đôi màn hình"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Chia đôi màn hình lên phía trên cùng"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Chia đôi màn hình về bên trái"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Chia đôi màn hình về bên phải"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Thông tin ứng dụng cho %1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Chạm và giữ để di chuyển một tiện ích."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Nhấn đúp và giữ để di chuyển một tiện ích hoặc sử dụng các thao tác tùy chỉnh."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..c7eb21e 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"无法使用快捷方式"</string>
     <string name="home_screen" msgid="5629429142036709174">"主屏幕"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分屏"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"上分屏"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"左分屏"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"右分屏"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的应用信息"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"轻触并按住微件即可移动该微件。"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"点按两次并按住微件即可移动该微件或使用自定义操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..a278327 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"沒有可用的捷徑"</string>
     <string name="home_screen" msgid="5629429142036709174">"主畫面"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割螢幕"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"頂部分割"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"左側分割"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"右側分割"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的應用程式資料"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"輕觸並按住即可移動小工具。"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"㩒兩下之後㩒住,就可以郁小工具或者用自訂操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..a2cfcc7 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"目前無法使用捷徑"</string>
     <string name="home_screen" msgid="5629429142036709174">"主畫面"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割畫面"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"分割上方畫面"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"分割左側畫面"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"分割右側畫面"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"「%1$s」的應用程式資訊"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"按住即可移動小工具。"</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"輕觸兩下並按住即可移動小工具或使用自訂操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..ca01769 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -28,6 +28,10 @@
     <string name="shortcut_not_available" msgid="2536503539825726397">"Isinqamuleli asitholakali"</string>
     <string name="home_screen" msgid="5629429142036709174">"Ikhaya"</string>
     <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Hlukanisa isikrini"</string>
+    <string name="split_screen_position_top" msgid="1504965011158689649">"Hlukanisa phezulu"</string>
+    <string name="split_screen_position_left" msgid="7537793098851830883">"Hlukanisa ngakwesobunxele"</string>
+    <string name="split_screen_position_right" msgid="1569377524925193369">"Hlukanisa ngakwesokudla"</string>
+    <string name="split_app_info_accessibility" msgid="5475288491241414932">"Ulwazi lwe-App ye-%1$s"</string>
     <string name="long_press_widget_to_add" msgid="3587712543577675817">"Thinta uphinde ubambe ukuze uhambise iwijethi."</string>
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Thepha kabili uphinde ubambe ukuze uhambise iwijethi noma usebenzise izindlela ezingokwezifiso."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -36,14 +40,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 +61,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 +90,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..08570eb 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,19 @@
 
         <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" />
+        <!-- By default all categories are enabled -->
+        <attr name="deviceCategory" format="integer" >
+            <!-- Enable on phone only -->
+            <flag name="phone" value="1" />
+            <!-- Enable on tablets only -->
+            <flag name="tablet" value="2" />
+            <!-- Enable on multi display devices only -->
+            <flag name="multi_display" value="4" />
+        </attr>
 
     </declare-styleable>
 
@@ -169,31 +181,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 +289,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..25911e6 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -136,7 +136,6 @@
     <item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
 
     <item name="swipe_up_scale_start"  type="dimen" format="float">0.88</item>
-    <item name="swipe_up_duration"  type="dimen" format="float">400</item>
 
     <item name="swipe_up_trans_y_dp"  type="dimen" format="float">4.5</item>
     <item name="swipe_up_trans_y_dp_per_s" type="dimen" format="float">3</item>
@@ -147,36 +146,13 @@
     <item name="swipe_up_rect_xy_damping_ratio" type="dimen" format="float">0.8</item>
     <item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
 
-
-    <item name="swipe_up_rect_2_x_damping_ratio" type="dimen" format="float">1</item>
-    <item name="swipe_up_rect_2_x_stiffness" type="dimen" format="float">250</item>
-
-    <item name="swipe_up_rect_2_y_damping_ratio" type="dimen" format="float">1</item>
-    <item name="swipe_up_rect_2_y_stiffness" type="dimen" format="float">600</item>
-
-    <item name="swipe_up_rect_2_y_stiffness_low_swipe_multiplier" type="dimen" format="float">0.8</item>
     <item name="swipe_up_low_swipe_duration_multiplier"  type="dimen" format="float">1</item>
 
     <item name="swipe_up_launcher_alpha_max_progress" type="dimen" format="float">0.85</item>
 
-
-    <item name="c1_a" type="dimen" format="float">0.05</item>
-    <item name="c1_b" type="dimen" format="float">0</item>
-    <item name="c1_c" type="dimen" format="float">0.133333</item>
-    <item name="c1_d" type="dimen" format="float">0.06</item>
-
-    <item name="mp_x" type="dimen" format="float">0.166666</item>
-    <item name="mp_y" type="dimen" format="float">.4</item>
-
-    <item name="c2_a" type="dimen" format="float">0.208333</item>
-    <item name="c2_b" type="dimen" format="float">.82</item>
-    <item name="c2_c" type="dimen" format="float">.25</item>
-    <item name="c2_d" type="dimen" format="float">1</item>
-
-
     <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,36 +160,18 @@
 
     <!-- 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>
         <item>@dimen/swipe_up_scale_start</item>
         <item>@dimen/swipe_up_trans_y_dp</item>
         <item>@dimen/swipe_up_trans_y_dp_per_s</item>
         <item>@dimen/swipe_up_trans_y_damping</item>
         <item>@dimen/swipe_up_trans_y_stiffness</item>
-        <item>@dimen/swipe_up_rect_2_x_damping_ratio</item>
-        <item>@dimen/swipe_up_rect_2_x_stiffness</item>
-        <item>@dimen/swipe_up_rect_2_y_damping_ratio</item>
-        <item>@dimen/swipe_up_rect_2_y_stiffness</item>
         <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/c1_a</item>
-        <item>@dimen/c1_b</item>
-        <item>@dimen/c1_c</item>
-        <item>@dimen/c1_d</item>
-
-        <item>@dimen/mp_x</item>
-        <item>@dimen/mp_y</item>
-
-        <item>@dimen/c2_a</item>
-        <item>@dimen/c2_b</item>
-        <item>@dimen/c2_c</item>
-        <item>@dimen/c2_d</item>
+        <item>@dimen/swipe_up_max_velocity</item>
     </array>
 
     <string-array name="filtered_components" ></string-array>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a1e4cd9..6d223e7 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,8 +103,10 @@
     <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>
+    <dimen name="all_apps_height_extra">6dp</dimen>
+
     <!-- The size of corner radius of the arrow in the arrow toast. -->
     <dimen name="arrow_toast_corner_radius">2dp</dimen>
     <dimen name="arrow_toast_elevation">2dp</dimen>
@@ -126,7 +126,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 +142,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 +176,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>
@@ -304,6 +300,7 @@
     <dimen name="snackbar_elevation">3dp</dimen>
     <dimen name="snackbar_min_text_size">12sp</dimen>
     <dimen name="snackbar_max_text_size">14sp</dimen>
+    <dimen name="snackbar_max_width">504dp</dimen>
 
 <!-- Developer Options -->
     <dimen name="developer_options_filter_margins">10dp</dimen>
@@ -317,16 +314,41 @@
 
 <!-- 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 -->
+<!-- Base Swipe Detector, speed in dp/s -->
+    <dimen name="base_swift_detector_fling_release_velocity">1dp</dimen>
+
+<!-- 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_button_spacing">0dp</dimen>
+    <dimen name="overview_actions_button_spacing_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_side_margin_portrait">0dp</dimen>
+    <dimen name="overview_grid_side_margin_landscape">0dp</dimen>
+    <dimen name="overview_grid_row_spacing">0dp</dimen>
+    <dimen name="overview_page_spacing">0dp</dimen>
+    <dimen name="overview_page_spacing_grid_portrait">0dp</dimen>
+    <dimen name="overview_page_spacing_grid_landscape">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..508caff 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -17,7 +17,21 @@
 <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" />
+    <!--  Used for A11y actions in staged split to identify each task uniquely  -->
+    <item type="id" name="split_topLeft_appInfo" />
+    <item type="id" name="split_bottomRight_appInfo" />
+
+
+    <!--  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..868b5f3 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
 *
@@ -41,9 +40,10 @@
     <!-- Options for recent tasks -->
     <!-- Title for an option to enter split screen mode for a given app -->
     <string name="recent_task_option_split_screen">Split screen</string>
-    <string translatable="false" name="split_screen_position_top">Pin to top</string>
-    <string translatable="false" name="split_screen_position_left">Pin to left</string>
-    <string translatable="false" name="split_screen_position_right">Pin to right</string>
+    <string name="split_screen_position_top">Split top</string>
+    <string name="split_screen_position_left">Split left</string>
+    <string name="split_screen_position_right">Split right</string>
+    <string name="split_app_info_accessibility">App info for %1$s</string>
 
     <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
@@ -67,17 +67,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 +132,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 +195,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 +215,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] -->
@@ -250,7 +252,7 @@
     <string name="wallpaper_button_text">Wallpapers</string>
     <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
     <string name="styles_wallpaper_button_text">Wallpaper &amp; style</string>
-    <!-- Text for settings button [CHAR LIMIT=30]-->
+    <!-- Text for settings button [CHAR LIMIT=20]-->
     <string name="settings_button_text">Home settings</string>
     <!-- Message shown when a feature is disabled by the administrator -->
     <string name="msg_disabled_by_admin">Disabled by your admin</string>
@@ -308,7 +310,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/default_workspace_6x5.xml b/res/xml/default_workspace_6x5.xml
new file mode 100644
index 0000000..b078cfd
--- /dev/null
+++ b/res/xml/default_workspace_6x5.xml
@@ -0,0 +1,76 @@
+<?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.
+-->
+
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+
+    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+    <!-- Mail Calendar Gallery Store Internet Camera -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+        <favorite launcher:uri="mailto:" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="1"
+        launcher:x="1"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_CALENDAR;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="2"
+        launcher:x="2"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
+        <favorite launcher:uri="#Intent;type=images/*;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="3"
+        launcher:x="3"
+        launcher:y="0" >
+        <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>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="4"
+        launcher:x="4"
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+        <favorite launcher:uri="http://www.example.com/" />
+    </resolve>
+
+    <!-- Resolve camera intent if GoogleCamera is not available e.g. on emulator -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="5"
+        launcher:x="5"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+    </resolve>
+
+</favorites>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 256999c..08698e7 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -25,7 +25,8 @@
         launcher:numFolderColumns="3"
         launcher:numHotseatIcons="3"
         launcher:dbFile="launcher_3_by_3.db"
-        launcher:defaultLayoutId="@xml/default_workspace_3x3" >
+        launcher:defaultLayoutId="@xml/default_workspace_3x3"
+        launcher:deviceCategory="phone|multi_display" >
 
         <display-option
             launcher:name="Super Short Stubby"
@@ -53,7 +54,8 @@
         launcher:numFolderColumns="4"
         launcher:numHotseatIcons="4"
         launcher:dbFile="launcher_4_by_4.db"
-        launcher:defaultLayoutId="@xml/default_workspace_4x4" >
+        launcher:defaultLayoutId="@xml/default_workspace_4x4"
+        launcher:deviceCategory="phone|multi_display" >
 
         <display-option
             launcher:name="Short Stubby"
@@ -105,7 +107,8 @@
         launcher:numFolderColumns="4"
         launcher:numHotseatIcons="5"
         launcher:dbFile="launcher.db"
-        launcher:defaultLayoutId="@xml/default_workspace_5x5" >
+        launcher:defaultLayoutId="@xml/default_workspace_5x5"
+        launcher:deviceCategory="phone|multi_display" >
 
         <display-option
             launcher:name="Large Phone"
@@ -121,7 +124,7 @@
             launcher:minHeightDps="694"
             launcher:iconImageSize="56"
             launcher:iconTextSize="14.4"
-            launcher:canBeDefault="split_display" />
+            launcher:canBeDefault="true" />
 
         <display-option
             launcher:name="Shorter Stubby"
@@ -133,4 +136,32 @@
 
     </grid-option>
 
+    <grid-option
+        launcher:name="6_by_5"
+        launcher:numRows="5"
+        launcher:numColumns="6"
+        launcher:numFolderRows="3"
+        launcher:numFolderColumns="3"
+        launcher:numHotseatIcons="6"
+        launcher:numAllAppsColumns="6"
+        launcher:dbFile="launcher_6_by_5.db"
+        launcher:defaultLayoutId="@xml/default_workspace_6x5"
+        launcher:deviceCategory="tablet" >
+
+        <display-option
+            launcher:name="Tablet"
+            launcher:minWidthDps="900"
+            launcher:minHeightDps="820"
+            launcher:minCellHeightDps="104"
+            launcher:minCellWidthDps="80"
+            launcher:iconImageSize="60"
+            launcher:iconTextSize="14"
+            launcher:borderSpaceDps="16"
+            launcher:allAppsIconSize="60"
+            launcher:allAppsIconTextSize="14"
+            launcher:allAppsCellSpacingDps="16"
+            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..163b442 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,9 @@
 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;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.util.Property;
@@ -65,9 +69,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 +91,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
@@ -128,6 +142,7 @@
     private final CheckLongPressHelper mLongPressHelper;
 
     private final boolean mLayoutHorizontal;
+    private final boolean mIsRtl;
     private final int mIconSize;
 
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -155,6 +170,7 @@
     private HandlerRunnable mIconLoadRequest;
 
     private boolean mEnableIconUpdateAnimation = false;
+    private BubbleTextHolder mBubbleTextHolder;
 
     public BubbleTextView(Context context) {
         this(context, null, 0);
@@ -171,6 +187,8 @@
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
         mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
+        mIsRtl = (getResources().getConfiguration().getLayoutDirection()
+                == View.LAYOUT_DIRECTION_RTL);
         DeviceProfile grid = mActivity.getDeviceProfile();
 
         mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
@@ -254,9 +272,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 +308,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 +319,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 +339,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 +347,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 +429,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 +452,7 @@
         }
     }
 
-    void clearPressedBackground() {
+    public void clearPressedBackground() {
         setPressed(false);
         setStayPressed(false);
     }
@@ -424,6 +469,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);
@@ -471,19 +585,29 @@
         return mDotInfo != null;
     }
 
+    /**
+     * Get the icon bounds on the view depending on the layout type.
+     */
     public void getIconBounds(Rect outBounds) {
-        getIconBounds(this, outBounds, mIconSize);
+        getIconBounds(mIconSize, outBounds);
     }
 
-    public static void getIconBounds(View iconView, Rect outBounds, int iconSize) {
-        int top = iconView.getPaddingTop();
-        int left = (iconView.getWidth() - iconSize) / 2;
-        int right = left + iconSize;
-        int bottom = top + iconSize;
-        outBounds.set(left, top, right, bottom);
+    /**
+     * Get the icon bounds on the view depending on the layout type.
+     */
+    public void getIconBounds(int iconSize, Rect outBounds) {
+        Utilities.setRectToViewCenter(this, iconSize, outBounds);
+        if (mLayoutHorizontal) {
+            if (mIsRtl) {
+                outBounds.offsetTo(getWidth() - iconSize - getPaddingRight(), outBounds.top);
+            } else {
+                outBounds.offsetTo(getPaddingLeft(), outBounds.top);
+            }
+        } else {
+            outBounds.offsetTo(outBounds.left, getPaddingTop());
+        }
     }
 
-
     /**
      * Sets whether to vertically center the content.
      */
@@ -662,14 +786,14 @@
                     invalidate();
                 }
             }
-            if (itemInfo.contentDescription != null) {
+            if (!TextUtils.isEmpty(itemInfo.contentDescription)) {
                 if (itemInfo.isDisabled()) {
                     setContentDescription(getContext().getString(R.string.disabled_app_label,
                             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 +893,7 @@
             } else if (info instanceof PackageItemInfo) {
                 applyFromItemInfoWithIcon((PackageItemInfo) info);
             } else if (info instanceof SearchActionItemInfo) {
-                applyFromSearchActionItemInfo((SearchActionItemInfo) info);
+                applyFromItemInfoWithIcon((SearchActionItemInfo) info);
             }
 
             mDisableRelayout = false;
@@ -799,8 +923,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 +960,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;
     }
@@ -844,8 +994,7 @@
 
     @Override
     public void getWorkspaceVisualDragBounds(Rect bounds) {
-        DeviceProfile grid = mActivity.getDeviceProfile();
-        BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);
+        getIconBounds(mIconSize, bounds);
     }
 
     private int getIconSizeForDisplay(int display) {
@@ -853,15 +1002,16 @@
         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;
         }
     }
 
     public void getSourceVisualDragBounds(Rect bounds) {
-        BubbleTextView.getIconBounds(this, bounds, getIconSizeForDisplay(mDisplay));
+        getIconBounds(mIconSize, bounds);
     }
 
     @Override
@@ -884,4 +1034,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..38d5077 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -108,8 +108,8 @@
         // We do not set the drawable in the xml as that inflates two drawables corresponding to
         // drawableLeft and drawableStart.
         mDrawable = getContext().getDrawable(resId).mutate();
-        mDrawable.setBounds(0, 0, mDrawableSize, mDrawableSize);
         mDrawable.setTintList(getTextColors());
+        centerIcon();
         setCompoundDrawablesRelative(mDrawable, null, null, null);
     }
 
@@ -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);
     }
 
@@ -284,7 +277,7 @@
         }
 
         final int top = to.top + (getMeasuredHeight() - height) / 2;
-        final int bottom = top +  height;
+        final int bottom = top + height;
 
         to.set(left, top, right, bottom);
 
@@ -296,6 +289,12 @@
         return to;
     }
 
+    private void centerIcon() {
+        int x = mTextVisible ? 0
+                : (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 - mDrawableSize / 2;
+        mDrawable.setBounds(x, 0, x + mDrawableSize, mDrawableSize);
+    }
+
     @Override
     public void onClick(View v) {
         mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null);
@@ -306,12 +305,19 @@
         if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
             mTextVisible = isVisible;
             setText(newText);
+            centerIcon();
             setCompoundDrawablesRelative(mDrawable, null, null, null);
             int drawablePadding = mTextVisible ? mDrawablePadding : 0;
             setCompoundDrawablePadding(drawablePadding);
         }
     }
 
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        centerIcon();
+    }
+
     public void setToolTipLocation(int location) {
         mToolTipLocation = location;
         hideTooltip();
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index a6adfc4..adb1613 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -18,8 +18,9 @@
 
 import static android.animation.ValueAnimator.areAnimatorsEnabled;
 
-import static com.android.launcher3.Utilities.getBoundsForViewInDragLayer;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
+import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
+import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -38,7 +39,6 @@
 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.Parcelable;
 import android.util.ArrayMap;
@@ -62,6 +62,7 @@
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.CellAndSpan;
@@ -92,7 +93,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 +238,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 +310,7 @@
 
         mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context, mContainerType);
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
-                mBorderSpacing);
+                mBorderSpace);
         addView(mShortcutsAndWidgets);
     }
 
@@ -369,7 +370,7 @@
         mFixedCellWidth = mCellWidth = width;
         mFixedCellHeight = mCellHeight = height;
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
-                mBorderSpacing);
+                mBorderSpace);
     }
 
     public void setGridSize(int x, int y) {
@@ -379,7 +380,7 @@
         mTmpOccupied = new GridOccupancy(mCountX, mCountY);
         mTempRectStack.clear();
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
-                mBorderSpacing);
+                mBorderSpace);
         requestLayout();
     }
 
@@ -443,18 +444,43 @@
         }
 
         if (DEBUG_VISUALIZE_OCCUPIED) {
-            int[] pt = new int[2];
-            ColorDrawable cd = new ColorDrawable(Color.RED);
-            cd.setBounds(0, 0,  mCellWidth, mCellHeight);
-            for (int i = 0; i < mCountX; i++) {
-                for (int j = 0; j < mCountY; j++) {
-                    if (mOccupied.cells[i][j]) {
-                        cellToPoint(i, j, pt);
-                        canvas.save();
-                        canvas.translate(pt[0], pt[1]);
-                        cd.draw(canvas);
-                        canvas.restore();
+            Rect cellBounds = new Rect();
+            // Will contain the bounds of the cell including spacing between cells.
+            Rect cellBoundsWithSpacing = new Rect();
+            int[] targetCell = new int[2];
+            int[] cellCenter = new int[2];
+            Paint debugPaint = new Paint();
+            debugPaint.setStrokeWidth(Utilities.dpToPx(1));
+            for (int x = 0; x < mCountX; x++) {
+                for (int y = 0; y < mCountY; y++) {
+                    if (!mOccupied.cells[x][y]) {
+                        continue;
                     }
+                    targetCell[0] = x;
+                    targetCell[1] = y;
+
+                    boolean canCreateFolder = canCreateFolder(getChildAt(x, y));
+                    cellToRect(x, y, 1, 1, cellBounds);
+                    cellBoundsWithSpacing.set(cellBounds);
+                    cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2);
+                    getWorkspaceCellVisualCenter(x, y, cellCenter);
+
+                    canvas.save();
+                    canvas.clipRect(cellBoundsWithSpacing);
+
+                    // Draw reorder drag target.
+                    debugPaint.setColor(Color.RED);
+                    canvas.drawCircle(cellCenter[0], cellCenter[1], getReorderRadius(targetCell),
+                            debugPaint);
+
+                    // Draw folder creation drag target.
+                    if (canCreateFolder) {
+                        debugPaint.setColor(Color.GREEN);
+                        canvas.drawCircle(cellCenter[0], cellCenter[1],
+                                getFolderCreationRadius(targetCell), debugPaint);
+                    }
+
+                    canvas.restore();
                 }
             }
         }
@@ -483,6 +509,14 @@
     }
 
     /**
+     * Returns whether dropping an icon on the given View can create (or add to) a folder.
+     */
+    private boolean canCreateFolder(View child) {
+        return child instanceof DraggableView
+                && ((DraggableView) child).getViewType() == DRAGGABLE_ICON;
+    }
+
+    /**
      * Indicates the progress of the Workspace entering the SpringLoaded state; allows the
      * CellLayout to update various visuals for this state.
      *
@@ -542,9 +576,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 +602,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);
@@ -818,7 +852,7 @@
     }
 
     /**
-     * Given a cell coordinate and span return the point that represents the center of the regio
+     * Given a cell coordinate and span return the point that represents the center of the region
      *
      * @param cellX X coordinate of the cell
      * @param cellY Y coordinate of the cell
@@ -831,11 +865,65 @@
         result[1] = mTempRect.centerY();
     }
 
-    public float getDistanceFromCell(float x, float y, int[] cell) {
-        cellToCenterPoint(cell[0], cell[1], mTmpPoint);
+    /**
+     * Returns the distance between the given coordinate and the visual center of the given cell.
+     */
+    public float getDistanceFromWorkspaceCellVisualCenter(float x, float y, int[] cell) {
+        getWorkspaceCellVisualCenter(cell[0], cell[1], mTmpPoint);
         return (float) Math.hypot(x - mTmpPoint[0], y - mTmpPoint[1]);
     }
 
+    private void getWorkspaceCellVisualCenter(int cellX, int cellY, int[] outPoint) {
+        View child = getChildAt(cellX, cellY);
+        if (child instanceof DraggableView) {
+            DraggableView draggableChild = (DraggableView) child;
+            if (draggableChild.getViewType() == DRAGGABLE_ICON) {
+                cellToPoint(cellX, cellY, outPoint);
+                draggableChild.getWorkspaceVisualDragBounds(mTempRect);
+                mTempRect.offset(outPoint[0], outPoint[1]);
+                outPoint[0] = mTempRect.centerX();
+                outPoint[1] = mTempRect.centerY();
+                return;
+            }
+        }
+        cellToCenterPoint(cellX, cellY, outPoint);
+    }
+
+    /**
+     * Returns the max distance from the center of a cell that can accept a drop to create a folder.
+     */
+    public float getFolderCreationRadius(int[] targetCell) {
+        DeviceProfile grid = mActivity.getDeviceProfile();
+        float iconVisibleRadius = ICON_VISIBLE_AREA_FACTOR * grid.iconSizePx / 2;
+        // Halfway between reorder radius and icon.
+        return (getReorderRadius(targetCell) + iconVisibleRadius) / 2;
+    }
+
+    /**
+     * Returns the max distance from the center of a cell that will start to reorder on drag over.
+     */
+    public float getReorderRadius(int[] targetCell) {
+        int[] centerPoint = mTmpPoint;
+        getWorkspaceCellVisualCenter(targetCell[0], targetCell[1], centerPoint);
+
+        Rect cellBoundsWithSpacing = mTempRect;
+        cellToRect(targetCell[0], targetCell[1], 1, 1, cellBoundsWithSpacing);
+        cellBoundsWithSpacing.inset(-mBorderSpace.x / 2, -mBorderSpace.y / 2);
+
+        if (canCreateFolder(getChildAt(targetCell[0], targetCell[1]))) {
+            // Take only the circle in the smaller dimension, to ensure we don't start reordering
+            // too soon before accepting a folder drop.
+            int minRadius = centerPoint[0] - cellBoundsWithSpacing.left;
+            minRadius = Math.min(minRadius, centerPoint[1] - cellBoundsWithSpacing.top);
+            minRadius = Math.min(minRadius, cellBoundsWithSpacing.right - centerPoint[0]);
+            minRadius = Math.min(minRadius, cellBoundsWithSpacing.bottom - centerPoint[1]);
+            return minRadius;
+        }
+        // Take up the entire cell, including space between this cell and the adjacent ones.
+        return (float) Math.hypot(cellBoundsWithSpacing.width() / 2f,
+                cellBoundsWithSpacing.height() / 2f);
+    }
+
     public int getCellWidth() {
         return mCellWidth;
     }
@@ -859,15 +947,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 +1009,7 @@
      */
     public int getUnusedHorizontalSpace() {
         return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth)
-                - ((mCountX - 1) * mBorderSpacing);
+                - ((mCountX - 1) * mBorderSpace.x);
     }
 
     @Override
@@ -1071,18 +1159,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 +2230,7 @@
         mShakeAnimators.clear();
     }
 
-    private void commitTempPlacement() {
+    private void commitTempPlacement(View dragView) {
         mTmpOccupied.copyTo(mOccupied);
 
         int screenId = Launcher.cast(mActivity).getWorkspace().getIdForScreen(this);
@@ -2166,7 +2248,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 +2410,7 @@
             animateItemsToSolution(swapSolution, dragView, commit);
 
             if (commit) {
-                commitTempPlacement();
+                commitTempPlacement(null);
                 completeAndClearReorderPreviewAnimations();
                 setItemPlacementDirty(false);
             } else {
@@ -2422,7 +2504,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 +2681,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 +2704,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 +2825,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 +2850,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 +2961,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..d2b9dfe 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,14 +16,10 @@
 
 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;
 import static com.android.launcher3.Utilities.pxFromSp;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
-import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -33,11 +29,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 +38,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 +49,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;
@@ -65,7 +61,6 @@
     public final boolean isPhone;
     public final boolean transposeLayoutWithOrientation;
     public final boolean isTwoPanels;
-    public final boolean allowRotation;
 
     // Device properties in current orientation
     public final boolean isLandscape;
@@ -81,6 +76,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 +94,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 +134,8 @@
     public int folderIconOffsetYPx;
 
     // Folder content
-    public int folderCellLayoutBorderSpacingPx;
+    public Point folderCellLayoutBorderSpacePx;
+    public int folderCellLayoutBorderSpaceOriginalPx;
     public int folderContentPaddingLeftRight;
     public int folderContentPaddingTop;
 
@@ -163,25 +160,37 @@
     // 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 final int overviewActionsButtonSpacing;
+    public int overviewPageSpacing;
+    public int overviewRowSpacing;
+    public int overviewGridSideMargin;
 
     // Widgets
     public final PointF appWidgetScale = new PointF(1.0f, 1.0f);
@@ -202,15 +211,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,15 +238,10 @@
         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
-        // original dimensions to determine if rotation is allowed of not.
-        float originalSmallestWidth = dpiFromPx(Math.min(widthPx, heightPx), DENSITY_DEVICE_STABLE);
-        allowRotation = originalSmallestWidth >= MIN_TABLET_WIDTH;
-        // Tablet UI does not support emulated landscape.
-        isTablet = allowRotation && info.isTablet(windowBounds);
+        isTablet = info.isTablet(windowBounds);
         isPhone = !isTablet;
         isTwoPanels = isTablet && useTwoPanels;
 
@@ -250,31 +255,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 +289,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 +305,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 +348,62 @@
         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);
+                overviewPageSpacing = res.getDimensionPixelSize(
+                        R.dimen.overview_page_spacing_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);
+                overviewPageSpacing = res.getDimensionPixelSize(
+                        R.dimen.overview_page_spacing_grid_portrait);
+            }
+            overviewActionsButtonSpacing = res.getDimensionPixelSize(
+                    R.dimen.overview_actions_button_spacing_grid);
+        } else {
+            overviewActionsTopMarginGesturePx = res.getDimensionPixelSize(
+                    R.dimen.overview_actions_margin_gesture);
+            overviewActionsBottomMarginGesturePx = overviewActionsTopMarginGesturePx;
+            overviewActionsButtonSpacing = res.getDimensionPixelSize(
+                    R.dimen.overview_actions_button_spacing);
+            overviewPageSpacing = res.getDimensionPixelSize(R.dimen.overview_page_spacing);
+        }
         overviewActionsMarginThreeButtonPx = res.getDimensionPixelSize(
-                R.dimen.overview_actions_bottom_margin_three_button);
+                R.dimen.overview_actions_margin_three_button);
+        // Grid task's top margin is only overviewTaskIconSizePx + overviewTaskMarginGridPx, but
+        // overviewTaskThumbnailTopMarginPx is applied to all TaskThumbnailView, so exclude the
+        // extra  margin when calculating row spacing.
+        int extraTopMargin = overviewTaskThumbnailTopMarginPx - overviewTaskIconSizePx
+                - overviewTaskMarginGridPx;
+        overviewRowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing)
+                - extraTopMargin;
+        overviewGridSideMargin = isLandscape
+                ? res.getDimensionPixelSize(R.dimen.overview_grid_side_margin_landscape)
+                : res.getDimensionPixelSize(R.dimen.overview_grid_side_margin_portrait);
 
         // Calculate all of the remaining variables.
         extraSpace = updateAvailableDimensions(res);
@@ -420,6 +469,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 +492,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 +522,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 +592,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 +623,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 +643,7 @@
     }
 
     private int getCellLayoutHeight() {
-        return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacingPx * (inv.numRows - 1));
+        return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacePx.y * (inv.numRows - 1));
     }
 
     /**
@@ -569,24 +656,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 +693,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 +706,7 @@
             allAppsCellHeightPx = getCellSize().y;
         }
         allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
+        updateAllAppsWidth();
 
         if (isVerticalLayout) {
             hideWorkspaceLabelsIfNotEnoughSpace();
@@ -652,14 +743,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 +762,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 +779,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,16 +818,29 @@
         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 screenWidthPx = getWorkspaceWidth(padding);
+        result.x = calculateCellWidth(screenWidthPx, cellLayoutBorderSpacePx.x, numColumns);
         result.y = calculateCellHeight(availableHeightPx - padding.y
-                - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows);
+                - cellLayoutBottomPaddingPx, cellLayoutBorderSpacePx.y, inv.numRows);
         return result;
     }
 
+    public int getWorkspaceWidth() {
+        return getWorkspaceWidth(getTotalWorkspacePadding());
+    }
+
+    public int getWorkspaceWidth(Point workspacePadding) {
+        int cellLayoutTotalPadding =
+                isTwoPanels ? 4 * cellLayoutPaddingLeftRightPx : 2 * cellLayoutPaddingLeftRightPx;
+        return availableWidthPx - workspacePadding.x - cellLayoutTotalPadding;
+    }
+
     public Point getTotalWorkspacePadding() {
         updateWorkspacePadding();
         return new Point(workspacePadding.left + workspacePadding.right,
@@ -757,38 +864,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 +888,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 +933,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 +980,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;
     }
@@ -901,7 +1041,6 @@
         writer.println(prefix + "DeviceProfile:");
         writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
 
-        writer.println(prefix + "\tallowRotation:" + allowRotation);
         writer.println(prefix + "\tisTablet:" + isTablet);
         writer.println(prefix + "\tisPhone:" + isPhone);
         writer.println(prefix + "\ttransposeLayoutWithOrientation:"
@@ -923,11 +1062,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 +1073,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 +1088,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 +1113,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 +1126,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..9fb14f6 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,16 @@
     public ButtonDropTarget[] getDropTargets() {
         return mDropTargets;
     }
+
+    @Override
+    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        if (TestProtocol.sDebugTracing) {
+            if (visibility == VISIBLE) {
+                Log.d(TestProtocol.NO_DROP_TARGET, "9");
+            } else {
+                Log.d(TestProtocol.NO_DROP_TARGET, "Hiding drop target", new Exception());
+            }
+        }
+    }
 }
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..ff7a90c 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;
@@ -42,10 +42,12 @@
 import android.util.Xml;
 import android.view.Display;
 
+import androidx.annotation.IntDef;
 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;
@@ -57,7 +59,10 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -68,11 +73,12 @@
     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;
+    @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 String KEY_IDP_GRID_NAME = "idp_grid_name";
 
@@ -86,6 +92,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 +112,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 @DeviceType int deviceType;
 
-    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 +160,8 @@
      */
     public List<DeviceProfile> supportedProfiles = Collections.EMPTY_LIST;
 
-    @Nullable public DevicePaddings devicePaddings;
+    @Nullable
+    public DevicePaddings devicePaddings;
 
     public Point defaultWallpaperSize;
     public Rect defaultWidgetPadding;
@@ -153,34 +169,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 +179,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);
@@ -222,28 +208,72 @@
         String gridName = getCurrentGridName(context);
 
         // Get the display info based on default display and interpolate it to existing display
+        Info defaultInfo = DisplayController.INSTANCE.get(context).getInfo();
+        @DeviceType int defaultDeviceType = getDeviceType(defaultInfo);
         DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
-                DisplayController.INSTANCE.get(context).getInfo(),
-                getPredefinedDeviceProfiles(context, gridName, false), false);
+                defaultInfo,
+                getPredefinedDeviceProfiles(context, gridName, defaultDeviceType,
+                        /*allowDisabledGrid=*/false),
+                defaultDeviceType);
 
         Info myInfo = new Info(context, display);
+        @DeviceType int deviceType = getDeviceType(myInfo);
         DisplayOption myDisplayOption = invDistWeightedInterpolate(
-                myInfo, getPredefinedDeviceProfiles(context, gridName, false), false);
+                myInfo,
+                getPredefinedDeviceProfiles(context, gridName, deviceType,
+                        /*allowDisabledGrid=*/false),
+                deviceType);
 
         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;
 
-        initGrid(context, myInfo, result, false);
+        System.arraycopy(defaultDisplayOption.minCellSize, 0, result.minCellSize, 0,
+                COUNT_SIZES);
+        System.arraycopy(defaultDisplayOption.borderSpaces, 0, result.borderSpaces, 0,
+                COUNT_SIZES);
+
+        initGrid(context, myInfo, result, deviceType);
+    }
+
+    /**
+     * 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);
+        }
+    }
+
+    private static @DeviceType int getDeviceType(Info displayInfo) {
+        // Each screen has two profiles (portrait/landscape), so devices with four or more
+        // supported profiles implies two or more internal displays.
+        if (displayInfo.supportedBounds.size() >= 4 && ENABLE_TWO_PANEL_HOME.get()) {
+            return TYPE_MULTI_DISPLAY;
+        } else if (displayInfo.supportedBounds.stream().allMatch(displayInfo::isTablet)) {
+            return TYPE_TABLET;
+        } else {
+            return TYPE_PHONE;
+        }
     }
 
     public static String getCurrentGridName(Context context) {
@@ -253,29 +283,19 @@
 
     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();
+        @DeviceType int deviceType = getDeviceType(displayInfo);
 
         ArrayList<DisplayOption> allOptions =
-                getPredefinedDeviceProfiles(context, gridName, isSplitDisplay);
+                getPredefinedDeviceProfiles(context, gridName, deviceType,
+                        RestoreDbTask.isPending(context));
         DisplayOption displayOption =
-                invDistWeightedInterpolate(displayInfo, allOptions, isSplitDisplay);
-        initGrid(context, displayInfo, displayOption, isSplitDisplay);
+                invDistWeightedInterpolate(displayInfo, allOptions, deviceType);
+        initGrid(context, displayInfo, displayOption, deviceType);
         return displayOption.grid.name;
     }
 
-    private void initGrid(
-            Context context, Info displayInfo, DisplayOption displayOption,
-            boolean isSplitDisplay) {
+    private void initGrid(Context context, Info displayInfo, DisplayOption displayOption,
+            @DeviceType int deviceType) {
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         GridOption closestProfile = displayOption.grid;
         numRows = closestProfile.numRows;
@@ -287,34 +307,38 @@
         numFolderColumns = closestProfile.numFolderColumns;
         isScalable = closestProfile.isScalable;
         devicePaddingId = closestProfile.devicePaddingId;
+        this.deviceType = deviceType;
 
         mExtraAttrs = closestProfile.extraAttrs;
 
-        iconSize = displayOption.iconSize;
-        landscapeIconSize = displayOption.landscapeIconSize;
-        iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
-        iconTextSize = displayOption.iconTextSize;
-        landscapeIconTextSize = displayOption.landscapeIconTextSize;
+        iconSize = displayOption.iconSizes;
+        float maxIconSize = iconSize[0];
+        for (int i = 1; i < iconSize.length; i++) {
+            maxIconSize = Math.max(maxIconSize, iconSize[i]);
+        }
+        iconBitmapSize = ResourceUtils.pxFromDp(maxIconSize, 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
+        numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
                 ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
 
         numAllAppsColumns = closestProfile.numAllAppsColumns;
-        numDatabaseAllAppsColumns = isSplitDisplay
+        numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY
                 ? 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) {
@@ -329,7 +353,7 @@
         defaultWallpaperSize = new Point(displayInfo.currentSize);
         for (WindowBounds bounds : displayInfo.supportedBounds) {
             localSupportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
-                    .setUseTwoPanels(isSplitDisplay)
+                    .setUseTwoPanels(deviceType == TYPE_MULTI_DISPLAY)
                     .setWindowBounds(bounds).build());
 
             // Wallpaper size should be the maximum of the all possible sizes Launcher expects
@@ -352,11 +376,6 @@
         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
     }
 
-    @Nullable
-    public TypedValue getAttrValue(int attr) {
-        return mExtraAttrs == null ? null : mExtraAttrs.get(attr);
-    }
-
     public void addOnChangeListener(OnIDPChangeListener listener) {
         mChangeListeners.add(listener);
     }
@@ -372,19 +391,29 @@
         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) {
+    private static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context,
+            String gridName, @DeviceType int deviceType, boolean allowDisabledGrid) {
         ArrayList<DisplayOption> profiles = new ArrayList<>();
+
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
             final int depth = parser.getDepth();
             int type;
@@ -393,28 +422,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),
+                            deviceType);
+                    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 +465,36 @@
         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<>();
+
+        try (XmlResourceParser parser = context.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())) {
+                    GridOption option =
+                            new GridOption(context, Xml.asAttributeSet(parser), deviceType);
+                    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,
@@ -474,12 +533,12 @@
     }
 
     private static DisplayOption invDistWeightedInterpolate(
-            Info displayInfo, ArrayList<DisplayOption> points, boolean isSplitDisplay) {
+            Info displayInfo, ArrayList<DisplayOption> points, @DeviceType int deviceType) {
         int minWidthPx = Integer.MAX_VALUE;
         int minHeightPx = Integer.MAX_VALUE;
         for (WindowBounds bounds : displayInfo.supportedBounds) {
             boolean isTablet = displayInfo.isTablet(bounds);
-            if (isTablet && isSplitDisplay) {
+            if (isTablet && deviceType == TYPE_MULTI_DISPLAY) {
                 // For split displays, take half width per page
                 minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2);
                 minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
@@ -502,37 +561,48 @@
                 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 all bitmap sizes are not larger than
+        // predefined size to avoid cache invalidation
+        for (int i = INDEX_DEFAULT; i < COUNT_SIZES; i++) {
+            out.iconSizes[i] = Math.min(out.iconSizes[i], closestPoint.iconSizes[i]);
+        }
+
+        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 +631,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 +642,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 +654,7 @@
         /**
          * Called when the device provide changes
          */
-        void onIdpChanged(InvariantDeviceProfile profile);
+        void onIdpChanged(boolean modelPropertiesChanged);
     }
 
 
@@ -591,9 +662,16 @@
 
         public static final String TAG_NAME = "grid-option";
 
+        private static final int DEVICE_CATEGORY_PHONE = 1 << 0;
+        private static final int DEVICE_CATEGORY_TABLET = 1 << 1;
+        private static final int DEVICE_CATEGORY_MULTI_DISPLAY = 1 << 2;
+        private static final int DEVICE_CATEGORY_ALL =
+                DEVICE_CATEGORY_PHONE | DEVICE_CATEGORY_TABLET | DEVICE_CATEGORY_MULTI_DISPLAY;
+
         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 +691,7 @@
 
         private final SparseArray<TypedValue> extraAttrs;
 
-        public GridOption(Context context, AttributeSet attrs) {
+        public GridOption(Context context, AttributeSet attrs, @DeviceType int deviceType) {
             TypedArray a = context.obtainStyledAttributes(
                     attrs, R.styleable.GridDisplayOption);
             name = a.getString(R.styleable.GridDisplayOption_name);
@@ -621,8 +699,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(deviceType == TYPE_MULTI_DISPLAY && 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 +726,16 @@
             devicePaddingId = a.getResourceId(
                     R.styleable.GridDisplayOption_devicePaddingId, 0);
 
+            int deviceCategory = a.getInt(R.styleable.GridDisplayOption_deviceCategory,
+                    DEVICE_CATEGORY_ALL);
+            isEnabled = (deviceType == TYPE_PHONE
+                        && ((deviceCategory & DEVICE_CATEGORY_PHONE) == DEVICE_CATEGORY_PHONE))
+                    || (deviceType == TYPE_TABLET
+                        && ((deviceCategory & DEVICE_CATEGORY_TABLET) == DEVICE_CATEGORY_TABLET))
+                    || (deviceType == TYPE_MULTI_DISPLAY
+                        && ((deviceCategory & DEVICE_CATEGORY_MULTI_DISPLAY)
+                            == DEVICE_CATEGORY_MULTI_DISPLAY));
+
             a.recycle();
             extraAttrs = Themes.createValueMap(context, attrs,
                     IntArray.wrap(R.styleable.GridDisplayOption));
@@ -661,44 +751,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 +876,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..81a0d5b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -23,10 +23,13 @@
 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.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
@@ -37,6 +40,7 @@
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions;
+import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
@@ -48,12 +52,12 @@
 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;
 import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
+import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -80,6 +84,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 +92,8 @@
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.Log;
@@ -106,6 +113,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 +125,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;
@@ -149,6 +158,7 @@
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.PinRequestHelper;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.popup.SystemShortcut;
@@ -166,6 +176,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 +184,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;
@@ -207,6 +219,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -248,8 +261,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 +270,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 +289,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 +340,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 +380,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 +484,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 +577,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 +596,8 @@
     }
 
     @Override
-    public void onIdpChanged(InvariantDeviceProfile idp) {
-        initDeviceProfile(idp);
+    public void onIdpChanged(boolean modelPropertiesChanged) {
+        initDeviceProfile(mDeviceProfile.inv);
         dispatchDeviceProfileChanged();
         reapplyUi();
         mDragLayer.recreateControllers();
@@ -596,7 +629,7 @@
         }
 
         onDeviceProfileInitiated();
-        mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true);
+        mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true, this);
     }
 
     public RotationHelper getRotationHelper() {
@@ -868,11 +901,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 +1225,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 +1272,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 +1327,7 @@
             }
 
             if (!foundCellSpan) {
-                mWorkspace.onNoCellFound(layout);
+                mWorkspace.onNoCellFound(layout, info, /* logInstanceId= */ null);
                 return;
             }
 
@@ -1312,7 +1345,8 @@
         }
     }
 
-    public FolderIcon findFolderIcon(final int folderIconId) {
+    @Override
+    public @Nullable FolderIcon findFolderIcon(final int folderIconId) {
         return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId);
     }
 
@@ -1363,22 +1397,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 +1606,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) {
@@ -1614,6 +1637,8 @@
         AbstractFloatingView.closeOpenViews(this, false, TYPE_ALL & ~TYPE_REBIND_SAFE);
         finishAutoCancelActionMode();
 
+        DragView.removeAllViews(this);
+
         if (mPendingRequestArgs != null) {
             outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
         }
@@ -1921,7 +1946,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 +2102,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 +2163,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 +2201,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 +2219,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 +2254,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 +2298,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 +2359,7 @@
                 }
             }
             workspace.addInScreenFromBind(view, item);
-            if (animateIcons) {
+            if (forceAnimateIcons) {
                 // Animate all the applications up now
                 view.setAlpha(0f);
                 view.setScaleX(0f);
@@ -2305,7 +2375,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 +2391,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 +2602,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 +2609,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 +2643,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 +2655,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 +2672,7 @@
         TraceHelper.INSTANCE.endSection(traceToken);
     }
 
-    private boolean canRunNewAppsAnimation() {
+    private boolean canAnimatePageChange() {
         if (mDragController.isDragging()) {
             return false;
         } else {
@@ -2619,6 +2681,79 @@
         }
     }
 
+    /**
+     * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
+     * animation.
+     *
+     * @param preferredItemId The id of the preferred item to match to if it exists.
+     * @param packageName The package name of the app to match.
+     * @param user The user of the app to match.
+     */
+    public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) {
+        final ItemInfoMatcher preferredItem = (info, cn) ->
+                info != null && info.id == preferredItemId;
+        final ItemInfoMatcher packageAndUserAndApp = (info, cn) ->
+                info != null
+                        && info.itemType == ITEM_TYPE_APPLICATION
+                        && info.user.equals(user)
+                        && info.getTargetComponent() != null
+                        && TextUtils.equals(info.getTargetComponent().getPackageName(),
+                        packageName);
+
+        if (isInState(LauncherState.ALL_APPS)) {
+            return getFirstMatch(Collections.singletonList(mAppsView.getActiveRecyclerView()),
+                    preferredItem, packageAndUserAndApp);
+        } else {
+            List<ViewGroup> containers = new ArrayList<>(mWorkspace.getPanelCount() + 1);
+            containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets());
+            mWorkspace.forEachVisiblePage(page
+                    -> containers.add(((CellLayout) page).getShortcutsAndWidgets()));
+
+            // Order: Preferred item by itself or in folder, then by matching package/user
+            if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
+                return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem),
+                        packageAndUserAndApp, forFolderMatch(packageAndUserAndApp));
+            } else {
+                // Do not use Folder as a criteria, since it'll cause a crash when trying to draw
+                // FolderAdaptiveIcon as the background.
+                return getFirstMatch(containers, preferredItem, packageAndUserAndApp);
+            }
+        }
+    }
+
+    /**
+     * Finds the first view matching the ordered operators across the given viewgroups in order.
+     * @param containers List of ViewGroups to scan, in order of preference.
+     * @param operators List of operators, in order starting from best matching operator.
+     */
+    private static View getFirstMatch(Iterable<ViewGroup> containers,
+            final ItemInfoMatcher... operators) {
+        for (ItemInfoMatcher operator : operators) {
+            for (ViewGroup container : containers) {
+                View match = mapOverViewGroup(container, operator);
+                if (match != null) {
+                    return match;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the first view matching the operator in the given ViewGroups, or null if none.
+     * Forward iteration matters.
+     */
+    private static View mapOverViewGroup(ViewGroup container, ItemInfoMatcher op) {
+        final int itemCount = container.getChildCount();
+        for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+            View item = container.getChildAt(itemIdx);
+            if (op.matchesInfo((ItemInfo) item.getTag())) {
+                return item;
+            }
+        }
+        return null;
+    }
+
     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
         ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
                 .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION);
@@ -2637,9 +2772,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 +2810,7 @@
     @Override
     public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
         if (!updated.isEmpty()) {
-            mWorkspace.updateShortcuts(updated);
+            mWorkspace.updateWorkspaceItems(updated, this);
             PopupContainerWithArrow.dismissInvalidPopup(this);
         }
     }
@@ -2682,7 +2822,7 @@
      */
     @Override
     public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
-        mWorkspace.updateRestoreItems(updates);
+        mWorkspace.updateRestoreItems(updates, this);
     }
 
     /**
@@ -2834,13 +2974,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 +3050,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);
     }
@@ -2954,4 +3113,13 @@
     public StatsLogManager getStatsLogManager() {
         return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId);
     }
+
+    /**
+     * Returns the current popup for testing, if any.
+     */
+    @VisibleForTesting
+    @Nullable
+    public ArrowPopup<?> getOptionsPopup() {
+        return findViewById(R.id.popup_container);
+    }
 }
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..e59eac8 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;
@@ -15,6 +16,7 @@
     private static final String XML = ".xml";
 
     public static final String LAUNCHER_DB = "launcher.db";
+    public static final String LAUNCHER_6_BY_5_DB = "launcher_6_by_5.db";
     public static final String LAUNCHER_4_BY_5_DB = "launcher_4_by_5.db";
     public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
     public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
@@ -29,16 +31,25 @@
     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_6_BY_5_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..68e19cb 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);
         }
     }
 
@@ -393,7 +383,7 @@
             case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
                 Bundle result = new Bundle();
                 result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
-                        mOpenHelper.generateNewScreenId());
+                        mOpenHelper.getNewScreenId());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
@@ -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;
@@ -499,7 +483,7 @@
                             LauncherSettings.Favorites.CONTAINER + " FROM "
                                 + Favorites.TABLE_NAME + ")";
 
-            IntArray folderIds = LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME,
+            IntArray folderIds = LauncherDbUtils.queryIntArray(false, db, Favorites.TABLE_NAME,
                     Favorites._ID, selection, null, null);
             if (!folderIds.isEmpty()) {
                 db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
@@ -644,7 +628,6 @@
         private final Context mContext;
         private final boolean mForMigration;
         private int mMaxItemId = -1;
-        private int mMaxScreenId = -1;
         private boolean mBackupTableExists;
         private boolean mHotseatRestoreTableExists;
 
@@ -655,8 +638,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 +649,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);
 
@@ -693,9 +671,6 @@
             if (mMaxItemId == -1) {
                 mMaxItemId = initializeMaxItemId(getWritableDatabase());
             }
-            if (mMaxScreenId == -1) {
-                mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
-            }
         }
 
         @Override
@@ -703,7 +678,6 @@
             if (LOGD) Log.d(TAG, "creating new launcher database");
 
             mMaxItemId = 1;
-            mMaxScreenId = 0;
 
             addFavoritesTable(db, false);
 
@@ -852,16 +826,12 @@
                 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.
-                    IntArray finalScreens = LauncherDbUtils.queryIntArray(db, "workspaceScreens",
-                            BaseColumns._ID, null, null, "screenRank");
+                    IntArray finalScreens = LauncherDbUtils.queryIntArray(false, db,
+                            "workspaceScreens", BaseColumns._ID, null, null, "screenRank");
                     int[] original = finalScreens.toArray();
                     Arrays.sort(original);
                     String updatemap = "";
@@ -889,6 +859,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;
                 }
@@ -939,7 +914,7 @@
                 Log.e(TAG, "getAppWidgetIds not supported", e);
                 return;
             }
-            final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(db,
+            final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(false, db,
                     Favorites.TABLE_NAME, Favorites.APPWIDGET_ID,
                     "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null));
             for (int widgetId : allWidgets) {
@@ -1063,36 +1038,19 @@
         public void checkId(ContentValues values) {
             int id = values.getAsInteger(Favorites._ID);
             mMaxItemId = Math.max(id, mMaxItemId);
-
-            Integer screen = values.getAsInteger(Favorites.SCREEN);
-            Integer container = values.getAsInteger(Favorites.CONTAINER);
-            if (screen != null && container != null
-                    && container.intValue() == Favorites.CONTAINER_DESKTOP) {
-                mMaxScreenId = Math.max(screen, mMaxScreenId);
-            }
         }
 
         private int initializeMaxItemId(SQLiteDatabase db) {
             return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s", Favorites._ID, Favorites.TABLE_NAME);
         }
 
-        // Generates a new ID to use for an workspace screen in your database. This method
-        // should be only called from the main UI thread. As an exception, we do call it when we
-        // call the constructor from the worker thread; however, this doesn't extend until after the
-        // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
-        // after that point
-        public int generateNewScreenId() {
-            if (mMaxScreenId < 0) {
-                throw new RuntimeException("Error: max screen id was not initialized");
-            }
-            mMaxScreenId += 1;
-            return mMaxScreenId;
-        }
-
-        private int initializeMaxScreenId(SQLiteDatabase db) {
-            return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d",
+        // Returns a new ID to use for an workspace screen in your database that is greater than all
+        // existing screen IDs.
+        private int getNewScreenId() {
+            return getMaxId(getWritableDatabase(),
+                    "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);
+                    Favorites.CONTAINER_DESKTOP) + 1;
         }
 
         @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
@@ -1101,7 +1059,6 @@
 
             // Ensure that the max ids are initialized
             mMaxItemId = initializeMaxItemId(db);
-            mMaxScreenId = initializeMaxScreenId(db);
             return count;
         }
     }
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..be2cd88 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -57,10 +57,9 @@
     public static final int ALL_APPS_CONTENT = 1 << 1;
     public static final int VERTICAL_SWIPE_INDICATOR = 1 << 2;
     public static final int OVERVIEW_ACTIONS = 1 << 3;
-    public static final int TASKBAR = 1 << 4;
-    public static final int CLEAR_ALL_BUTTON = 1 << 5;
-    public static final int WORKSPACE_PAGE_INDICATOR = 1 << 6;
-    public static final int SPLIT_PLACHOLDER_VIEW = 1 << 7;
+    public static final int CLEAR_ALL_BUTTON = 1 << 4;
+    public static final int WORKSPACE_PAGE_INDICATOR = 1 << 5;
+    public static final int SPLIT_PLACHOLDER_VIEW = 1 << 6;
 
     // Flag indicating workspace has multiple pages visible.
     public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
@@ -181,20 +180,12 @@
         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;
     }
 
     public int getVisibleElements(Launcher launcher) {
-        return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR;
+        return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR;
     }
 
     /**
@@ -205,6 +196,16 @@
         return (getVisibleElements(launcher) & elements) == elements;
     }
 
+    /** Returns whether taskbar is stashed and thus should replace hotseat with a handle */
+    public boolean isTaskbarStashed(Launcher launcher) {
+        return false;
+    }
+
+    /** Returns whether taskbar is aligned with the hotseat vs position inside apps */
+    public boolean isTaskbarAlignedWithHotseat(Launcher launcher) {
+        return !isTaskbarStashed(launcher);
+    }
+
     /**
      * 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..2c14f07 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);
+        mOrientationHandler.setPrimary(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);
@@ -481,16 +556,18 @@
             int oldPos = mOrientationHandler.getPrimaryScroll(this);
             int newPos = mScroller.getCurrX();
             if (oldPos != newPos) {
-                mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrX());
+                mOrientationHandler.setPrimary(this, VIEW_SCROLL_TO, mScroller.getCurrX());
             }
 
             if (mAllowOverScroll) {
                 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
@@ -1200,7 +1280,7 @@
                 mLastMotionRemainder = delta - movedDelta;
 
                 if (delta != 0) {
-                    mOrientationHandler.set(this, VIEW_SCROLL_BY, movedDelta);
+                    mOrientationHandler.setPrimary(this, VIEW_SCROLL_BY, movedDelta);
 
                     if (mAllowOverScroll) {
                         final float pulledToX = oldScroll + delta;
@@ -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,26 @@
 
     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) {
+        if (mScroller.isFinished()) {
+            // This was not caused by the scroller, skip it.
+            return;
+        }
+        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.
@@ -1641,20 +1749,23 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         final boolean pagesFlipped = isPageOrderFlipped();
-        int offset = (mAllowOverScroll ? 0 : 1);
-        info.setScrollable(getPageCount() > offset);
-        if (getCurrentPage() < getPageCount() - offset) {
+        info.setScrollable(getPageCount() > 0);
+        int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+        if (getCurrentPage() < getPageCount() - getPanelCount()
+                || (getCurrentPage() == getPageCount() - getPanelCount()
+                && primaryScroll != getScrollForPage(getPageCount() - getPanelCount()))) {
             info.addAction(pagesFlipped ?
-                AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
-                : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+                    AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD
+                    : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
             info.addAction(mIsRtl ?
                 AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT
                 : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);
         }
-        if (getCurrentPage() >= offset) {
+        if (getCurrentPage() > 0
+                || (getCurrentPage() == 0 && primaryScroll != getScrollForPage(0))) {
             info.addAction(pagesFlipped ?
-                AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
-                : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+                    AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD
+                    : AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
             info.addAction(mIsRtl ?
                 AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT
                 : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);
@@ -1699,16 +1810,16 @@
             } break;
             case android.R.id.accessibilityActionPageRight: {
                 if (!mIsRtl) {
-                  return scrollRight();
+                    return scrollRight();
                 } else {
-                  return scrollLeft();
+                    return scrollLeft();
                 }
             }
             case android.R.id.accessibilityActionPageLeft: {
                 if (!mIsRtl) {
-                  return scrollLeft();
+                    return scrollLeft();
                 } else {
-                  return scrollRight();
+                    return scrollRight();
                 }
             }
         }
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..63313f7 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.
@@ -441,9 +460,10 @@
      * Trims the string, removing all whitespace at the beginning and end of the string.
      * Non-breaking whitespaces are also removed.
      */
+    @NonNull
     public static String trim(CharSequence s) {
         if (s == null) {
-            return null;
+            return "";
         }
 
         // Just strip any sequence of whitespace or java space characters from the beginning and end
@@ -508,6 +528,18 @@
     }
 
     /**
+     * Using the view's bounds and icon size, calculate where the icon bounds will
+     * be if it was positioned at the center of the view.
+     */
+    public static void setRectToViewCenter(View iconView, int iconSize, Rect outBounds) {
+        int top = (iconView.getHeight() - iconSize) / 2;
+        int left = (iconView.getWidth() - iconSize) / 2;
+        int right = left + iconSize;
+        int bottom = top + iconSize;
+        outBounds.set(left, top, right, bottom);
+    }
+
+    /**
      * Ensures that a value is within given bounds. Specifically:
      * If value is less than lowerBound, return lowerBound; else if value is greater than upperBound,
      * return upperBound; else return value unchanged.
@@ -632,21 +664,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 +676,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 +704,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 +737,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 +758,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 +860,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..c8dd003 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -26,7 +26,6 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
-import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
@@ -51,7 +50,6 @@
 import android.os.Message;
 import android.os.Parcelable;
 import android.os.UserHandle;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -63,6 +61,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 +79,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 +100,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 +122,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 +136,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 +198,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 +207,6 @@
 
     private boolean mStripScreensOnPageStopMoving = false;
 
-    private DragPreviewProvider mOutlineProvider = null;
-
     private boolean mWorkspaceFadeInAdjacentScreens;
 
     final WallpaperOffsetInterpolator mWallpaperOffset;
@@ -217,11 +218,13 @@
     private FolderIcon mDragOverFolderIcon = null;
     private boolean mCreateUserFolderOnDrop = false;
     private boolean mAddToExistingFolderOnDrop = false;
-    private float mMaxDistanceForFolderCreation;
 
     // 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;
@@ -302,19 +305,13 @@
     public void setInsets(Rect insets) {
         DeviceProfile grid = mLauncher.getDeviceProfile();
 
-        mMaxDistanceForFolderCreation = grid.isTablet
-                ? 0.75f * grid.iconSizePx : 0.55f * grid.iconSizePx;
         mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
 
         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 +323,55 @@
             setPageSpacing(Math.max(maxInsets, maxPadding));
         }
 
+        updateWorkspaceScreensPadding();
+        updateWorkspaceWidgetsSizes();
+    }
+
+    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);
+        }
+    }
+
+    private void updateWorkspaceWidgetsSizes() {
+        int numberOfScreens = mScreenOrder.size();
+        for (int i = 0; i < numberOfScreens; i++) {
+            ShortcutAndWidgetContainer shortcutAndWidgetContainer =
+                    mWorkspaceScreens.get(mScreenOrder.get(i)).getShortcutsAndWidgets();
+            int shortcutsAndWidgetCount = shortcutAndWidgetContainer.getChildCount();
+            for (int j = 0; j < shortcutsAndWidgetCount; j++) {
+                View view = shortcutAndWidgetContainer.getChildAt(j);
+                if (view instanceof LauncherAppWidgetHostView
+                        && view.getTag() instanceof LauncherAppWidgetInfo) {
+                    LauncherAppWidgetInfo launcherAppWidgetInfo =
+                            (LauncherAppWidgetInfo) view.getTag();
+                    WidgetSizes.updateWidgetSizeRanges((LauncherAppWidgetHostView) view,
+                            mLauncher, launcherAppWidgetInfo.spanX, launcherAppWidgetInfo.spanY);
+                }
+            }
         }
     }
 
@@ -462,7 +484,7 @@
     }
 
     @Override
-    protected int getPanelCount() {
+    public int getPanelCount() {
         return isTwoPanelEnabled() ? 2 : super.getPanelCount();
     }
 
@@ -491,7 +513,6 @@
         });
 
         mDragInfo = null;
-        mOutlineProvider = null;
         mDragSourceInternal = null;
     }
 
@@ -546,19 +567,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 +588,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 +600,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 +614,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 +643,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 +651,7 @@
                 mLauncher.getStateManager().getState(), newScreen, insertIndex);
 
         updatePageScrollValues();
+        updateWorkspaceScreensPadding();
         return newScreen;
     }
 
@@ -642,18 +660,28 @@
         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);
+                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 +690,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 +774,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 +804,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 +832,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(),
+        return extraEmptyPageIds;
+    }
+
+    private int commitExtraEmptyScreen(int emptyScreenId) {
+        CellLayout cl = mWorkspaceScreens.get(emptyScreenId);
+        mWorkspaceScreens.remove(emptyScreenId);
+        mScreenOrder.removeValue(emptyScreenId);
+
+        int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(),
                 LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
                 .getInt(LauncherSettings.Settings.EXTRA_VALUE);
-        mWorkspaceScreens.put(newId, cl);
-        mScreenOrder.add(newId);
+        // Launcher database isn't aware of empty pages that are already bound, so we need to
+        // skip those IDs manually.
+        while (mWorkspaceScreens.containsKey(newScreenId)) {
+            newScreenId++;
+        }
 
-        return newId;
+        mWorkspaceScreens.put(newScreenId, cl);
+        mScreenOrder.add(newScreenId);
+
+        return newScreenId;
     }
 
     @Override
@@ -786,6 +906,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 +921,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 +974,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 +1003,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 +1063,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 +1507,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 +1528,7 @@
      */
     @Override
     public void setState(LauncherState toState) {
-        onStartStateTransition(toState);
+        onStartStateTransition();
         mStateTransitionAnimation.setState(toState);
         onEndStateTransition();
     }
@@ -1359,7 +1539,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 +1648,6 @@
             icon.clearPressedBackground();
         }
 
-        mOutlineProvider = previewProvider;
-
         if (draggableView == null && child instanceof DraggableView) {
             draggableView = (DraggableView) child;
         }
@@ -1505,10 +1683,10 @@
         }
 
         if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) {
-            PopupContainerWithArrow popupContainer = PopupContainerWithArrow
+            PopupContainerWithArrow<Launcher> popupContainer = PopupContainerWithArrow
                     .showForIcon((BubbleTextView) child);
             if (popupContainer != null) {
-                dragOptions.preDragCondition = popupContainer.createPreDragCondition();
+                dragOptions.preDragCondition = popupContainer.createPreDragCondition(true);
             }
         }
 
@@ -1591,8 +1769,8 @@
             mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
                     (int) mDragViewVisualCenter[1], minSpanX, minSpanY, dropTargetLayout,
                     mTargetCell);
-            float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
-                    mDragViewVisualCenter[1], mTargetCell);
+            float distance = dropTargetLayout.getDistanceFromWorkspaceCellVisualCenter(
+                    mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
             if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo,
                     dropTargetLayout, mTargetCell, distance, true)) {
                 return true;
@@ -1611,14 +1789,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;
@@ -1626,7 +1804,7 @@
 
     boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell,
             float distance, boolean considerTimeout) {
-        if (distance > mMaxDistanceForFolderCreation) return false;
+        if (distance > target.getFolderCreationRadius(targetCell)) return false;
         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
         return willCreateUserFolder(info, dropOverView, considerTimeout);
     }
@@ -1661,7 +1839,7 @@
 
     boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell,
             float distance) {
-        if (distance > mMaxDistanceForFolderCreation) return false;
+        if (distance > target.getFolderCreationRadius(targetCell)) return false;
         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
         return willAddToExistingUserFolder(dragInfo, dropOverView);
 
@@ -1685,7 +1863,7 @@
 
     boolean createUserFolderIfNecessary(View newView, int container, CellLayout target,
             int[] targetCell, float distance, boolean external, DragObject d) {
-        if (distance > mMaxDistanceForFolderCreation) return false;
+        if (distance > target.getFolderCreationRadius(targetCell)) return false;
         View v = target.getChildAt(targetCell[0], targetCell[1]);
 
         boolean hasntMoved = false;
@@ -1742,7 +1920,7 @@
 
     boolean addToExistingFolderIfNecessary(View newView, CellLayout target, int[] targetCell,
             float distance, DragObject d, boolean external) {
-        if (distance > mMaxDistanceForFolderCreation) return false;
+        if (distance > target.getFolderCreationRadius(targetCell)) return false;
 
         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
         if (!mAddToExistingFolderOnDrop) return false;
@@ -1779,19 +1957,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
@@ -1809,8 +1984,8 @@
 
                 mTargetCell = findNearestArea((int) mDragViewVisualCenter[0], (int)
                         mDragViewVisualCenter[1], spanX, spanY, dropTargetLayout, mTargetCell);
-                float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
-                        mDragViewVisualCenter[1], mTargetCell);
+                float distance = dropTargetLayout.getDistanceFromWorkspaceCellVisualCenter(
+                        mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
 
                 // If the item being dropped is a shortcut and the nearest drop
                 // cell also contains a shortcut, then create a folder with the two shortcuts.
@@ -1864,11 +2039,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 +2080,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 +2091,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 +2138,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 +2149,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 +2159,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);
     }
 
     /**
@@ -2231,7 +2413,7 @@
 
             setCurrentDropOverCell(mTargetCell[0], mTargetCell[1]);
 
-            float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
+            float targetCellDistance = mDragTargetLayout.getDistanceFromWorkspaceCellVisualCenter(
                     mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
 
             manageFolderFeedback(targetCellDistance, d);
@@ -2244,8 +2426,9 @@
                 mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
                         item.spanX, item.spanY, d);
             } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
-                    && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
-                    mLastReorderY != reorderY)) {
+                    && !mReorderAlarm.alarmPending()
+                    && (mLastReorderX != reorderX || mLastReorderY != reorderY)
+                    && targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell)) {
 
                 int[] resultSpan = new int[2];
                 mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0],
@@ -2290,30 +2473,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 +2512,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;
             }
@@ -2341,7 +2525,7 @@
     }
 
     private void manageFolderFeedback(float distance, DragObject dragObject) {
-        if (distance > mMaxDistanceForFolderCreation) {
+        if (distance > mDragTargetLayout.getFolderCreationRadius(mTargetCell)) {
             if ((mDragMode == DRAG_MODE_ADD_TO_FOLDER
                     || mDragMode == DRAG_MODE_CREATE_FOLDER)) {
                 setDragMode(DRAG_MODE_NONE);
@@ -2486,8 +2670,8 @@
             if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
                 mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
                         cellLayout, mTargetCell);
-                float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
-                        mDragViewVisualCenter[1], mTargetCell);
+                float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter(
+                        mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
                 if (willCreateUserFolder(d.dragInfo, cellLayout, mTargetCell, distance, true)
                         || willAddToExistingUserFolder(
                                 d.dragInfo, cellLayout, mTargetCell, distance)) {
@@ -2560,11 +2744,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:
@@ -2580,8 +2770,8 @@
             if (touchXY != null) {
                 mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
                         cellLayout, mTargetCell);
-                float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
-                        mDragViewVisualCenter[1], mTargetCell);
+                float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter(
+                        mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
                 if (createUserFolderIfNecessary(view, container, cellLayout, mTargetCell, distance,
                         true, d)) {
                     return;
@@ -2691,9 +2881,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 +2924,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);
         }
     }
@@ -2957,62 +3144,6 @@
         return layouts;
     }
 
-    /**
-     * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
-     * animation.
-     *
-     * @param preferredItemId The id of the preferred item to match to if it exists.
-     * @param packageName The package name of the app to match.
-     * @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) ->
-                info != null && info.id == preferredItemId;
-        final Workspace.ItemOperator preferredItemInFolder = (info, view) -> {
-            if (info instanceof FolderInfo) {
-                FolderInfo folderInfo = (FolderInfo) info;
-                for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
-                    if (preferredItem.evaluate(shortcutInfo, view)) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        };
-        final Workspace.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) -> {
-            if (info instanceof FolderInfo) {
-                FolderInfo folderInfo = (FolderInfo) info;
-                for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
-                    if (packageAndUserAndApp.evaluate(shortcutInfo, view)) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        };
-
-        List<CellLayout> cellLayouts = new ArrayList<>(getPanelCount() + 1);
-        cellLayouts.add(getHotseat());
-        forEachVisiblePage(page -> cellLayouts.add((CellLayout) page));
-
-        // Order: Preferred item, App icons in hotseat/workspace, app in folder in hotseat/workspace
-        if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
-            return getFirstMatch(cellLayouts, preferredItem, preferredItemInFolder,
-                    packageAndUserAndApp, packageAndUserAndAppInFolder);
-        } else {
-            // Do not use Folder as a criteria, since it'll cause a crash when trying to draw
-            // FolderAdaptiveIcon as the background.
-            return getFirstMatch(cellLayouts, preferredItem, packageAndUserAndApp);
-        }
-    }
-
     public View getHomescreenIconByItemId(final int id) {
         return getFirstMatch((info, v) -> info != null && info.id == id);
     }
@@ -3038,23 +3169,6 @@
         return value[0];
     }
 
-    /**
-     * Finds the first view matching the ordered operators across the given cell layouts by order.
-     * @param cellLayouts List of CellLayouts to scan, in order of preference.
-     * @param operators List of operators, in order starting from best matching operator.
-     */
-    View getFirstMatch(Iterable<CellLayout> cellLayouts, final ItemOperator... operators) {
-        for (ItemOperator operator : operators) {
-            for (CellLayout cellLayout : cellLayouts) {
-                View match = mapOverCellLayout(cellLayout, operator);
-                if (match != null) {
-                    return match;
-                }
-            }
-        }
-        return null;
-    }
-
     void clearDropTargets() {
         mapOverItems(new ItemOperator() {
             @Override
@@ -3105,22 +3219,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 +3245,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 +3284,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 +3448,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 +3455,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/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 874fe80..d5c9a53 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -41,7 +41,9 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.Arrays;
@@ -95,16 +97,15 @@
         // The type of this item
         public int viewType;
 
-        /** App-only properties */
-        // The section name of this app.  Note that there can be multiple items with different
+        // The section name of this item.  Note that there can be multiple items with different
         // sectionNames in the same section
         public String sectionName = null;
         // The row that this item shows up on
         public int rowIndex;
         // The index of this app in the row
         public int rowAppIndex;
-        // The associated AppInfo for the app
-        public AppInfo appInfo = null;
+        // The associated ItemInfoWithIcon for the item
+        public ItemInfoWithIcon itemInfo = null;
         // The index of this app not including sections
         public int appIndex = -1;
         // Search section associated to result
@@ -119,7 +120,7 @@
             item.viewType = VIEW_TYPE_ICON;
             item.position = pos;
             item.sectionName = sectionName;
-            item.appInfo = appInfo;
+            item.itemInfo = appInfo;
             item.appIndex = appIndex;
             return item;
         }
@@ -273,6 +274,8 @@
     // The intent to send off to the market app, updated each time the search query changes.
     private Intent mMarketSearchIntent;
 
+    private final int mExtraHeight;
+
     public AllAppsGridAdapter(BaseDraggingActivity launcher, LayoutInflater inflater,
             AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) {
         Resources res = launcher.getResources();
@@ -288,6 +291,7 @@
 
         mAdapterProviders = adapterProviders;
         setAppsPerRow(mLauncher.getDeviceProfile().numShownAllAppsColumns);
+        mExtraHeight = launcher.getResources().getDimensionPixelSize(R.dimen.all_apps_height_extra);
     }
 
     public void setAppsPerRow(int appsPerRow) {
@@ -347,14 +351,19 @@
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case VIEW_TYPE_ICON:
+                int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon
+                        : R.layout.all_apps_icon_twoline;
                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
-                        R.layout.all_apps_icon, parent, false);
+                        layout, parent, false);
                 icon.setLongPressTimeoutFactor(1f);
                 icon.setOnFocusChangeListener(mIconFocusListener);
                 icon.setOnClickListener(mOnIconClickListener);
                 icon.setOnLongClickListener(mOnIconLongClickListener);
                 // Ensure the all apps icon height matches the workspace icons in portrait mode.
                 icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
+                if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
+                    icon.getLayoutParams().height += mExtraHeight;
+                }
                 return new ViewHolder(icon);
             case VIEW_TYPE_EMPTY_SEARCH:
                 return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
@@ -373,7 +382,7 @@
                 if (adapterProvider != null) {
                     return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
                 }
-                throw new RuntimeException("Unexpected view type");
+                throw new RuntimeException("Unexpected view type" + viewType);
         }
     }
 
@@ -382,10 +391,13 @@
         switch (holder.getItemViewType()) {
             case VIEW_TYPE_ICON:
                 AdapterItem adapterItem = mApps.getAdapterItems().get(position);
-                AppInfo info = adapterItem.appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.itemView;
                 icon.reset();
-                icon.applyFromApplicationInfo(info);
+                if (adapterItem.itemInfo instanceof AppInfo) {
+                    icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo);
+                } else {
+                    icon.applyFromItemInfoWithIcon(adapterItem.itemInfo);
+                }
                 break;
             case VIEW_TYPE_EMPTY_SEARCH:
                 TextView emptyViewText = (TextView) holder.itemView;
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..85ee636 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -108,6 +108,7 @@
     // enabled or disabled, and represent the current set of all rows.
     private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
 
+
     public FloatingHeaderView(@NonNull Context context) {
         this(context, null);
     }
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/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index 729523f..8d77b4b 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.anim;
 
+import android.animation.Animator;
 import android.animation.TimeInterpolator;
 import android.util.FloatProperty;
 import android.util.IntProperty;
@@ -64,4 +65,9 @@
             TimeInterpolator interpolator) {
         property.setValue(target, value);
     }
+
+    default void add(Animator animatorSet) {
+        animatorSet.setDuration(0);
+        animatorSet.start();
+    }
 }
diff --git a/src/com/android/launcher3/anim/RevealOutlineAnimation.java b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
index f99dabc..f5a746f 100644
--- a/src/com/android/launcher3/anim/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
@@ -26,9 +26,27 @@
     /** Sets the progress, from 0 to 1, of the reveal animation. */
     abstract void setProgress(float progress);
 
+    /**
+     * @see #createRevealAnimator(View, boolean, float) where startProgress is set to 0.
+     */
     public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
-        ValueAnimator va =
-                isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
+        return createRevealAnimator(revealView, isReversed, 0f /* startProgress */);
+    }
+
+    /**
+     * Animates the given View's ViewOutline according to {@link #setProgress(float)}.
+     * @param revealView The View whose outline we are animating.
+     * @param isReversed Whether we are hiding rather than revealing the View.
+     * @param startProgress The progress at which to start the newly created animation. Useful if
+     * the previous reveal animation was cancelled and we want to create a new animation where it
+     * left off. Note that if isReversed=true, we start at 1 - startProgress (and go to 0).
+     * @return The Animator, which the caller must start.
+     */
+    public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed,
+            float startProgress) {
+        ValueAnimator va = isReversed
+                ? ValueAnimator.ofFloat(1f - startProgress, 0f)
+                : ValueAnimator.ofFloat(startProgress, 1f);
         final float elevation = revealView.getElevation();
 
         va.addListener(new AnimatorListenerAdapter() {
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 30c3417..dd58123 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -41,11 +41,10 @@
     }
 
     /**
-     *
      * @param target The view the accessibility event is initialized on.
      *               If null, this method has no effect.
-     * @param type See TYPE_ constants defined in {@link AccessibilityEvent}.
-     * @param text Optional text to add to the event, which will be announced to the user.
+     * @param type   See TYPE_ constants defined in {@link AccessibilityEvent}.
+     * @param text   Optional text to add to the event, which will be announced to the user.
      */
     public static void sendCustomAccessibilityEvent(@Nullable View target, int type,
             @Nullable String text) {
@@ -89,6 +88,24 @@
         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);
+    }
+
+    /**
+     * Notify running tests of a folder opened.
+     */
+    public static void sendFolderOpenedEventToTest(Context context) {
+        final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
+        if (accessibilityManager == null) return;
+
+        sendEventToTest(accessibilityManager, context, TestProtocol.FOLDER_OPENED_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..2d31aa4 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -72,12 +72,7 @@
             "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");
-
+    // TODO: b/206508141: Long pressing on some icons on home screen cause launcher to crash.
     public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag(
             "ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups.");
 
@@ -94,14 +89,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_TWOLINE_ALLAPPS = getDebugFlag(
+            "ENABLE_TWOLINE_ALLAPPS", false, "Enables two line label inside all apps.");
+
     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 +117,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,46 +127,34 @@
     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");
 
-    public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag(
-            "ENABLE_SMARTSPACE_UNIVERSAL", false,
-            "Replace Smartspace with a version rendered by System UI.");
-
-    public static final BooleanFlag ENABLE_SMARTSPACE_ENHANCED = getDebugFlag(
-            "ENABLE_SMARTSPACE_ENHANCED", true,
-            "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,14 +191,21 @@
             + "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_TASKBAR_POPUP_MENU = getDebugFlag(
+            "ENABLE_TASKBAR_POPUP_MENU", false, "Enables long pressing taskbar icons to show the"
+                    + " popup menu.");
 
     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_SCRIM_FOR_APP_LAUNCH = getDebugFlag(
@@ -235,7 +213,7 @@
             "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");
@@ -247,9 +225,6 @@
     public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false,
             "Sends a notification whenever launcher encounters an uncaught exception.");
 
-    public static final BooleanFlag PROTOTYPE_APP_CLOSE = getDebugFlag(
-            "PROTOTYPE_APP_CLOSE", false, "Enables new app close");
-
     public static final BooleanFlag ENABLE_WALLPAPER_SCRIM = getDebugFlag(
             "ENABLE_WALLPAPER_SCRIM", false,
             "Enables scrim over wallpaper for text protection.");
@@ -258,6 +233,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 +282,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 +301,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..c37613f 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
@@ -597,4 +565,19 @@
         iv.setImageDrawable(drawable);
         return iv;
     }
+
+    /**
+     * Removes any stray DragView from the DragLayer.
+     */
+    public static void removeAllViews(ActivityContext activity) {
+        BaseDragLayer dragLayer = activity.getDragLayer();
+        // Iterate in reverse order. DragView is added later to the dragLayer,
+        // and will be one of the last views.
+        for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
+            View child = dragLayer.getChildAt(i);
+            if (child instanceof DragView) {
+                dragLayer.removeView(child);
+            }
+        }
+    }
 }
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..daef682 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -75,10 +75,10 @@
 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;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragController.DragListener;
@@ -94,6 +94,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 +277,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);
@@ -683,6 +688,7 @@
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
                 announceAccessibilityChanges();
+                AccessibilityManagerCompat.sendFolderOpenedEventToTest(getContext());
 
                 mContent.setFocusOnFirstChild();
             }
@@ -1196,8 +1202,7 @@
     }
 
     void replaceFolderWithFinalItem() {
-        mLauncherDelegate.replaceFolderWithFinalItem(this);
-        mDestroyed = true;
+        mDestroyed = mLauncherDelegate.replaceFolderWithFinalItem(this);
     }
 
     public boolean isDestroyed() {
@@ -1262,7 +1267,8 @@
 
         PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
                 ? (PendingAddShortcutInfo) d.dragInfo : null;
-        WorkspaceItemInfo pasiSi = pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null;
+        WorkspaceItemInfo pasiSi =
+                pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null;
         if (pasi != null && pasiSi == null) {
             // There is no WorkspaceItemInfo, so we have to go through a configuration activity.
             pasi.container = mInfo.id;
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..98be72a 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);
 
@@ -626,7 +628,10 @@
     public void drawDot(Canvas canvas) {
         if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
             Rect iconBounds = mDotParams.iconBounds;
-            BubbleTextView.getIconBounds(this, iconBounds, mActivity.getDeviceProfile().iconSizePx);
+
+            Utilities.setRectToViewCenter(this, mActivity.getDeviceProfile().iconSizePx,
+                    iconBounds);
+            iconBounds.offsetTo(iconBounds.left, getPaddingTop());
             float iconScale = (float) mBackground.previewSize / iconBounds.width();
             Utilities.scaleRectAboutCenter(iconBounds, iconScale);
 
@@ -683,6 +688,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 +700,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 +771,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 +787,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..8b7bebc 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,19 @@
         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),
+
+        @UiEvent(doc = "User clicks on the search icon on header to launch search in app.")
+        LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH(913);
 
         // ADD MORE
 
@@ -627,7 +661,7 @@
     public StatsLogger logger() {
         StatsLogger logger = createLogger();
         if (mInstanceId != null) {
-            return logger.withInstanceId(mInstanceId);
+            logger.withInstanceId(mInstanceId);
         }
         return logger;
     }
@@ -650,7 +684,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..a13fa55 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.model;
 
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+
 import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
@@ -27,6 +29,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 +41,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 +291,23 @@
 
         // 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);
         }
 
-        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 +323,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..d3351dc 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -31,6 +31,8 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Workspace;
@@ -49,7 +51,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;
@@ -215,6 +217,19 @@
     }
 
     public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
+        addItem(context, item, newItem, null);
+    }
+
+    public synchronized void addItem(
+            Context context, ItemInfo item, boolean newItem, @Nullable LoaderMemoryLogger logger) {
+        if (logger != null) {
+            logger.addLog(
+                    Log.DEBUG,
+                    TAG,
+                    String.format("Adding item to ID map: %s", item.toString()),
+                    /* stackTrace= */ null);
+        }
+
         itemsIdMap.put(item.id, item);
         switch (item.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -446,37 +461,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..fa11d4e
--- /dev/null
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -0,0 +1,119 @@
+/*
+ * 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.InvariantDeviceProfile.DeviceType;
+import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
+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 com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
+
+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";
+
+    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.deviceType;
+    }
+
+    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
+                && 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..178fbdb 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,40 +180,29 @@
      * 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
+                || itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                || restoreFlag != 0
+                ? getBlob(iconIndex) : null;
+
+        return new IconRequestInfo<>(
+                wai, mActivityInfo, packageName, resourceName, iconBlob, useLowResIcon);
     }
 
     /**
      * Returns the title or empty string
      */
     private String getTitle() {
-        String title = getString(titleIndex);
-        return TextUtils.isEmpty(title) ? "" : Utilities.trim(title);
+        return Utilities.trim(getString(titleIndex));
     }
 
     /**
@@ -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) {
@@ -390,18 +382,23 @@
         info.cellY = getInt(cellYIndex);
     }
 
+    public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
+        checkAndAddItem(info, dataModel, null);
+    }
+
     /**
      * Adds the {@param info} to {@param dataModel} if it does not overlap with any other item,
      * otherwise marks it for deletion.
      */
-    public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
+    public void checkAndAddItem(
+            ItemInfo info, BgDataModel dataModel, LoaderMemoryLogger logger) {
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             // Ensure that it is a valid intent. An exception here will
             // cause the item loading to get skipped
             ShortcutKey.fromItemInfo(info);
         }
         if (checkItemPlacement(info)) {
-            dataModel.addItem(mContext, info, false);
+            dataModel.addItem(mContext, info, false, logger);
         } else {
             markDeleted("Item position overlap");
         }
diff --git a/src/com/android/launcher3/model/LoaderMemoryLogger.java b/src/com/android/launcher3/model/LoaderMemoryLogger.java
new file mode 100644
index 0000000..f48efcb
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderMemoryLogger.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Helper logger that collects logs while {@code LoaderTask#run} executes and prints them all iff
+ * an exception is caught in {@code LoaderTask#run}.
+ */
+public class LoaderMemoryLogger {
+
+    private static final String TAG = "LoaderMemoryLogger";
+
+    private final ArrayList<LogEntry> mLogEntries = new ArrayList<>();
+
+    protected LoaderMemoryLogger() {}
+
+    protected void addLog(int logLevel, String tag, String log) {
+        addLog(logLevel, tag, log, null);
+    }
+
+    protected void addLog(
+            int logLevel, String tag, String log, Exception stackTrace) {
+        switch (logLevel) {
+            case Log.ASSERT:
+            case Log.ERROR:
+            case Log.DEBUG:
+            case Log.INFO:
+            case Log.VERBOSE:
+            case Log.WARN:
+                mLogEntries.add(new LogEntry(logLevel, tag, log, stackTrace));
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid log level provided: " + logLevel);
+
+        }
+    }
+
+    protected void clearLogs() {
+        mLogEntries.clear();
+    }
+
+    protected void printLogs() {
+        for (LogEntry logEntry : mLogEntries) {
+            String tag = String.format("%s: %s", TAG, logEntry.mLogTag);
+            String logString = logEntry.mStackStrace == null
+                    ? logEntry.mLogString
+                    : String.format(
+                            "%s\n%s",
+                            logEntry.mLogString,
+                            Log.getStackTraceString(logEntry.mStackStrace));
+
+            Log.println(logEntry.mLogLevel, tag, logString);
+        }
+        clearLogs();
+    }
+
+    private static class LogEntry {
+
+        protected final int mLogLevel;
+        protected final String mLogTag;
+        protected final String mLogString;
+        @Nullable protected final Exception mStackStrace;
+
+        protected LogEntry(
+                int logLevel, String logTag, String logString, @Nullable Exception stackStrace) {
+            mLogLevel = logLevel;
+            mLogTag = logTag;
+            mLogString = logString;
+            mStackStrace = stackStrace;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f6b0b4d..2a0f9a6 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;
@@ -52,6 +52,8 @@
 import android.util.LongSparseArray;
 import android.util.TimingLogger;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
@@ -72,6 +74,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 +82,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 +178,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);
     }
@@ -192,9 +199,15 @@
 
         Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
         TimingLogger logger = new TimingLogger(TAG, "run");
+        LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
             List<ShortcutInfo> allShortcuts = new ArrayList<>();
-            loadWorkspace(allShortcuts);
+            Trace.beginSection("LoadWorkspace");
+            try {
+                loadWorkspace(allShortcuts, memoryLogger);
+            } finally {
+                Trace.endSection();
+            }
             logASplit(logger, "loadWorkspace");
 
             // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
@@ -208,7 +221,7 @@
             }
 
             verifyNotStopped();
-            mResults.bindWorkspace();
+            mResults.bindWorkspace(true /* incrementBindId */);
             logASplit(logger, "bindWorkspace");
 
             mModelDelegate.workspaceLoadComplete();
@@ -222,7 +235,13 @@
             verifyNotStopped();
 
             // second step
-            List<LauncherActivityInfo> allActivityList = loadAllApps();
+            Trace.beginSection("LoadAllApps");
+            List<LauncherActivityInfo> allActivityList;
+            try {
+               allActivityList = loadAllApps();
+            } finally {
+                Trace.endSection();
+            }
             logASplit(logger, "loadAllApps");
 
             verifyNotStopped();
@@ -295,9 +314,13 @@
 
             mModelDelegate.modelLoadComplete();
             transaction.commit();
+            memoryLogger.clearLogs();
         } catch (CancellationException e) {
             // Loader stopped, ignore
             logASplit(logger, "Cancelled");
+        } catch (Exception e) {
+            memoryLogger.printLogs();
+            throw e;
         } finally {
             logger.dumpToLog();
         }
@@ -309,13 +332,21 @@
         this.notify();
     }
 
-    private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
+    private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger logger) {
         loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
-                null /* selection */);
+                null /* selection */, logger);
     }
 
-    protected void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, Uri contentUri,
-            String selection) {
+    protected void loadWorkspace(
+            List<ShortcutInfo> allDeepShortcuts, Uri contentUri, String selection) {
+        loadWorkspace(allDeepShortcuts, contentUri, selection, null);
+    }
+
+    protected void loadWorkspace(
+            List<ShortcutInfo> allDeepShortcuts,
+            Uri contentUri,
+            String selection,
+            @Nullable LoaderMemoryLogger logger) {
         final Context context = mApp.getContext();
         final ContentResolver contentResolver = context.getContentResolver();
         final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
@@ -324,16 +355,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 +436,7 @@
                 LauncherAppWidgetProviderInfo widgetProviderInfo;
                 Intent intent;
                 String targetPkg;
+                List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
 
                 while (!mStopped && c.moveToNext()) {
                     try {
@@ -536,7 +559,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 +599,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 +615,8 @@
                             }
 
                             if (info != null) {
+                                iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon));
+
                                 c.applyCommonProperties(info);
 
                                 info.intent = intent;
@@ -621,7 +650,7 @@
                                         }
                                 }
 
-                                c.checkAndAddItem(info, mBgDataModel);
+                                c.checkAndAddItem(info, mBgDataModel, logger);
                             } else {
                                 throw new RuntimeException("Unexpected null WorkspaceItemInfo");
                             }
@@ -640,7 +669,7 @@
                             // no special handling required for restored folders
                             c.markRestored();
 
-                            c.checkAndAddItem(folderInfo, mBgDataModel);
+                            c.checkAndAddItem(folderInfo, mBgDataModel, logger);
                             break;
 
                         case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
@@ -791,8 +820,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 +835,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 +953,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 +967,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 +1066,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..94e06d1 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);
@@ -273,7 +292,8 @@
         FileLog.d(TAG, "removing items from db " + items.stream().map(
                 (item) -> item.getTargetComponent() == null ? ""
                         : item.getTargetComponent().getPackageName()).collect(
-                Collectors.joining(",")), new Exception());
+                Collectors.joining(",")));
+        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..1e8e3ca 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -25,6 +25,7 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SETTINGS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WALLPAPERS;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
@@ -55,6 +56,7 @@
 import com.android.launcher3.logger.LauncherAtom.Shortcut;
 import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
 import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
+import com.android.launcher3.logger.LauncherAtom.WallpapersContainer;
 import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.util.ContentWriter;
@@ -164,6 +166,7 @@
 
     public void copyFrom(ItemInfo info) {
         id = info.id;
+        title = info.title;
         cellX = info.cellX;
         cellY = info.cellY;
         spanX = info.spanX;
@@ -232,9 +235,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);
@@ -372,7 +375,8 @@
 
     protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
         LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
-        itemBuilder.setIsWork(user != Process.myUserHandle());
+        itemBuilder.setIsWork(!Process.myUserHandle().equals(user));
+        itemBuilder.setRank(rank);
         return itemBuilder;
     }
 
@@ -427,6 +431,10 @@
                 return ContainerInfo.newBuilder()
                         .setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance())
                         .build();
+            case CONTAINER_WALLPAPERS:
+                return ContainerInfo.newBuilder()
+                        .setWallpapersContainer(WallpapersContainer.getDefaultInstance())
+                        .build();
             case EXTENDED_CONTAINERS:
                 return ContainerInfo.newBuilder()
                         .setExtendedContainers(getExtendedContainer())
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..c6e5e8a 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,15 @@
     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;
+    public static final int FLAG_SEARCH_IN_APP = 1 << 7;
 
-    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 +61,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 +69,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 +91,7 @@
     }
 
     public void setFlags(int flags) {
-        mFlags |= flags ;
+        mFlags |= flags;
     }
 
     @Override
@@ -134,4 +148,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..b1a4109 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,48 +73,49 @@
  *
  * @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.
-    private static final int OPEN_DURATION = 276;
-    private static final int OPEN_FADE_START_DELAY = 0;
-    private static final int OPEN_FADE_DURATION = 38;
-    private static final int OPEN_CHILD_FADE_START_DELAY = 38;
-    private static final int OPEN_CHILD_FADE_DURATION = 76;
+    protected int OPEN_DURATION = 276;
+    protected int OPEN_FADE_START_DELAY = 0;
+    protected int OPEN_FADE_DURATION = 38;
+    protected int OPEN_CHILD_FADE_START_DELAY = 38;
+    protected int OPEN_CHILD_FADE_DURATION = 76;
 
-    private static final int CLOSE_DURATION = 200;
-    private static final int CLOSE_FADE_START_DELAY = 140;
-    private static final int CLOSE_FADE_DURATION = 50;
-    private static final int CLOSE_CHILD_FADE_START_DELAY = 0;
-    private static final int CLOSE_CHILD_FADE_DURATION = 140;
+    protected int CLOSE_DURATION = 200;
+    protected int CLOSE_FADE_START_DELAY = 140;
+    protected int CLOSE_FADE_DURATION = 50;
+    protected int CLOSE_CHILD_FADE_START_DELAY = 0;
+    protected int CLOSE_CHILD_FADE_DURATION = 140;
 
     // Index used to get background color when using local wallpaper color extraction,
     private static final int DARK_COLOR_EXTRACTION_INDEX = android.R.color.system_neutral2_800;
     private static final int LIGHT_COLOR_EXTRACTION_INDEX = android.R.color.system_accent2_50;
 
-    private final Rect mTempRect = new Rect();
+    protected final Rect mTempRect = new Rect();
 
     protected final LayoutInflater mInflater;
-    private final float mOutlineRadius;
-    protected final T mLauncher;
+    protected final float mOutlineRadius;
+    protected final T mActivityContext;
     protected final boolean mIsRtl;
 
-    private final int mArrowOffsetVertical;
-    private final int mArrowOffsetHorizontal;
-    private final int mArrowWidth;
-    private final int mArrowHeight;
-    private final int mArrowPointRadius;
-    private final View mArrow;
+    protected final int mArrowOffsetVertical;
+    protected final int mArrowOffsetHorizontal;
+    protected final int mArrowWidth;
+    protected final int mArrowHeight;
+    protected final int mArrowPointRadius;
+    protected final View mArrow;
 
     private final int mMargin;
 
     protected boolean mIsLeftAligned;
     protected boolean mIsAboveIcon;
-    private int mGravity;
+    protected int mGravity;
 
     protected AnimatorSet mOpenCloseAnimator;
     protected boolean mDeferContainerRemoval;
+    protected boolean shouldScaleArrow = false;
 
     private final GradientDrawable mRoundedTop;
     private final GradientDrawable mRoundedBottom;
@@ -126,24 +123,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<>();
+    protected int mArrowColor;
+    protected final List<LocalColorExtractor> mColorExtractors;
 
-    @Nullable protected LocalColorExtractor mColorExtractor;
-
-    private final float mElevation;
+    protected 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 +169,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 +232,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;
+    protected void assignMarginsAndBackgrounds(ViewGroup viewGroup, int backgroundColor) {
+        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 +267,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 +286,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 +303,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 +313,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 +328,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 +348,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 +436,7 @@
     /**
      * Shows the popup at the desired location.
      */
-    protected void show() {
+    public void show() {
         setupForDisplay();
         onInflationComplete(false);
         assignMarginsAndBackgrounds(this);
@@ -457,7 +446,7 @@
         animateOpen();
     }
 
-    private void setupForDisplay() {
+    protected void setupForDisplay() {
         setVisibility(View.INVISIBLE);
         mIsOpen = true;
         getPopupContainer().addView(this);
@@ -494,7 +483,7 @@
         mArrow.setVisibility(show && shouldAddArrow() ? VISIBLE : INVISIBLE);
     }
 
-    private void addArrow() {
+    protected void addArrow() {
         getPopupContainer().addView(mArrow);
         mArrow.setX(getX() + getArrowLeft());
 
@@ -510,7 +499,7 @@
         mArrow.setPivotY(mIsAboveIcon ? mArrowHeight : 0);
     }
 
-    private void updateArrowColor() {
+    protected void updateArrowColor() {
         if (!Gravity.isVertical(mGravity)) {
             mArrow.setBackground(new RoundedArrowDrawable(
                     mArrowWidth, mArrowHeight, mArrowPointRadius,
@@ -698,12 +687,13 @@
         return getChildCount() > 0 ? getChildAt(0) : this;
     }
 
-    private void animateOpen() {
+    protected void animateOpen() {
         setVisibility(View.VISIBLE);
 
         mOpenCloseAnimator = getOpenCloseAnimator(true, OPEN_DURATION, OPEN_FADE_START_DELAY,
                 OPEN_FADE_DURATION, OPEN_CHILD_FADE_START_DELAY, OPEN_CHILD_FADE_DURATION,
                 DECELERATED_EASE);
+        onCreateOpenAnimation(mOpenCloseAnimator);
         mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -740,6 +730,14 @@
         scale.setInterpolator(interpolator);
         animatorSet.play(scale);
 
+        if (shouldScaleArrow) {
+            Animator arrowScaleAnimator = ObjectAnimator.ofFloat(mArrow, View.SCALE_Y,
+                    scaleValues);
+            arrowScaleAnimator.setDuration(totalDuration);
+            arrowScaleAnimator.setInterpolator(interpolator);
+            animatorSet.play(arrowScaleAnimator);
+        }
+
         fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet);
 
         return animatorSet;
@@ -797,6 +795,11 @@
     }
 
     /**
+     * Called when creating the open transition allowing subclass can add additional animations.
+     */
+    protected void onCreateOpenAnimation(AnimatorSet anim) { }
+
+    /**
      * Called when creating the close transition allowing subclass can add additional animations.
      */
     protected void onCreateCloseAnimation(AnimatorSet anim) { }
@@ -814,11 +817,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 +830,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..b261970 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));
     }
 
@@ -397,7 +396,7 @@
      * Current behavior:
      * - Start the drag if the touch passes a certain distance from the original touch down.
      */
-    public DragOptions.PreDragCondition createPreDragCondition() {
+    public DragOptions.PreDragCondition createPreDragCondition(boolean updateIconUi) {
         return new DragOptions.PreDragCondition() {
 
             @Override
@@ -407,6 +406,9 @@
 
             @Override
             public void onPreDragStart(DropTarget.DragObject dragObject) {
+                if (!updateIconUi) {
+                    return;
+                }
                 if (mIsAboveIcon) {
                     // Hide only the icon, keep the text visible.
                     mOriginalIcon.setIconVisible(false);
@@ -419,6 +421,9 @@
 
             @Override
             public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+                if (!updateIconUi) {
+                    return;
+                }
                 mOriginalIcon.setIconVisible(true);
                 if (dragStarted) {
                     // Make sure we keep the original icon hidden while it is being dragged.
@@ -439,7 +444,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 +485,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 +598,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/RoundedArrowDrawable.java b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
index e662d5c..436aa51 100644
--- a/src/com/android/launcher3/popup/RoundedArrowDrawable.java
+++ b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
@@ -78,6 +78,32 @@
         mPath.transform(pathTransform);
     }
 
+    /**
+     * Constructor for an arrow that points to the left or right.
+     *
+     * @param width        of the arrow.
+     * @param height       of the arrow.
+     * @param radius       of the tip of the arrow.
+     * @param isPointingLeft or not.
+     * @param color        to draw the triangle.
+     */
+    public RoundedArrowDrawable(float width, float height, float radius, boolean isPointingLeft,
+            int color) {
+        mPath = new Path();
+        mPaint = new Paint();
+        mPaint.setColor(color);
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setAntiAlias(true);
+
+        // Make the drawable with the triangle pointing down...
+        addDownPointingRoundedTriangleToPath(width, height, radius, mPath);
+
+        // ... then rotate it to the side it needs to point.
+        Matrix pathTransform = new Matrix();
+        pathTransform.setRotate(isPointingLeft ? 90 : -90, width * 0.5f, height * 0.5f);
+        mPath.transform(pathTransform);
+    }
+
     @Override
     public void draw(Canvas canvas) {
         canvas.drawPath(mPath, mPaint);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index d3f4909..af87275 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,12 +37,12 @@
  * 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;
-    private final int mLabelResId;
-    private final int mAccessibilityActionId;
+    protected final int mLabelResId;
+    protected int mAccessibilityActionId;
 
     protected final T mTarget;
     protected final ItemInfo mItemInfo;
@@ -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,22 +137,67 @@
 
     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) {
+        @Nullable
+        private SplitAccessibilityInfo mSplitA11yInfo;
+
+        public AppInfo(T target, ItemInfo itemInfo) {
             super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
                     itemInfo);
         }
 
+        /**
+         * Constructor used by overview for staged split to provide custom A11y information.
+         *
+         * Future improvements considerations:
+         * Have the logic in {@link #createAccessibilityAction(Context)} be moved to super
+         * call in {@link SystemShortcut#createAccessibilityAction(Context)} by having
+         * SystemShortcut be aware of TaskContainers and staged split.
+         * That way it could directly create the correct node info for any shortcut that supports
+         * split, but then we'll need custom resIDs for each pair of shortcuts.
+         */
+        public AppInfo(T target, ItemInfo itemInfo, SplitAccessibilityInfo accessibilityInfo) {
+            this(target, itemInfo);
+            mSplitA11yInfo = accessibilityInfo;
+            mAccessibilityActionId = accessibilityInfo.nodeId;
+        }
+
+        @Override
+        public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(
+                Context context) {
+            if (mSplitA11yInfo != null && mSplitA11yInfo.containsMultipleTasks) {
+                String accessibilityLabel = context.getString(R.string.split_app_info_accessibility,
+                        mSplitA11yInfo.taskTitle);
+                return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId,
+                        accessibilityLabel);
+            } else {
+                return super.createAccessibilityAction(context);
+            }
+        }
+
         @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)
                     .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
         }
+
+        public static class SplitAccessibilityInfo {
+            public final boolean containsMultipleTasks;
+            public final CharSequence taskTitle;
+            public final int nodeId;
+
+            public SplitAccessibilityInfo(boolean containsMultipleTasks,
+                    CharSequence taskTitle, int nodeId) {
+                this.containsMultipleTasks = containsMultipleTasks;
+                this.taskTitle = taskTitle;
+                this.nodeId = nodeId;
+            }
+        }
     }
 
     public static final Factory<BaseDraggingActivity> INSTALL = (activity, itemInfo) -> {
@@ -180,7 +217,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 +233,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..b510378 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -16,89 +16,26 @@
 
 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) {
+    public static IntArray queryIntArray(boolean distinct, SQLiteDatabase db, String tableName,
+            String columnName, String selection, String groupBy, String orderBy) {
         IntArray out = new IntArray();
-        try (Cursor c = db.query(tableName, new String[] { columnName }, selection, null,
-                groupBy, null, orderBy)) {
+        try (Cursor c = db.query(distinct, tableName, new String[] { columnName }, selection, null,
+                groupBy, null, orderBy, null)) {
             while (c.moveToNext()) {
                 out.add(c.getInt(0));
             }
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..d994dbe 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.provider;
 
+import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
+import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 
 import android.app.backup.BackupManager;
@@ -38,6 +40,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 +60,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 +68,30 @@
     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) {
         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(context, helper, db, new BackupManager(context));
             task.restoreAppWidgetIdsIfExists(context);
             t.commit();
             return true;
@@ -114,7 +134,7 @@
         GridBackupTable backupTable = new GridBackupTable(context, db, idp.numDatabaseHotseatIcons,
                 idp.numColumns, idp.numRows);
         if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
-            int itemsDeleted = sanitizeDB(helper, db, backupManager);
+            int itemsDeleted = sanitizeDB(context, helper, db, backupManager);
             LauncherAppState.getInstance(context).getModel().forceReload();
             restoreAppWidgetIdsIfExists(context);
             if (itemsDeleted == 0) {
@@ -131,11 +151,12 @@
      *      the restored apps get installed.
      *   3. If the user serial for any restored profile is different than that of the previous
      *      device, update the entries to the new profile id.
+     *   4. If restored from a single display backup, remove gaps between screenIds
      *
      * @return number of items deleted.
      */
-    private int sanitizeDB(DatabaseHelper helper, SQLiteDatabase db, BackupManager backupManager)
-            throws Exception {
+    private int sanitizeDB(Context context, DatabaseHelper helper, SQLiteDatabase db,
+            BackupManager backupManager) throws Exception {
         // Primary user ids
         long myProfileId = helper.getDefaultUserSerial();
         long oldProfileId = getDefaultProfileId(db);
@@ -211,10 +232,43 @@
         if (myProfileId != oldProfileId) {
             changeDefaultColumn(db, myProfileId);
         }
+
+        // If restored from a single display backup, remove gaps between screenIds
+        if (Utilities.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE)
+                != TYPE_MULTI_DISPLAY) {
+            removeScreenIdGaps(db);
+        }
+
         return itemsDeleted;
     }
 
     /**
+     * Remove gaps between screenIds to make sure no empty pages are left in between.
+     *
+     * e.g. [0, 3, 4, 6, 7] -> [0, 1, 2, 3, 4]
+     */
+    protected void removeScreenIdGaps(SQLiteDatabase db) {
+        FileLog.d(TAG, "Removing gaps between screenIds");
+        IntArray distinctScreens = LauncherDbUtils.queryIntArray(true, db, Favorites.TABLE_NAME,
+                Favorites.SCREEN, Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP, null,
+                Favorites.SCREEN);
+        if (distinctScreens.isEmpty()) {
+            return;
+        }
+
+        StringBuilder sql = new StringBuilder("UPDATE ").append(Favorites.TABLE_NAME)
+                .append(" SET ").append(Favorites.SCREEN).append(" =\nCASE\n");
+        int screenId = distinctScreens.contains(0) ? 0 : 1;
+        for (int i = 0; i < distinctScreens.size(); i++) {
+            sql.append("WHEN ").append(Favorites.SCREEN).append(" == ")
+                    .append(distinctScreens.get(i)).append(" THEN ").append(screenId++).append("\n");
+        }
+        sql.append("ELSE screen\nEND WHERE ").append(Favorites.CONTAINER).append(" = ")
+                .append(Favorites.CONTAINER_DESKTOP).append(";");
+        db.execSQL(sql.toString());
+    }
+
+    /**
      * Updates profile id of all entries from {@param oldProfileId} to {@param newProfileId}.
      */
     protected void migrateProfileId(SQLiteDatabase db, long oldProfileId, long newProfileId) {
@@ -279,12 +333,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/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index f348a33..0c39632 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -49,6 +49,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 
 import java.util.Collections;
@@ -253,12 +254,13 @@
                 case ALLOW_ROTATION_PREFERENCE_KEY:
                     DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get(
                             getContext()).getDeviceProfile(getContext());
-                    if (deviceProfile.allowRotation) {
+                    if (deviceProfile.isTablet) {
                         // Launcher supports rotation by default. No need to show this setting.
                         return false;
                     }
                     // Initialize the UI once
-                    preference.setDefaultValue(false);
+                    preference.setDefaultValue(
+                            RotationHelper.getAllowRotationDefaultValue(deviceProfile));
                     return true;
 
                 case FLAGS_PREFERENCE_KEY:
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index b34af97..1767939 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -84,7 +84,7 @@
                 + ", mCurrentStableState:" + mCurrentStableState
                 + ", mState:" + mState
                 + ", mRestState:" + mRestState
-                + ", isInTransition:" + (mConfig.currentAnimation != null) + ")";
+                + ", isInTransition:" + isInTransition() + ")";
     }
 
     public void dump(String prefix, PrintWriter writer) {
@@ -93,7 +93,7 @@
         writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
         writer.println(prefix + "\tmState:" + mState);
         writer.println(prefix + "\tmRestState:" + mRestState);
-        writer.println(prefix + "\tisInTransition:" + (mConfig.currentAnimation != null));
+        writer.println(prefix + "\tisInTransition:" + isInTransition());
     }
 
     public StateHandler[] getStateHandlers() {
@@ -130,6 +130,13 @@
     }
 
     /**
+     * @return {@code true} If there is an active transition.
+     */
+    public boolean isInTransition() {
+        return mConfig.currentAnimation != null;
+    }
+
+    /**
      * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
      */
     public void goToState(STATE_TYPE state) {
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 8a35cb3..e036943 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,19 @@
         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) { }
+
+    /**
+     * Runs the given {@param r} runnable when this activity binds to the touch interaction service.
+     */
+    public void runOnBindToTouchInteractionService(Runnable r) {
+        r.run();
+    }
 }
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 87871b1..867fd99 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,6 +18,10 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
+
+import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -25,7 +29,6 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.UiThreadHelper;
 
 /**
@@ -38,6 +41,17 @@
 
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
 
+    /**
+     * Returns the default value of {@link #ALLOW_ROTATION_PREFERENCE_KEY} preference.
+     */
+    public static boolean getAllowRotationDefaultValue(DeviceProfile deviceProfile) {
+        // If the device's pixel density was scaled (usually via settings for A11y), use the
+        // original dimensions to determine if rotation is allowed of not.
+        float originalSmallestWidth = dpiFromPx(
+                Math.min(deviceProfile.widthPx, deviceProfile.heightPx), DENSITY_DEVICE_STABLE);
+        return originalSmallestWidth >= MIN_TABLET_WIDTH;
+    }
+
     public static final int REQUEST_NONE = 0;
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
@@ -51,7 +65,7 @@
 
     /**
      * Rotation request made by
-     * {@link ActivityTracker.SchedulerCallback}.
+     * {@link com.android.launcher3.util.ActivityTracker.SchedulerCallback}.
      * This supersedes any other request.
      */
     private int mStateHandlerRequest = REQUEST_NONE;
@@ -84,7 +98,7 @@
                 mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
             }
             mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
-                    mActivity.getDeviceProfile().allowRotation);
+                    getAllowRotationDefaultValue(mActivity.getDeviceProfile()));
         } else {
             if (mSharedPrefs != null) {
                 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
@@ -98,7 +112,7 @@
         if (mDestroyed) return;
         boolean wasRotationEnabled = mHomeRotationEnabled;
         mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
-                mActivity.getDeviceProfile().allowRotation);
+                getAllowRotationDefaultValue(mActivity.getDeviceProfile()));
         if (mHomeRotationEnabled != wasRotationEnabled) {
             notifyChange();
         }
@@ -106,7 +120,7 @@
 
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
-        boolean ignoreAutoRotateSettings = dp.allowRotation;
+        boolean ignoreAutoRotateSettings = dp.isTablet;
         if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) {
             setIgnoreAutoRotateSettings(ignoreAutoRotateSettings);
             notifyChange();
@@ -143,7 +157,7 @@
     public void initialize() {
         if (!mInitialized) {
             mInitialized = true;
-            setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().allowRotation);
+            setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().isTablet);
             mActivity.addOnDeviceProfileChangeListener(this);
             notifyChange();
         }
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 8db1dbe..d52594e 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;
@@ -91,9 +90,4 @@
     public float getWorkspaceBackgroundAlpha(Launcher launcher) {
         return 0.2f;
     }
-
-    @Override
-    public int getVisibleElements(Launcher launcher) {
-        return (super.getVisibleElements(launcher) | HOTSEAT_ICONS) & ~TASKBAR;
-    }
 }
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 4261d08..8ebfd62 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -21,6 +21,7 @@
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Insets;
 import android.os.Build;
 import android.os.Bundle;
@@ -62,6 +63,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 +103,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 +131,38 @@
                 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;
+
+            case TestProtocol.REQUEST_START_DRAG_THRESHOLD: {
+                final Resources resources = mContext.getResources();
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+                        resources.getDimensionPixelSize(R.dimen.deep_shortcuts_start_drag_threshold)
+                                + resources.getDimensionPixelSize(R.dimen.pre_drag_view_scale));
+                return response;
+            }
+
+            case TestProtocol.REQUEST_ENABLE_ROTATION:
+                MAIN_EXECUTOR.submit(() ->
+                        Launcher.ACTIVITY_TRACKER.getCreatedActivity().getRotationHelper()
+                                .forceAllowRotationForTesting(Boolean.parseBoolean(arg)));
+                return null;
+
             default:
                 return null;
         }
@@ -162,6 +208,7 @@
 
     /**
      * Generic interface for setting a fiend in bundle
+     *
      * @param <T> the type of value being set
      */
     public interface BundleSetter<T> {
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..673b011 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -24,6 +24,8 @@
     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 String FOLDER_OPENED_MESSAGE = "TAPL_FOLDER_OPENED";
     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 +87,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 +95,34 @@
     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_START_DRAG_THRESHOLD = "start-drag-threshold";
+    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 final String REQUEST_ENABLE_ROTATION = "enable_rotation";
+
+    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/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
index 1276ece..52c3581 100644
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -17,6 +17,7 @@
 
 import static android.view.MotionEvent.INVALID_POINTER_ID;
 
+import android.content.Context;
 import android.graphics.PointF;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -26,6 +27,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.R;
+
 import java.util.LinkedList;
 import java.util.Queue;
 
@@ -44,10 +47,9 @@
     private static final boolean DBG = false;
     private static final String TAG = "BaseSwipeDetector";
     private static final float ANIMATION_DURATION = 1200;
-    /** The minimum release velocity in pixels per millisecond that triggers fling.*/
-    private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
     private static final PointF sTempPoint = new PointF();
 
+    private final float mReleaseVelocity;
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
     protected final boolean mIsRtl;
@@ -64,6 +66,7 @@
     private boolean mIsSettingState;
 
     protected boolean mIgnoreSlopWhenSettling;
+    protected Context mContext;
 
     private enum ScrollState {
         IDLE,
@@ -71,10 +74,14 @@
         SETTLING       // onDragEnd
     }
 
-    protected BaseSwipeDetector(@NonNull ViewConfiguration config, boolean isRtl) {
+    protected BaseSwipeDetector(@NonNull Context context, @NonNull ViewConfiguration config,
+            boolean isRtl) {
         mTouchSlop = config.getScaledTouchSlop();
         mMaxVelocity = config.getScaledMaximumFlingVelocity();
         mIsRtl = isRtl;
+        mContext = context;
+        mReleaseVelocity = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.base_swift_detector_fling_release_velocity);
     }
 
     public static long calculateDuration(float velocity, float progressNeeded) {
@@ -120,7 +127,7 @@
     }
 
     public boolean isFling(float velocity) {
-        return Math.abs(velocity) > RELEASE_VELOCITY_PX_MS;
+        return Math.abs(velocity) > mReleaseVelocity;
     }
 
     public boolean onTouchEvent(MotionEvent ev) {
@@ -236,7 +243,7 @@
         } else {
             mSubtractDisplacement.x = mDisplacement.x > 0 ? mTouchSlop : -mTouchSlop;
             mSubtractDisplacement.y = mDisplacement.y > 0 ? mTouchSlop : -mTouchSlop;
-        } 
+        }
     }
 
     protected abstract boolean shouldScrollStart(PointF displacement);
diff --git a/src/com/android/launcher3/touch/BothAxesSwipeDetector.java b/src/com/android/launcher3/touch/BothAxesSwipeDetector.java
index 944391e..6e2f0d8 100644
--- a/src/com/android/launcher3/touch/BothAxesSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BothAxesSwipeDetector.java
@@ -21,7 +21,6 @@
 import android.view.ViewConfiguration;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.Utilities;
 
@@ -43,13 +42,7 @@
     private int mScrollDirections;
 
     public BothAxesSwipeDetector(@NonNull Context context, @NonNull Listener l) {
-        this(ViewConfiguration.get(context), l, Utilities.isRtl(context.getResources()));
-    }
-
-    @VisibleForTesting
-    protected BothAxesSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
-            boolean isRtl) {
-        super(config, isRtl);
+        super(context, ViewConfiguration.get(context), Utilities.isRtl(context.getResources()));
         mListener = l;
     }
 
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index b53f96e..8d57d69 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
 import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
@@ -46,6 +47,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;
@@ -284,7 +287,13 @@
                         Toast.LENGTH_SHORT).show();
             }
         }
-        launcher.getStatsLogManager().logger().withItemInfo(itemInfo).log(LAUNCHER_APP_LAUNCH_TAP);
+        if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SEARCH_IN_APP)) {
+            launcher.getStatsLogManager().logger().withItemInfo(itemInfo).log(
+                    LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH);
+        } else {
+            launcher.getStatsLogManager().logger().withItemInfo(itemInfo).log(
+                    LAUNCHER_APP_LAUNCH_TAP);
+        }
     }
 
     private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
@@ -314,6 +323,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..a94ad7c 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;
@@ -31,17 +36,22 @@
 import android.graphics.RectF;
 import android.graphics.drawable.ShapeDrawable;
 import android.util.FloatProperty;
+import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.Surface;
 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;
@@ -92,12 +102,12 @@
     }
 
     @Override
-    public <T> void set(T target, Int2DAction<T> action, int param) {
+    public <T> void setPrimary(T target, Int2DAction<T> action, int param) {
         action.call(target, 0, param);
     }
 
     @Override
-    public <T> void set(T target, Float2DAction<T> action, float param) {
+    public <T> void setPrimary(T target, Float2DAction<T> action, float param) {
         action.call(target, 0, param);
     }
 
@@ -107,6 +117,12 @@
     }
 
     @Override
+    public <T> void set(T target, Int2DAction<T> action, int primaryParam,
+            int secondaryParam) {
+        action.call(target, secondaryParam, primaryParam);
+    }
+
+    @Override
     public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
         return event.getY(pointerIndex);
     }
@@ -162,16 +178,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 +220,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,17 +250,12 @@
         return view.getHeight() - view.getPaddingBottom() - insets.bottom;
     }
 
-    @Override
-    public int getPrimaryTranslationDirectionFactor() {
-        return -1;
-    }
-
     public int getSecondaryTranslationDirectionFactor() {
         return 1;
     }
 
     @Override
-    public int getSplitTranslationDirectionFactor(int stagePosition) {
+    public int getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile) {
         if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
             return -1;
         } else {
@@ -249,31 +264,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 +293,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
@@ -300,6 +309,46 @@
         return new PointF(margin, 0);
     }
 
+    @Override
+    public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+            int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+            View[] thumbnailViews, int desiredTaskId, View banner) {
+        float translationX = 0;
+        float translationY = 0;
+        FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+        banner.setPivotX(0);
+        banner.setPivotY(0);
+        banner.setRotation(getDegreesRotated());
+        translationX = banner.getHeight();
+        FrameLayout.LayoutParams snapshotParams =
+                (FrameLayout.LayoutParams) thumbnailViews[0]
+                        .getLayoutParams();
+        bannerParams.gravity = TOP | START;
+        if (splitBounds == null) {
+            // Single, fullscreen case
+            bannerParams.width = taskViewHeight - snapshotParams.topMargin;
+            return new Pair<>(translationX, Integer.valueOf(snapshotParams.topMargin).floatValue());
+        }
+
+        // Set correct width
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            bannerParams.width = thumbnailViews[0].getMeasuredHeight();
+        } else {
+            bannerParams.width = thumbnailViews[1].getMeasuredHeight();
+        }
+
+        // Set translations
+        if (desiredTaskId == splitBounds.rightBottomTaskId) {
+            translationY = (snapshotParams.topMargin + taskViewHeight)
+                    * (splitBounds.leftTaskPercent) +
+                    (taskViewHeight * splitBounds.dividerWidthPercent);
+        }
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            translationY = snapshotParams.topMargin;
+        }
+        return new Pair<>(translationX, translationY);
+    }
+
     /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
 
     @Override
@@ -348,13 +397,114 @@
     public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
         // Add "left" side of phone which is actually the top
         return Collections.singletonList(new SplitPositionOption(
-                R.drawable.ic_split_screen, R.string.split_screen_position_left,
+                R.drawable.ic_split_left, R.string.split_screen_position_left,
                 STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
     }
 
     @Override
-    public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
-            DeviceProfile deviceProfile) {
-        return primary;
+    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,
+            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, int primarySnapshotWidth, int primarySnapshotHeight,
+            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+        FrameLayout.LayoutParams primaryIconParams =
+                (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
+        FrameLayout.LayoutParams secondaryIconParams =
+                new FrameLayout.LayoutParams(primaryIconParams);
+        int dividerBar = (splitConfig.appsStackedVertically ?
+                splitConfig.visualDividerBounds.height() :
+                splitConfig.visualDividerBounds.width());
+
+        primaryIconParams.gravity = (isRtl ? START : END) | TOP;
+        primaryIconView.setTranslationY(primarySnapshotHeight - primaryIconView.getHeight() / 2f);
+        primaryIconView.setTranslationX(0);
+
+        secondaryIconParams.gravity = (isRtl ? START : END) | TOP;
+        secondaryIconView.setTranslationY(primarySnapshotHeight + taskIconHeight + dividerBar);
+        secondaryIconView.setTranslationX(0);
+        primaryIconView.setLayoutParams(primaryIconParams);
+        secondaryIconView.setLayoutParams(secondaryIconParams);
+    }
+
+    @Override
+    public int getDefaultSplitPosition(DeviceProfile deviceProfile) {
+        throw new IllegalStateException("Default position not available in fake landscape");
+    }
+
+    @Override
+    public Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
+            FloatProperty secondary, DeviceProfile deviceProfile) {
+        return new Pair<>(primary, secondary);
     }
 }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 266e05f..19c4639 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -24,15 +24,19 @@
 import android.graphics.RectF;
 import android.graphics.drawable.ShapeDrawable;
 import android.util.FloatProperty;
+import android.util.Pair;
 import android.view.MotionEvent;
 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;
 
@@ -62,9 +66,10 @@
     Float2DAction<Canvas> CANVAS_TRANSLATE = Canvas::translate;
     Float2DAction<Matrix> MATRIX_POST_TRANSLATE = Matrix::postTranslate;
 
-    <T> void set(T target, Int2DAction<T> action, int param);
-    <T> void set(T target, Float2DAction<T> action, float param);
+    <T> void setPrimary(T target, Int2DAction<T> action, int param);
+    <T> void setPrimary(T target, Float2DAction<T> action, float param);
     <T> void setSecondary(T target, Float2DAction<T> action, float param);
+    <T> void set(T target, Int2DAction<T> action, int primaryParam, int secondaryParam);
     float getPrimaryDirection(MotionEvent event, int pointerIndex);
     float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
     int getMeasuredSize(View view);
@@ -78,27 +83,27 @@
     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);
+    int getSplitTranslationDirectionFactor(@StagePosition int stagePosition,
+            DeviceProfile deviceProfile);
     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);
@@ -110,15 +115,58 @@
     float getSecondaryValue(float x, float y);
 
     boolean isLayoutNaturalToLauncher();
-    FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
-            DeviceProfile deviceProfile);
+    Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
+            FloatProperty secondary, 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,
+            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, int primarySnapshotWidth, int primarySnapshotHeight,
+            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.
@@ -143,6 +191,10 @@
      */
     PointF getAdditionalInsetForTaskMenu(float margin);
 
+    Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+            int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+            View[] thumbnailViews, int desiredTaskId, View banner);
+
     // The following are only used by TaskViewTouchHandler.
     /** @return Either VERTICAL or HORIZONTAL. */
     SingleAxisSwipeDetector.Direction getUpDownSwipeDirection();
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index dd97af5..ad9f95c 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -16,6 +16,13 @@
 
 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.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;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
@@ -24,22 +31,28 @@
 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;
 import android.graphics.drawable.ShapeDrawable;
 import android.util.FloatProperty;
+import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.Surface;
 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 +60,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;
@@ -88,12 +104,12 @@
     }
 
     @Override
-    public <T> void set(T target, Int2DAction<T> action, int param) {
+    public <T> void setPrimary(T target, Int2DAction<T> action, int param) {
         action.call(target, param, 0);
     }
 
     @Override
-    public <T> void set(T target, Float2DAction<T> action, float param) {
+    public <T> void setPrimary(T target, Float2DAction<T> action, float param) {
         action.call(target, param, 0);
     }
 
@@ -103,6 +119,12 @@
     }
 
     @Override
+    public <T> void set(T target, Int2DAction<T> action, int primaryParam,
+            int secondaryParam) {
+        action.call(target, primaryParam, secondaryParam);
+    }
+
+    @Override
     public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
         return event.getX(pointerIndex);
     }
@@ -158,9 +180,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 +190,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 +228,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,18 +258,13 @@
         return view.getWidth() - view.getPaddingRight() - insets.right;
     }
 
-    @Override
-    public int getPrimaryTranslationDirectionFactor() {
-        return 1;
-    }
-
     public int getSecondaryTranslationDirectionFactor() {
         return -1;
     }
 
     @Override
-    public int getSplitTranslationDirectionFactor(int stagePosition) {
-        if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+    public int getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile) {
+        if (deviceProfile.isLandscape && stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
             return -1;
         } else {
             return 1;
@@ -251,16 +272,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 +288,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
@@ -320,6 +323,50 @@
         return new PointF(0, 0);
     }
 
+    @Override
+    public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+            int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+            View[] thumbnailViews, int desiredTaskId, View banner) {
+        float translationX = 0;
+        float translationY = 0;
+        FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+        banner.setPivotX(0);
+        banner.setPivotY(0);
+        banner.setRotation(getDegreesRotated());
+        if (splitBounds == null) {
+            // Single, fullscreen case
+            bannerParams.width = MATCH_PARENT;
+            bannerParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+            return new Pair<>(translationX, translationY);
+        }
+
+        bannerParams.gravity = BOTTOM | ((deviceProfile.isLandscape) ? START : CENTER_HORIZONTAL);
+
+        // Set correct width
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            bannerParams.width = thumbnailViews[0].getMeasuredWidth();
+        } else {
+            bannerParams.width = thumbnailViews[1].getMeasuredWidth();
+        }
+
+        // Set translations
+        if (deviceProfile.isLandscape) {
+            if (desiredTaskId == splitBounds.rightBottomTaskId) {
+                translationX = ((taskViewWidth * splitBounds.leftTaskPercent)
+                                + (taskViewWidth * splitBounds.dividerWidthPercent));
+            }
+        } else {
+            if (desiredTaskId == splitBounds.leftTopTaskId) {
+                FrameLayout.LayoutParams snapshotParams =
+                        (FrameLayout.LayoutParams) thumbnailViews[0]
+                                .getLayoutParams();
+                translationY = -((taskViewHeight - snapshotParams.topMargin)
+                        * (1f - splitBounds.topTaskPercent));
+            }
+        }
+        return new Pair<>(translationX, translationY);
+    }
+
     /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
 
     @Override
@@ -369,28 +416,27 @@
     public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
         List<SplitPositionOption> options = new ArrayList<>(1);
         // Add both left and right options if we're in tablet mode
-        // TODO: Add in correct icons
         if (dp.isTablet && dp.isLandscape) {
             options.add(new SplitPositionOption(
-                    R.drawable.ic_split_screen, R.string.split_screen_position_right,
+                    R.drawable.ic_split_right, R.string.split_screen_position_right,
                     STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
             options.add(new SplitPositionOption(
-                    R.drawable.ic_split_screen, R.string.split_screen_position_left,
+                    R.drawable.ic_split_left, R.string.split_screen_position_left,
                     STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
         } else {
             if (dp.isSeascape()) {
                 // Add left/right options
                 options.add(new SplitPositionOption(
-                        R.drawable.ic_split_screen, R.string.split_screen_position_right,
+                        R.drawable.ic_split_right, R.string.split_screen_position_right,
                         STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
             } else if (dp.isLandscape) {
                 options.add(new SplitPositionOption(
-                        R.drawable.ic_split_screen, R.string.split_screen_position_left,
+                        R.drawable.ic_split_left, R.string.split_screen_position_left,
                         STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
             } else {
                 // Only add top option
                 options.add(new SplitPositionOption(
-                        R.drawable.ic_split_screen, R.string.split_screen_position_top,
+                        R.drawable.ic_split_top, R.string.split_screen_position_top,
                         STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
             }
         }
@@ -398,12 +444,186 @@
     }
 
     @Override
-    public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
-            DeviceProfile dp) {
-        if (dp.isLandscape) { // or seascape
-            return primary;
+    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;
+        if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+            if (isLandscape) {
+                outRect.right = outRect.left + (int) (outRect.width() * splitInfo.leftTaskPercent);
+            } else {
+                outRect.bottom = outRect.top + (int) (outRect.height() * splitInfo.topTaskPercent);
+            }
         } else {
-            return secondary;
+            if (isLandscape) {
+                outRect.left += (int) (outRect.width() *
+                        (splitInfo.leftTaskPercent + splitInfo.dividerWidthPercent));
+            } else {
+                outRect.top += (int) (outRect.height() *
+                        (splitInfo.topTaskPercent + splitInfo.dividerHeightPercent));
+            }
+        }
+    }
+
+    @Override
+    public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
+            int parentWidth, int parentHeight,
+            StagedSplitBounds splitBoundsConfig, DeviceProfile dp) {
+        int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
+        int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
+        int dividerBar = splitBoundsConfig.appsStackedVertically
+                ? (int) (splitBoundsConfig.dividerHeightPercent * parentHeight)
+                : (int) (splitBoundsConfig.dividerWidthPercent * parentWidth);
+        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, int primarySnapshotWidth, int primarySnapshotHeight,
+            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+        FrameLayout.LayoutParams primaryIconParams =
+                (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
+        FrameLayout.LayoutParams secondaryIconParams =
+                new FrameLayout.LayoutParams(primaryIconParams);
+        int dividerBar = (splitConfig.appsStackedVertically ?
+                splitConfig.visualDividerBounds.height() :
+                splitConfig.visualDividerBounds.width());
+
+        if (deviceProfile.isLandscape) {
+            primaryIconParams.gravity = TOP | START;
+            primaryIconView.setTranslationX(
+                    primarySnapshotWidth - primaryIconView.getMeasuredWidth());
+            primaryIconView.setTranslationY(0);
+            secondaryIconParams.gravity = TOP | START;
+            secondaryIconView.setTranslationX(primarySnapshotWidth + dividerBar);
+        } else {
+            primaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
+            primaryIconView.setTranslationX(-(primaryIconView.getMeasuredWidth()) / 2f);
+            primaryIconView.setTranslationY(0);
+
+            secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
+            secondaryIconView.setTranslationX(secondaryIconView.getMeasuredWidth() / 2f);
+        }
+        secondaryIconView.setTranslationY(0);
+        primaryIconView.setLayoutParams(primaryIconParams);
+        secondaryIconView.setLayoutParams(secondaryIconParams);
+    }
+
+    @Override
+    public int getDefaultSplitPosition(DeviceProfile deviceProfile) {
+        if (!deviceProfile.isTablet) {
+            throw new IllegalStateException("Default position available only for large screens");
+        }
+        if (deviceProfile.isLandscape) {
+            return STAGE_POSITION_BOTTOM_OR_RIGHT;
+        } else {
+            return STAGE_POSITION_TOP_OR_LEFT;
+        }
+    }
+
+    @Override
+    public Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
+            FloatProperty secondary, DeviceProfile deviceProfile) {
+        if (deviceProfile.isLandscape) { // or seascape
+            return new Pair<>(primary, secondary);
+        } else {
+            return new Pair<>(secondary, primary);
         }
     }
 }
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 91d44bd..de5f99c 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -16,6 +16,12 @@
 
 package com.android.launcher3.touch;
 
+import static android.view.Gravity.BOTTOM;
+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 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;
@@ -24,14 +30,17 @@
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.util.Pair;
 import android.view.Surface;
 import android.view.View;
+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.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.Collections;
@@ -45,7 +54,7 @@
     }
 
     @Override
-    public int getSplitTranslationDirectionFactor(int stagePosition) {
+    public int getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile) {
         if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
             return -1;
         } else {
@@ -54,11 +63,6 @@
     }
 
     @Override
-    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
-        return translationOffset;
-    }
-
-    @Override
     public boolean getRecentsRtlSetting(Resources resources) {
         return Utilities.isRtl(resources);
     }
@@ -81,13 +85,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
@@ -102,6 +108,47 @@
     }
 
     @Override
+    public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+            int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+            View[] thumbnailViews, int desiredTaskId, View banner) {
+        float translationX = 0;
+        float translationY = 0;
+        FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+        banner.setPivotX(0);
+        banner.setPivotY(0);
+        banner.setRotation(getDegreesRotated());
+        FrameLayout.LayoutParams snapshotParams =
+                (FrameLayout.LayoutParams) thumbnailViews[0]
+                        .getLayoutParams();
+        bannerParams.gravity = BOTTOM | END;
+        translationX = taskViewWidth - banner.getHeight();
+        if (splitBounds == null) {
+            // Single, fullscreen case
+            bannerParams.width = taskViewHeight - snapshotParams.topMargin;
+            translationY = banner.getHeight();
+            return new Pair<>(translationX, translationY);
+        }
+
+        // Set correct width
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            bannerParams.width = thumbnailViews[1].getMeasuredHeight();
+        } else {
+            bannerParams.width = thumbnailViews[0].getMeasuredHeight();
+        }
+
+        // Set translations
+        if (desiredTaskId == splitBounds.rightBottomTaskId) {
+            translationY = -(taskViewHeight - snapshotParams.topMargin)
+                    * (1f - splitBounds.leftTaskPercent)
+                    + banner.getHeight();
+        }
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            translationY = banner.getHeight();
+        }
+        return new Pair<>(translationX, translationY);
+    }
+
+    @Override
     public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
         return dp.widthPx - rect.right;
     }
@@ -110,10 +157,38 @@
     public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
         // Add "right" option which is actually the top
         return Collections.singletonList(new SplitPositionOption(
-                R.drawable.ic_split_screen, R.string.split_screen_position_right,
+                R.drawable.ic_split_right, R.string.split_screen_position_right,
                 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;
+    }
+
+    @Override
+    public void setSplitIconParams(View primaryIconView, View secondaryIconView,
+            int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
+            boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+        super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight,
+                primarySnapshotWidth, primarySnapshotHeight, isRtl, deviceProfile, splitConfig);
+        FrameLayout.LayoutParams primaryIconParams =
+                (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
+        FrameLayout.LayoutParams secondaryIconParams =
+                (FrameLayout.LayoutParams) secondaryIconView.getLayoutParams();
+
+        primaryIconParams.gravity = (isRtl ? END : START) | TOP;
+        secondaryIconParams.gravity = (isRtl ? END : START) | TOP;
+        primaryIconView.setLayoutParams(primaryIconParams);
+        secondaryIconView.setLayoutParams(secondaryIconParams);
+    }
+
     /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
 
     @Override
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
index f751b7d..5c599c0 100644
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -106,13 +106,15 @@
 
     public SingleAxisSwipeDetector(@NonNull Context context, @NonNull Listener l,
             @NonNull Direction dir) {
-        this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
+        super(context, ViewConfiguration.get(context),  Utilities.isRtl(context.getResources()));
+        mListener = l;
+        mDir = dir;
     }
 
     @VisibleForTesting
-    protected SingleAxisSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
-            @NonNull Direction dir, boolean isRtl) {
-        super(config, isRtl);
+    protected SingleAxisSwipeDetector(@NonNull Context context, @NonNull ViewConfiguration config,
+            @NonNull Listener l, @NonNull Direction dir, boolean isRtl) {
+        super(context, config, isRtl);
         mListener = l;
         mDir = dir;
     }
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/ActivityLifecycleCallbacksAdapter.java b/src/com/android/launcher3/util/ActivityLifecycleCallbacksAdapter.java
new file mode 100644
index 0000000..baa8418
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityLifecycleCallbacksAdapter.java
@@ -0,0 +1,29 @@
+package com.android.launcher3.util;
+
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.os.Bundle;
+
+public interface ActivityLifecycleCallbacksAdapter extends ActivityLifecycleCallbacks {
+
+    default void onActivityCreated(Activity activity, Bundle bundle) {
+    }
+
+    default void onActivityDestroyed(Activity activity) {
+    }
+
+    default void onActivityPaused(Activity activity) {
+    }
+
+    default void onActivityResumed(Activity activity) {
+    }
+
+    default void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+    }
+
+    default void onActivityStarted(Activity activity) {
+    }
+
+    default void onActivityStopped(Activity activity) {
+    }
+}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index e2c0a32..c050c6c 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,21 @@
         }
         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 (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) {
+                Log.e("b/198965093",
+                        "Inconsistent number of displays"
+                                + "\ndisplay state: " + display.getState()
+                                + "\noldInfo.supportedBounds: " + oldInfo.supportedBounds
+                                + "\nnewInfo.supportedBounds: " + newInfo.supportedBounds);
+            }
+            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 +282,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 +292,6 @@
 
     public static class Info {
 
-        public final int id;
         public final int singleFrameMs;
 
         // Configuration properties
@@ -249,19 +300,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 +325,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..7917410 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -20,9 +20,11 @@
 import android.os.UserHandle;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.data.FolderInfo;
 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 +33,11 @@
  */
 public interface ItemInfoMatcher {
 
+    /**
+     * Empty component used for match testing
+     */
+    ComponentName EMPTY_COMPONENT = new ComponentName("", "");
+
     boolean matches(ItemInfo info, ComponentName cn);
 
     /**
@@ -39,7 +46,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;
         }
@@ -79,8 +86,16 @@
     }
 
     static ItemInfoMatcher ofShortcutKeys(Set<ShortcutKey> keys) {
-        return  (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
-                        keys.contains(ShortcutKey.fromItemInfo(info));
+        return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                && keys.contains(ShortcutKey.fromItemInfo(info));
+    }
+
+    /**
+     * Returns a matcher for items within folders.
+     */
+    static ItemInfoMatcher forFolderMatch(ItemInfoMatcher childOperator) {
+        return (info, cn) -> info instanceof FolderInfo && ((FolderInfo) info).contents.stream()
+                .anyMatch(childOperator::matchesInfo);
     }
 
     /**
@@ -89,4 +104,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..11cd07c 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -16,12 +16,15 @@
 
 package com.android.launcher3.util;
 
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
 import android.util.FloatProperty;
 import android.view.View;
 
 import com.android.launcher3.anim.AlphaUpdateListener;
 
 import java.util.Arrays;
+import java.util.function.Consumer;
 
 /**
  * Utility class to handle separating a single value as a factor of multiple values
@@ -83,6 +86,8 @@
         // Factor of all other alpha channels, only valid if mMyMask is present in mValidMask.
         private float mOthers = 1;
 
+        private Consumer<Float> mConsumer;
+
         AlphaProperty(int myMask) {
             mMyMask = myMask;
         }
@@ -107,19 +112,40 @@
             mValidMask = mMyMask;
             mValue = value;
 
-            mView.setAlpha(mOthers * mValue);
+            final float alpha = mOthers * mValue;
+            mView.setAlpha(alpha);
             if (mUpdateVisibility) {
                 AlphaUpdateListener.updateVisibility(mView);
             }
+            if (mConsumer != null) {
+                mConsumer.accept(mValue);
+            }
         }
 
         public float getValue() {
             return mValue;
         }
 
+        public void setConsumer(Consumer<Float> consumer) {
+            mConsumer = consumer;
+            if (mConsumer != null) {
+                mConsumer.accept(mValue);
+            }
+        }
+
         @Override
         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..cb714b2 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;
@@ -66,20 +68,85 @@
     public @interface StageType {}
     ///////////////////////////////////
 
+    /**
+     * Default split ratio for launching app pair from overview.
+     */
+    public static final float DEFAULT_SPLIT_RATIO = 0.5f;
+
     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;
         }
     }
+
+    /**
+     * NOTE: Engineers complained about too little ambiguity in the last survey, so there is a class
+     * with the same name/functionality in wm.shell.util (which launcher3 cannot be built against)
+     *
+     * If you make changes here, consider making the same changes there
+     */
+    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;
+        public final float dividerWidthPercent;
+        public final float dividerHeightPercent;
+        /**
+         * 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 final int leftTopTaskId;
+        public final int rightBottomTaskId;
+
+        public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
+                int rightBottomTaskId) {
+            this.leftTopBounds = leftTopBounds;
+            this.rightBottomBounds = rightBottomBounds;
+            this.leftTopTaskId = leftTopTaskId;
+            this.rightBottomTaskId = rightBottomTaskId;
+
+            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;
+            dividerWidthPercent = visualDividerBounds.width() / (float) rightBottomBounds.right;
+            dividerHeightPercent = visualDividerBounds.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/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 872adec..0a800c3 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -70,6 +70,8 @@
 
     // Manages loading the icon on a worker thread
     private static @Nullable IconLoadResult sIconLoadResult;
+    private static long sFetchIconId = 0;
+    private static long sRecycledFetchIconId = sFetchIconId;
 
     public static final float SHAPE_PROGRESS_DURATION = 0.10f;
     private static final RectF sTmpRectF = new RectF();
@@ -86,7 +88,6 @@
 
     private IconLoadResult mIconLoadResult;
 
-    // Draw the drawable of the BubbleTextView behind ClipIconView to reveal the built in shadow.
     private View mBtvDrawable;
 
     private ClipIconView mClipIconView;
@@ -347,10 +348,23 @@
             }
         }
 
-        if (!mIsOpening && btvIcon != null) {
+        setOriginalDrawableBackground(btvIcon);
+        invalidate();
+    }
+
+    /**
+     * Draws the drawable of the BubbleTextView behind ClipIconView
+     *
+     * This is used to:
+     * - Have icon displayed while Adaptive Icon is loading
+     * - Displays the built in shadow to ensure a clean handoff
+     *
+     * Allows nullable as this may be cleared when drawing is deferred to ClipIconView.
+     */
+    private void setOriginalDrawableBackground(@Nullable Drawable btvIcon) {
+        if (!mIsOpening) {
             mBtvDrawable.setBackground(btvIcon);
         }
-        invalidate();
     }
 
     /**
@@ -455,10 +469,12 @@
 
     @Override
     public void onAnimationStart(Animator animator) {
-        if (mIconLoadResult != null && mIconLoadResult.isIconLoaded) {
+        if ((mIconLoadResult != null && mIconLoadResult.isIconLoaded)
+                || (!mIsOpening && mBtvDrawable.getBackground() != null)) {
+            // No need to wait for icon load since we can display the BubbleTextView drawable.
             setVisibility(View.VISIBLE);
         }
-        if (!mIsOpening) {
+        if (!mIsOpening && mOriginalIcon != null) {
             // When closing an app, we want the item on the workspace to be invisible immediately
             setIconAndDotVisible(mOriginalIcon, false);
         }
@@ -518,9 +534,15 @@
 
         IconLoadResult result = new IconLoadResult(info,
                 btvIcon == null ? false : btvIcon.isThemed());
+        result.btvDrawable = btvIcon;
 
-        MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() ->
-                getIconResult(l, v, info, position, btvIcon, result));
+        final long fetchIconId = sFetchIconId++;
+        MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {
+            if (fetchIconId < sRecycledFetchIconId) {
+                return;
+            }
+            getIconResult(l, v, info, position, btvIcon, result);
+        });
 
         sIconLoadResult = result;
         return result;
@@ -542,6 +564,12 @@
                 launcher, parent);
         view.recycle();
 
+        // Init properties before getting the drawable.
+        view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
+        view.mIsOpening = isOpening;
+        view.mOriginalIcon = originalView;
+        view.mPositionOut = positionOut;
+
         // Get the drawable on the background thread
         boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal;
         if (shouldLoadIcon) {
@@ -551,14 +579,10 @@
                 view.mIconLoadResult = fetchIcon(launcher, originalView,
                         (ItemInfo) originalView.getTag(), isOpening);
             }
+            view.setOriginalDrawableBackground(view.mIconLoadResult.btvDrawable);
         }
         sIconLoadResult = null;
 
-        view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
-        view.mIsOpening = isOpening;
-        view.mOriginalIcon = originalView;
-        view.mPositionOut = positionOut;
-
         // Match the position of the original view.
         view.matchPositionOf(launcher, originalView, isOpening, positionOut);
 
@@ -616,12 +640,14 @@
         mLoadIconSignal = null;
         mEndRunnable = null;
         mFinalDrawableBounds.setEmpty();
+        mIsOpening = false;
         mPositionOut = null;
         mListenerView.setListener(null);
         mOriginalIcon = null;
         mOnTargetChangeRunnable = null;
         mBadge = null;
         sTmpObjArray[0] = null;
+        sRecycledFetchIconId = sFetchIconId;
         mIconLoadResult = null;
         mClipIconView.recycle();
         mBtvDrawable.setBackground(null);
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index e2e3be7..f32f904 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -158,7 +158,7 @@
         if (mContract == null) {
             return;
         }
-        View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(-1,
+        View icon = mLauncher.getFirstMatchForAppClose(-1,
                 mContract.componentName.getPackageName(), mContract.user);
 
         boolean iconChanged = mIcon != icon;
@@ -182,7 +182,7 @@
                 lp.topMargin = Math.round(mIconPosition.top);
             }
         }
-        if (iconChanged && !mIconBounds.isEmpty()) {
+        if (mIcon != null && iconChanged && !mIconBounds.isEmpty()) {
             // Record the icon display
             setCurrentIconVisible(true);
             Canvas c = mPicture.beginRecording(mIconBounds.width(), mIconBounds.height());
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index ecdd206..fc8b4b7 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -37,9 +37,9 @@
 import android.widget.Toast;
 
 import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
 import androidx.core.content.ContextCompat;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
@@ -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);
@@ -171,21 +175,6 @@
         return children;
     }
 
-    @VisibleForTesting
-    public static ArrowPopup getOptionsPopup(Launcher launcher) {
-        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
      */
@@ -226,6 +215,11 @@
             Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
             return null;
         } else {
+            AbstractFloatingView floatingView = AbstractFloatingView.getTopOpenViewWithType(
+                    launcher, TYPE_WIDGETS_FULL_SHEET);
+            if (floatingView != null) {
+                return (WidgetsFullSheet) floatingView;
+            }
             return WidgetsFullSheet.show(launcher, true /* animated */);
         }
     }
@@ -285,7 +279,7 @@
         public final OnLongClickListener clickListener;
 
         public OptionItem(Context context, int labelRes, int iconRes, EventEnum eventId,
-                          OnLongClickListener clickListener) {
+                OnLongClickListener clickListener) {
             this.labelRes = labelRes;
             this.label = context.getText(labelRes);
             this.icon = ContextCompat.getDrawable(context, iconRes);
@@ -294,7 +288,7 @@
         }
 
         public OptionItem(CharSequence label, Drawable icon, EventEnum eventId,
-                          OnLongClickListener clickListener) {
+                OnLongClickListener clickListener) {
             this.labelRes = 0;
             this.label = label;
             this.icon = icon;
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/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
index 49fcd2e..e582114 100644
--- a/src/com/android/launcher3/views/Snackbar.java
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -28,8 +28,9 @@
 import android.view.MotionEvent;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -44,7 +45,7 @@
     private static final long HIDE_DURATION_MS = 180;
     private static final int TIMEOUT_DURATION_MS = 4000;
 
-    private final BaseDraggingActivity mActivity;
+    private final ActivityContext mActivity;
     private Runnable mOnDismissed;
 
     public Snackbar(Context context, AttributeSet attrs) {
@@ -53,12 +54,19 @@
 
     public Snackbar(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mActivity = BaseDraggingActivity.fromContext(context);
+        mActivity = ActivityContext.lookupContext(context);
         inflate(context, R.layout.snackbar, this);
     }
 
-    public static void show(BaseDraggingActivity activity, int labelStringResId,
-            int actionStringResId, Runnable onDismissed, Runnable onActionClicked) {
+    /** Show a snackbar with just a label. */
+    public static <T extends Context & ActivityContext> void show(T activity, int labelStringRedId,
+            Runnable onDismissed) {
+        show(activity, labelStringRedId, NO_ID, onDismissed, null);
+    }
+
+    /** Show a snackbar with a label and action. */
+    public static <T extends Context & ActivityContext> void show(T activity, int labelStringResId,
+            int actionStringResId, Runnable onDismissed, @Nullable Runnable onActionClicked) {
         closeOpenViews(activity, true, TYPE_SNACKBAR);
         Snackbar snackbar = new Snackbar(activity, null);
         // Set some properties here since inflated xml only contains the children.
@@ -80,20 +88,42 @@
         int maxMarginLeftRight = res.getDimensionPixelSize(R.dimen.snackbar_max_margin_left_right);
         int minMarginLeftRight = res.getDimensionPixelSize(R.dimen.snackbar_min_margin_left_right);
         int marginBottom = res.getDimensionPixelSize(R.dimen.snackbar_margin_bottom);
+        int absoluteMaxWidth = res.getDimensionPixelSize(R.dimen.snackbar_max_width);
         Rect insets = activity.getDeviceProfile().getInsets();
-        int maxWidth = dragLayer.getWidth() - minMarginLeftRight * 2 - insets.left - insets.right;
-        int minWidth = dragLayer.getWidth() - maxMarginLeftRight * 2 - insets.left - insets.right;
+        int maxWidth = Math.min(
+                dragLayer.getWidth() - minMarginLeftRight * 2 - insets.left - insets.right,
+                absoluteMaxWidth);
+        int minWidth = Math.min(
+                dragLayer.getWidth() - maxMarginLeftRight * 2 - insets.left - insets.right,
+                absoluteMaxWidth);
         params.width = minWidth;
         params.setMargins(0, 0, 0, marginBottom + insets.bottom);
 
         TextView labelView = snackbar.findViewById(R.id.label);
-        TextView actionView = snackbar.findViewById(R.id.action);
         String labelText = res.getString(labelStringResId);
-        String actionText = res.getString(actionStringResId);
-        int totalContentWidth = (int) (labelView.getPaint().measureText(labelText)
-                + actionView.getPaint().measureText(actionText))
+        labelView.setText(labelText);
+
+        TextView actionView = snackbar.findViewById(R.id.action);
+        float actionWidth;
+        if (actionStringResId != NO_ID) {
+            String actionText = res.getString(actionStringResId);
+            actionWidth = actionView.getPaint().measureText(actionText)
+                    + actionView.getPaddingRight() + actionView.getPaddingLeft();
+            actionView.setText(actionText);
+            actionView.setOnClickListener(v -> {
+                if (onActionClicked != null) {
+                    onActionClicked.run();
+                }
+                snackbar.mOnDismissed = null;
+                snackbar.close(true);
+            });
+        } else {
+            actionWidth = 0;
+            actionView.setVisibility(GONE);
+        }
+
+        int totalContentWidth = (int) (labelView.getPaint().measureText(labelText) + actionWidth)
                 + labelView.getPaddingRight() + labelView.getPaddingLeft()
-                + actionView.getPaddingRight() + actionView.getPaddingLeft()
                 + padding * 2;
         if (totalContentWidth > params.width) {
             // The text doesn't fit in our standard width so update width to accommodate.
@@ -113,17 +143,8 @@
                 params.width = maxWidth;
             }
         }
-        labelView.setText(labelText);
-        actionView.setText(actionText);
-        actionView.setOnClickListener(v -> {
-            if (onActionClicked != null) {
-                onActionClicked.run();
-            }
-            snackbar.mOnDismissed = null;
-            snackbar.close(true);
-        });
-        snackbar.mOnDismissed = onDismissed;
 
+        snackbar.mOnDismissed = onDismissed;
         snackbar.setAlpha(0);
         snackbar.setScaleX(0.8f);
         snackbar.setScaleY(0.8f);
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..784f4f0 100644
--- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
+++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
@@ -16,95 +16,60 @@
 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;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
 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.ArrayMap;
 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.FastBitmapDrawable;
 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) {
+    private final UserHandle mMyUser = Process.myUserHandle();
+    private final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
+
+    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,268 +82,88 @@
      *
      * @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);
         }
     }
 
     /**
+     * Returns a drawable that can be used as a badge for the user or null.
+     */
+   // @UiThread
+    public Drawable getBadgeForUser(UserHandle user, int badgeSize) {
+        if (mMyUser.equals(user)) {
+            return null;
+        }
+
+        Bitmap badgeBitmap = getUserBadge(user, badgeSize);
+        FastBitmapDrawable d = new FastBitmapDrawable(badgeBitmap);
+        d.setFilterBitmap(true);
+        d.setBounds(0, 0, badgeBitmap.getWidth(), badgeBitmap.getHeight());
+        return d;
+    }
+
+    private Bitmap getUserBadge(UserHandle user, int badgeSize) {
+        synchronized (mUserBadges) {
+            Bitmap badgeBitmap = mUserBadges.get(user);
+            if (badgeBitmap != null) {
+                return badgeBitmap;
+            }
+
+            final Resources res = mContext.getResources();
+            badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
+
+            Drawable drawable = mContext.getPackageManager().getUserBadgedDrawableForDensity(
+                    new BitmapDrawable(res, badgeBitmap), user,
+                    new Rect(0, 0, badgeSize, badgeSize),
+                    0);
+            if (drawable instanceof BitmapDrawable) {
+                badgeBitmap = ((BitmapDrawable) drawable).getBitmap();
+            } else {
+                badgeBitmap.eraseColor(Color.TRANSPARENT);
+                Canvas c = new Canvas(badgeBitmap);
+                drawable.setBounds(0, 0, badgeSize, badgeSize);
+                drawable.draw(c);
+                c.setBitmap(null);
+            }
+
+            mUserBadges.put(user, badgeBitmap);
+            return badgeBitmap;
+        }
+    }
+
+
+    /**
      * 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 +194,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 +302,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 +337,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..57f8bc7 100644
--- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.widget;
 
+import android.annotation.SuppressLint;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -25,10 +26,14 @@
 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;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
 /**
  * A widget host views created while the host has not bind to the system service.
  */
@@ -55,6 +60,11 @@
     }
 
     @Override
+    public void addView(View child) {
+        // Not allowed
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
@@ -69,8 +79,22 @@
                 && mSetupTextLayout.getWidth() == availableWidth) {
             return;
         }
-        mSetupTextLayout = new StaticLayout(info.label, mPaint, availableWidth,
-                Layout.Alignment.ALIGN_CENTER, 1, 0, true);
+        try {
+            mSetupTextLayout = new StaticLayout(info.label, mPaint, availableWidth,
+                    Layout.Alignment.ALIGN_CENTER, 1, 0, true);
+        } catch (IllegalArgumentException e) {
+            @SuppressLint("DrawAllocation") StringWriter stringWriter = new StringWriter();
+            @SuppressLint("DrawAllocation") PrintWriter printWriter = new PrintWriter(stringWriter);
+            mActivity.getDeviceProfile().dump(/*prefix=*/"", printWriter);
+            printWriter.flush();
+            String message = "b/203530620 "
+                    + "- availableWidth: " + availableWidth
+                    + ", getMeasuredWidth: " + getMeasuredWidth()
+                    + ", getPaddingLeft: " + getPaddingLeft()
+                    + ", getPaddingRight: " + getPaddingRight()
+                    + ", deviceProfile: " + stringWriter.toString();
+            throw new IllegalArgumentException(message, e);
+        }
     }
 
     @Override
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..c92fe5a 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -16,40 +16,48 @@
 
 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;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 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.BaseIconFactory;
 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 +67,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,35 +80,58 @@
     /** 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;
+    private ImageView mWidgetBadge;
     private TextView mWidgetName;
     private TextView mWidgetDims;
     private TextView mWidgetDescription;
 
     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 +145,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 +155,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
@@ -139,6 +169,7 @@
 
         mWidgetImageContainer = findViewById(R.id.widget_preview_container);
         mWidgetImage = findViewById(R.id.widget_preview);
+        mWidgetBadge = findViewById(R.id.widget_badge);
         mWidgetName = findViewById(R.id.widget_name);
         mWidgetDims = findViewById(R.id.widget_dims);
         mWidgetDescription = findViewById(R.id.widget_description);
@@ -153,6 +184,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}
      */
@@ -163,11 +199,13 @@
         mWidgetImage.animate().cancel();
         mWidgetImage.setDrawable(null);
         mWidgetImage.setVisibility(View.VISIBLE);
+        mWidgetBadge.setImageDrawable(null);
+        mWidgetBadge.setVisibility(View.GONE);
         mWidgetName.setText(null);
         mWidgetDims.setText(null);
         mWidgetDescription.setText(null);
         mWidgetDescription.setVisibility(GONE);
-        mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
+        mTargetPreviewWidth = mTargetPreviewHeight = mPresetPreviewSize;
 
         if (mActiveRequest != null) {
             mActiveRequest.cancel();
@@ -178,6 +216,7 @@
             mWidgetImageContainer.removeView(mAppWidgetHostViewPreview);
         }
         mAppWidgetHostViewPreview = null;
+        mAppWidgetHostViewScale = 1f;
         mItem = null;
     }
 
@@ -185,7 +224,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 +275,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 +317,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 +329,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) {
@@ -319,6 +355,7 @@
                 mAppWidgetHostViewPreview = null;
             }
         }
+
         if (mAnimatePreview) {
             mWidgetImageContainer.setAlpha(0f);
             ViewPropertyAnimator anim = mWidgetImageContainer.animate();
@@ -326,55 +363,75 @@
         } else {
             mWidgetImageContainer.setAlpha(1f);
         }
+        if (mActiveRequest != null) {
+            mActiveRequest.cancel();
+            mActiveRequest = null;
+        }
+    }
+
+    /** Used to show the badge when the widget is in the recommended section
+     */
+    public void showBadge() {
+        Drawable badge = mWidgetPreviewLoader.getBadgeForUser(mItem.user,
+                BaseIconFactory.getBadgeSizeForIconSize(
+                        mActivity.getDeviceProfile().allAppsIconSizePx));
+        if (badge == null) {
+            mWidgetBadge.setVisibility(View.GONE);
+        } else {
+            mWidgetBadge.setVisibility(View.VISIBLE);
+            mWidgetBadge.setImageDrawable(badge);
+        }
     }
 
     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 +447,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 +457,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 +470,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..06cc65e 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,8 @@
 
             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);
+                widgetCell.showBadge();
             }
             addView(tableRow);
         }
@@ -129,7 +120,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..13a956b
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/OneSearch.java
@@ -0,0 +1,54 @@
+/*
+ * 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 = 5;
+
+    /**
+     * 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);
+
+    /** Get image bitmap with the URL. */
+    Parcelable getImageBitmap(String imageUrl);
+
+    /**
+     * Notifies search events to plugin
+     *
+     * @param event the SearchTargetEvent event created due to user action
+     */
+    void notifyEvent(Parcelable event);
+}
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..618ab41 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
@@ -109,7 +109,7 @@
         <activity
             android:name="com.android.launcher3.testcomponent.TestLauncherActivity"
             android:clearTaskOnLaunch="true"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
             android:enabled="false"
             android:label="Test launcher"
             android:launchMode="singleTask"
@@ -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,52 @@
                 <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>
+        <activity-alias android:name="Activity13" android:exported="true"
+            android:label="TestActivity13"
+            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>
+        <activity-alias android:name="Activity14" android:exported="true"
+            android:label="TestActivity14"
+            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 93%
rename from robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
rename to tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index 8aa6f37..8a4590a 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) {
@@ -88,7 +94,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));
 
@@ -127,7 +133,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 63%
rename from robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
rename to tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 4184d33..9c8de1c 100644
--- a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -15,24 +15,28 @@
  */
 package com.android.launcher3.provider;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
 import android.content.ContentValues;
 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
@@ -84,6 +88,56 @@
         assertEquals(1, getCount(db, "select * from favorites where profileId = 33"));
     }
 
+    @Test
+    public void testRemoveScreenIdGaps_firstScreenEmpty() {
+        runRemoveScreenIdGapsTest(
+                new int[]{1, 2, 5, 6, 6, 7, 9, 9},
+                new int[]{1, 2, 3, 4, 4, 5, 6, 6});
+    }
+
+    @Test
+    public void testRemoveScreenIdGaps_firstScreenOccupied() {
+        runRemoveScreenIdGapsTest(
+                new int[]{0, 2, 5, 6, 6, 7, 9, 9},
+                new int[]{0, 1, 2, 3, 3, 4, 5, 5});
+    }
+
+    @Test
+    public void testRemoveScreenIdGaps_noGap() {
+        runRemoveScreenIdGapsTest(
+                new int[]{0, 1, 1, 2, 3, 3, 4, 5},
+                new int[]{0, 1, 1, 2, 3, 3, 4, 5});
+    }
+
+    private void runRemoveScreenIdGapsTest(int[] screenIds, int[] expectedScreenIds) {
+        SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+        // Add some mock data
+        for (int i = 0; i < screenIds.length; i++) {
+            ContentValues values = new ContentValues();
+            values.put(Favorites._ID, i);
+            values.put(Favorites.SCREEN, screenIds[i]);
+            values.put(Favorites.CONTAINER, Favorites.CONTAINER_DESKTOP);
+            db.insert(Favorites.TABLE_NAME, null, values);
+        }
+        // Verify items are added
+        assertEquals(screenIds.length,
+                getCount(db, "select * from favorites where container = -100"));
+
+        new RestoreDbTask().removeScreenIdGaps(db);
+
+        // verify screenId gaps removed
+        int[] resultScreenIds = new int[screenIds.length];
+        try (Cursor c = db.rawQuery(
+                "select screen from favorites where container = -100 order by screen", null)) {
+            int i = 0;
+            while (c.moveToNext()) {
+                resultScreenIds[i++] = c.getInt(0);
+            }
+        }
+
+        assertArrayEquals(expectedScreenIds, resultScreenIds);
+    }
+
     private int getCount(SQLiteDatabase db, String sql) {
         try (Cursor c = db.rawQuery(sql, null)) {
             return c.getCount();
@@ -95,7 +149,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/touch/SingleAxisSwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
index 472e1a1..260f556 100644
--- a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -58,6 +59,7 @@
     private TouchEventGenerator mGenerator;
     private SingleAxisSwipeDetector mDetector;
     private int mTouchSlop;
+    Context mContext;
 
     @Mock
     private SingleAxisSwipeDetector.Listener mMockListener;
@@ -69,12 +71,13 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mGenerator = new TouchEventGenerator((ev) -> mDetector.onTouchEvent(ev));
-        ViewConfiguration orgConfig = ViewConfiguration
-                .get(InstrumentationRegistry.getTargetContext());
+        mContext = InstrumentationRegistry.getTargetContext();
+        ViewConfiguration orgConfig = ViewConfiguration.get(mContext);
         doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
                 .getScaledMaximumFlingVelocity();
 
-        mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+        mDetector = new SingleAxisSwipeDetector(mContext,
+                mMockConfig, mMockListener, VERTICAL, false);
         mDetector.setDetectableScrollConditions(DIRECTION_BOTH, false);
         mTouchSlop = orgConfig.getScaledTouchSlop();
         doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
@@ -84,7 +87,8 @@
 
     @Test
     public void testDragStart_verticalPositive() {
-        mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+        mDetector = new SingleAxisSwipeDetector(mContext,
+                mMockConfig, mMockListener, VERTICAL, false);
         mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
         mGenerator.put(0, 100, 100);
         mGenerator.move(0, 100, 100 - mTouchSlop);
@@ -94,7 +98,8 @@
 
     @Test
     public void testDragStart_verticalNegative() {
-        mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
+        mDetector = new SingleAxisSwipeDetector(mContext,
+                mMockConfig, mMockListener, VERTICAL, false);
         mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
         mGenerator.put(0, 100, 100);
         mGenerator.move(0, 100, 100 + mTouchSlop);
@@ -112,7 +117,8 @@
 
     @Test
     public void testDragStart_horizontalPositive() {
-        mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
+        mDetector = new SingleAxisSwipeDetector(mContext,
+                mMockConfig, mMockListener, HORIZONTAL, false);
         mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
 
         mGenerator.put(0, 100, 100);
@@ -123,7 +129,8 @@
 
     @Test
     public void testDragStart_horizontalNegative() {
-        mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
+        mDetector = new SingleAxisSwipeDetector(mContext,
+                mMockConfig, mMockListener, HORIZONTAL, false);
         mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
 
         mGenerator.put(0, 100, 100);
@@ -134,7 +141,8 @@
 
     @Test
     public void testDragStart_horizontalRtlPositive() {
-        mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
+        mDetector = new SingleAxisSwipeDetector(mContext,
+                mMockConfig, mMockListener, HORIZONTAL, true);
         mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
 
         mGenerator.put(0, 100, 100);
@@ -145,7 +153,8 @@
 
     @Test
     public void testDragStart_horizontalRtlNegative() {
-        mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
+        mDetector = new SingleAxisSwipeDetector(mContext,
+                mMockConfig, mMockListener, HORIZONTAL, true);
         mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
 
         mGenerator.put(0, 100, 100);
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index dcb6dc1..075505e 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -36,9 +36,9 @@
 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.system.OsConstants;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -52,7 +52,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 +62,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;
@@ -84,6 +84,7 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -99,11 +100,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 +111,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",
@@ -170,14 +149,13 @@
                 }
                 sDumpWasGenerated = true;
                 Log.d("b/195319692", "sDumpWasGenerated := true", new Exception());
-                result = "memory dump filename: " + fileName;
+                result = "saved memory dump as an artifact";
             } catch (Throwable e) {
                 Log.e(TAG, "dumpHprofData failed", e);
                 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() {
@@ -235,7 +213,8 @@
     }
 
     protected TestRule getRulesInsideActivityMonitor() {
-        final RuleChain inner = RuleChain.outerRule(new PortraitLandscapeRunner(this))
+        final RuleChain inner = RuleChain
+                .outerRule(new PortraitLandscapeRunner(this))
                 .around(new FailureWatcher(mDevice, mLauncher));
 
         return TestHelpers.isInLauncherProcess()
@@ -254,26 +233,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 +265,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());
                 }
             }
@@ -366,7 +329,12 @@
      */
     protected <T> T getOnUiThread(final Callable<T> callback) {
         try {
-            return mMainThreadExecutor.submit(callback).get();
+            return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT,
+                    TimeUnit.MILLISECONDS);
+        } catch (TimeoutException e) {
+            Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
+            Process.sendSignal(Process.myPid(), OsConstants.SIGABRT);
+            throw new RuntimeException(e);
         } catch (Throwable e) {
             throw new RuntimeException(e);
         }
@@ -385,13 +353,20 @@
     }
 
     // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
-    // expecting
-    // the results of that gesture because the wait can hide flakeness.
+    // expecting the results of that gesture because the wait can hide flakeness.
     protected void waitForState(String message, Supplier<LauncherState> state) {
         waitForLauncherCondition(message,
                 launcher -> launcher.getStateManager().getCurrentStableState() == state.get());
     }
 
+    // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
+    // expecting the results of that gesture because the wait can hide flakeness.
+    protected void waitForStateTransitionToEnd(String message, Supplier<LauncherState> state) {
+        waitForLauncherCondition(message,
+                launcher -> launcher.getStateManager().isInStableState(state.get())
+                        && !launcher.getStateManager().isInTransition());
+    }
+
     protected void waitForResumed(String message) {
         waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed());
     }
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..5b940a8 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -34,10 +34,10 @@
 import com.android.launcher3.tapl.AppIcon;
 import com.android.launcher3.tapl.AppIconMenu;
 import com.android.launcher3.tapl.AppIconMenuItem;
+import com.android.launcher3.tapl.Folder;
+import com.android.launcher3.tapl.FolderIcon;
 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 +80,8 @@
         assertTrue(message, failed);
     }
 
-    private boolean isWorkspaceScrollable(Launcher launcher) {
-        return launcher.getWorkspace().getPageCount() > 1;
+    public static boolean isWorkspaceScrollable(Launcher launcher) {
+        return launcher.getWorkspace().getPageCount() > launcher.getWorkspace().getPanelCount();
     }
 
     private int getCurrentWorkspacePage(Launcher launcher) {
@@ -93,25 +93,16 @@
     }
 
     @Test
-    @ScreenRecord //b/187080582
     public void testDevicePressMenu() throws Exception {
         mDevice.pressMenu();
         mDevice.waitForIdle();
         executeOnLauncher(
-                launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
-                        OptionsPopupView.getOptionsPopup(launcher) != null));
+                launcher -> assertNotNull("Launcher internal state didn't switch to Showing Menu",
+                        launcher.getOptionsPopup()));
         // Check that pressHome works when the menu is shown.
         mLauncher.pressHome();
     }
 
-    @Ignore
-    public void testOpenHomeSettingsFromWorkspace() {
-        mDevice.pressMenu();
-        mDevice.waitForIdle();
-        mLauncher.getOptionsPopupMenu().getMenuItem("Home settings")
-                .launch(mDevice.getLauncherPackageName());
-    }
-
     @Test
     public void testPressHomeOnAllAppsContextMenu() throws Exception {
         final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
@@ -195,8 +186,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",
+                        workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
         executeOnLauncher(
                 launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
                         isWorkspaceScrollable(launcher)));
@@ -212,8 +204,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",
+                        workspace.pagesPerScreen(), getCurrentWorkspacePage(launcher)));
         assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
 
         // Test starting a workspace app.
@@ -281,7 +273,7 @@
     }
 
     private boolean isOptionsPopupVisible(Launcher launcher) {
-        final ArrowPopup popup = OptionsPopupView.getOptionsPopup(launcher);
+        final ArrowPopup<?> popup = launcher.getOptionsPopup();
         return popup != null && popup.isShown();
     }
 
@@ -366,6 +358,83 @@
         }
     }
 
+    private AppIcon createShortcutIfNotExist(String name) {
+        AppIcon appIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name);
+        if (appIcon == null) {
+            AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+            allApps.freeze();
+            try {
+                appIcon = allApps.getAppIcon(name);
+                appIcon.dragToWorkspace(false, false);
+            } finally {
+                allApps.unfreeze();
+            }
+            appIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name);
+        }
+        return appIcon;
+    }
+
+    @Ignore("b/205014516")
+    @Test
+    @PortraitLandscape
+    public void testDragToFolder() throws Exception {
+        final AppIcon playStoreIcon = createShortcutIfNotExist("Play Store");
+        final AppIcon gmailIcon = createShortcutIfNotExist("Gmail");
+
+        FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon);
+
+        Folder folder = folderIcon.open();
+        folder.getAppIcon("Play Store");
+        folder.getAppIcon("Gmail");
+        Workspace workspace = folder.close();
+
+        assertNull("Gmail should be moved to a folder.",
+                workspace.tryGetWorkspaceAppIcon("Gmail"));
+        assertNull("Play Store should be moved to a folder.",
+                workspace.tryGetWorkspaceAppIcon("Play Store"));
+
+        final AppIcon youTubeIcon = createShortcutIfNotExist("YouTube");
+
+        folderIcon = youTubeIcon.dragToIcon(folderIcon);
+        folder = folderIcon.open();
+        folder.getAppIcon("YouTube");
+        folder.close();
+    }
+
+    @Ignore("b/205027405")
+    @Test
+    @PortraitLandscape
+    public void testPressBack() throws Exception {
+        mLauncher.getWorkspace().switchToAllApps();
+        mLauncher.pressBack();
+        mLauncher.getWorkspace();
+        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+
+        AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+        try {
+            allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
+        } finally {
+            allApps.unfreeze();
+        }
+        mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
+        mLauncher.pressBack();
+        mLauncher.getWorkspace();
+        waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testDeleteFromWorkspace() throws Exception {
+        // test delete both built-in apps and user-installed app from workspace
+        for (String appName : new String[] {"Gmail", "Play Store", APP_NAME}) {
+            final AppIcon appIcon = createShortcutIfNotExist(appName);
+            Workspace workspace = mLauncher.getWorkspace().deleteAppIcon(appIcon);
+            assertNull(appName + " app was found after being deleted from workspace",
+                    workspace.tryGetWorkspaceAppIcon(appName));
+        }
+    }
+
     public static String getAppPackageName() {
         return getInstrumentation().getContext().getPackageName();
     }
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..939cfe1
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.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.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.util.Log;
+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");
+        Log.d("b/203817455", "pm create-user; output: " + output);
+        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);
+
+        mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+        waitForStateTransitionToEnd("Launcher internal state didn't switch to Normal",
+                () -> NORMAL);
+        waitForResumed("Launcher internal state is still Background");
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+        waitForStateTransitionToEnd("Launcher internal state didn't switch to All Apps",
+                () -> ALL_APPS);
+    }
+
+    @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() {
+        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() {
+        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() {
+        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/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
new file mode 100644
index 0000000..b048cd4
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.workspace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Tests for two panel workspace.
+ *
+ * Note running these tests will clear the workspace on the device.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest {
+
+    Workspace mWorkspace;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        TaplTestsLauncher3.initialize(this);
+        mWorkspace = mLauncher.getWorkspace();
+    }
+
+    @Test
+    public void testDragIconToRightPanel() {
+        if (!mLauncher.isTwoPanels()) {
+            return;
+        }
+
+        // Pre verifying the screens
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPageEmpty(launcher, 1);
+        });
+
+        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Chrome"), 1);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Maps", "Play Store");
+            assertItemsOnPage(launcher, 1, "Chrome");
+        });
+    }
+
+    @Test
+    public void testDragIconToPage2() {
+        if (!mLauncher.isTwoPanels()) {
+            return;
+        }
+
+        // Pre verifying the screens
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPageEmpty(launcher, 1);
+        });
+
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 2, "Maps");
+            assertPageEmpty(launcher, 3);
+        });
+    }
+
+    @Test
+    public void testDragIconToPage3() {
+        if (!mLauncher.isTwoPanels()) {
+            return;
+        }
+
+        // Pre verifying the screens
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPageEmpty(launcher, 1);
+        });
+
+        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Phone"), 3);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3);
+            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPageEmpty(launcher, 1);
+            assertPageEmpty(launcher, 2);
+            assertItemsOnPage(launcher, 3, "Phone");
+        });
+    }
+
+
+    @Test
+    public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
+        if (!mLauncher.isTwoPanels()) {
+            return;
+        }
+
+        // Pre verifying the screens
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPageEmpty(launcher, 1);
+        });
+
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 3);
+        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Chrome"), 0);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 2, "Chrome");
+            assertItemsOnPage(launcher, 3, "Maps");
+        });
+
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), -1);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertItemsOnPage(launcher, 1, "Maps");
+            assertItemsOnPage(launcher, 2, "Chrome");
+            assertPageEmpty(launcher, 3);
+        });
+
+        // Move Chrome to the right panel as well, to make sure pages are not deleted whichever
+        // page is the empty one
+        mWorkspace.flingForward();
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Chrome"), 1);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertItemsOnPage(launcher, 1, "Maps");
+            assertPageEmpty(launcher, 2);
+            assertItemsOnPage(launcher, 3, "Chrome");
+        });
+    }
+
+
+    @Test
+    public void testEmptyPagesGetRemovedIfBothPagesAreEmpty() {
+        if (!mLauncher.isTwoPanels()) {
+            return;
+        }
+
+        // Pre verifying the screens
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPageEmpty(launcher, 1);
+        });
+
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Play Store"), 2);
+        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Camera"), 1);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3);
+            assertItemsOnPage(launcher, 0, "Maps");
+            assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 2, "Play Store");
+            assertItemsOnPage(launcher, 3, "Camera");
+        });
+
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Camera"), -1);
+        mWorkspace.flingForward();
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Play Store"), -2);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertItemsOnPage(launcher, 1, "Camera");
+        });
+    }
+
+    @Test
+    public void testMiddleEmptyPagesGetRemoved() {
+        if (!mLauncher.isTwoPanels()) {
+            return;
+        }
+
+        // Pre verifying the screens
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1);
+            assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+            assertPageEmpty(launcher, 1);
+        });
+
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
+        mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Messages"), 3);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 2, 3, 4, 5);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 2, "Maps");
+            assertPageEmpty(launcher, 3);
+            assertPageEmpty(launcher, 4);
+            assertItemsOnPage(launcher, 5, "Messages");
+        });
+
+        mWorkspace.flingBackward();
+        mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
+
+        executeOnLauncher(launcher -> {
+            assertPagesExist(launcher, 0, 1, 4, 5);
+            assertItemsOnPage(launcher, 0, "Play Store");
+            assertPageEmpty(launcher, 1);
+            assertItemsOnPage(launcher, 4, "Maps");
+            assertItemsOnPage(launcher, 5, "Messages");
+        });
+    }
+
+    private void assertPageEmpty(Launcher launcher, int pageId) {
+        CellLayout page = launcher.getWorkspace().getScreenWithId(pageId);
+        assertNotNull("Page " + pageId + " does NOT exist.", page);
+        assertEquals("Page " + pageId + " is NOT empty. Number of items on the page:", 0,
+                page.getShortcutsAndWidgets().getChildCount());
+    }
+
+    private void assertPagesExist(Launcher launcher, int... pageIds) {
+        int pageCount = launcher.getWorkspace().getPageCount();
+        assertEquals("Existing page count does NOT match.", pageIds.length, pageCount);
+        for (int i = 0; i < pageCount; i++) {
+            CellLayout page = (CellLayout) launcher.getWorkspace().getPageAt(i);
+            int pageId = launcher.getWorkspace().getIdForScreen(page);
+            assertEquals("The page's id at index " + i + " does NOT match.", pageId,
+                    pageIds[i]);
+        }
+    }
+
+    private void assertItemsOnPage(Launcher launcher, int pageId, String... itemTitles) {
+        Set<String> itemTitleSet = Arrays.stream(itemTitles).collect(Collectors.toSet());
+        CellLayout page = launcher.getWorkspace().getScreenWithId(pageId);
+        int itemCount = page.getShortcutsAndWidgets().getChildCount();
+        for (int i = 0; i < itemCount; i++) {
+            ItemInfo itemInfo = (ItemInfo) page.getShortcutsAndWidgets().getChildAt(i).getTag();
+            if (itemInfo != null) {
+                assertTrue("There was an extra item on page " + pageId + ": " + itemInfo.title,
+                        itemTitleSet.remove(itemInfo.title));
+            }
+        }
+        assertTrue("Could NOT find some of the items on page " + pageId + ": "
+                        + itemTitleSet.stream().collect(Collectors.joining(",")),
+                itemTitleSet.isEmpty());
+    }
+}
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..f33a50a 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]*|[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/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 21099b4..6da59da 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -16,8 +16,11 @@
 
 package com.android.launcher3.tapl;
 
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiObject2;
@@ -29,7 +32,7 @@
 /**
  * App icon, whether in all apps or in workspace/
  */
-public final class AppIcon extends Launchable {
+public final class AppIcon extends Launchable implements FolderDragTarget {
 
     private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick");
 
@@ -61,6 +64,29 @@
         }
     }
 
+    /**
+     * Drag the AppIcon to the given position of other icon. The drag must result in a folder.
+     *
+     * @param target the destination icon.
+     */
+    @NonNull
+    public FolderIcon dragToIcon(FolderDragTarget target) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to drag icon")) {
+            final Rect dropBounds = target.getDropLocationBounds();
+            Workspace.dragIconToWorkspace(
+                    mLauncher, this,
+                    () -> {
+                        final Rect bounds = target.getDropLocationBounds();
+                        return new Point(bounds.centerX(), bounds.centerY());
+                    },
+                    getLongPressIndicator());
+            FolderIcon result = target.getTargetFolder(dropBounds);
+            mLauncher.assertTrue("Can't find the target folder.", result != null);
+            return result;
+        }
+    }
+
     @Override
     protected void addExpectedEventsForLongClick() {
         mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
@@ -80,4 +106,20 @@
     protected String launchableType() {
         return "app icon";
     }
+
+    @Override
+    public Rect getDropLocationBounds() {
+        return mLauncher.getVisibleBounds(mObject);
+    }
+
+    @Override
+    public FolderIcon getTargetFolder(Rect bounds) {
+        for (FolderIcon folderIcon : mLauncher.getWorkspace().getFolderIcons()) {
+            final Rect folderIconBounds = folderIcon.getDropLocationBounds();
+            if (bounds.contains(folderIconBounds.centerX(), folderIconBounds.centerY())) {
+                return folderIcon;
+            }
+        }
+        return null;
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 4b1610e..4eaecca 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -30,6 +30,7 @@
 
 import com.android.launcher3.testing.TestProtocol;
 
+import java.util.List;
 import java.util.regex.Pattern;
 
 /**
@@ -62,49 +63,74 @@
                      "want to switch from background to overview")) {
             verifyActiveContainer();
             goToOverviewUnchecked();
-            return mLauncher.isFallbackOverview() ?
-                    new BaseOverview(mLauncher) : new Overview(mLauncher);
+            return mLauncher.isFallbackOverview()
+                    ? new BaseOverview(mLauncher) : new Overview(mLauncher);
         }
     }
 
+
     protected boolean zeroButtonToOverviewGestureStartsInLauncher() {
+        return mLauncher.isTablet();
+    }
+
+    protected boolean zeroButtonToOverviewGestureStateTransitionWhileHolding() {
         return false;
     }
 
     protected void goToOverviewUnchecked() {
         switch (mLauncher.getNavigationModel()) {
             case ZERO_BUTTON: {
-                final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
-                final int startY = getSwipeStartY();
-                final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
-                        getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
-                final Point start = new Point(centerX, startY);
-                final Point end =
-                        new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
+                sendDownPointerToEnterOverviewToLauncher();
+                String swipeAndHoldToEnterOverviewActionName =
+                        "swiping and holding to enter overview";
+                // If swiping from an app (e.g. Overview is in Background), we pause and hold on
+                // swipe up to make overview appear, or else swiping without holding would take
+                // us to the Home state. If swiping up from Home (e.g. Overview in Home or
+                // Workspace state where the below condition is true), there is no need to pause,
+                // and we will not test for an intermediate carousel as one will not exist.
+                if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) {
+                    mLauncher.runToState(this::sendSwipeUpAndHoldToEnterOverviewGestureToLauncher,
+                            OVERVIEW_STATE_ORDINAL, swipeAndHoldToEnterOverviewActionName);
+                    sendUpPointerToEnterOverviewToLauncher();
+                } else {
+                    // If swiping up from an app to overview, pause on intermediate carousel
+                    // until snapshots are visible. No intermediate carousel when swiping from
+                    // Home. The task swiped up is not a snapshot but the TaskViewSimulator. If
+                    // only a single task exists, no snapshots will be available during swipe up.
+                    mLauncher.executeAndWaitForLauncherEvent(
+                            this::sendSwipeUpAndHoldToEnterOverviewGestureToLauncher,
+                            event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(
+                                    event.getClassName().toString()),
+                            () -> "Pause wasn't detected",
+                            swipeAndHoldToEnterOverviewActionName);
+                    try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                            "paused on swipe up to overview")) {
+                        if (mLauncher.getRecentTasks().size() > 1) {
+                            // When swiping up to grid-overview for tablets, the swiped tab will be
+                            // in the middle of the screen (TaskViewSimulator, not a snapshot), and
+                            // all remaining snapshots will be to the left of that task. In
+                            // non-tablet overview, snapshots can be on either side of the swiped
+                            // task, but we still check that they become visible after swiping and
+                            // pausing.
+                            mLauncher.waitForOverviewObject("snapshot");
+                            if (mLauncher.isTablet()) {
+                                List<UiObject2> tasks = mLauncher.getDevice().findObjects(
+                                        mLauncher.getOverviewObjectSelector("snapshot"));
+                                final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+                                mLauncher.assertTrue(
+                                        "All tasks not to the left of the swiped task",
+                                        tasks.stream()
+                                                .allMatch(
+                                                        t -> t.getVisibleBounds().right < centerX));
+                            }
 
-                final long downTime = SystemClock.uptimeMillis();
-                final LauncherInstrumentation.GestureScope gestureScope =
-                        zeroButtonToOverviewGestureStartsInLauncher()
-                                ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
-                                : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
-
-                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");
+                        }
+                        String upPointerToEnterOverviewActionName =
+                                "sending UP pointer to enter overview";
+                        mLauncher.runToState(this::sendUpPointerToEnterOverviewToLauncher,
+                                OVERVIEW_STATE_ORDINAL, upPointerToEnterOverviewActionName);
+                    }
+                }
                 break;
             }
 
@@ -134,9 +160,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;
         }
@@ -146,6 +178,60 @@
     private void expectSwitchToOverviewEvents() {
     }
 
+    private void sendDownPointerToEnterOverviewToLauncher() {
+        final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+        final int startY = getSwipeStartY();
+        final Point start = new Point(centerX, startY);
+        final long downTime = SystemClock.uptimeMillis();
+        final LauncherInstrumentation.GestureScope gestureScope =
+                zeroButtonToOverviewGestureStartsInLauncher()
+                        ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+                        : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+
+        mLauncher.sendPointer(
+                downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
+    }
+
+    private void sendSwipeUpAndHoldToEnterOverviewGestureToLauncher() {
+        final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+        final int startY = getSwipeStartY();
+        final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).getInt(
+                TestProtocol.TEST_INFO_RESPONSE_FIELD);
+        final Point start = new Point(centerX, startY);
+        final Point end =
+                new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
+        final long downTime = SystemClock.uptimeMillis();
+        final LauncherInstrumentation.GestureScope gestureScope =
+                zeroButtonToOverviewGestureStartsInLauncher()
+                        ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+                        : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+
+        mLauncher.movePointer(
+                downTime,
+                downTime,
+                ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
+                start,
+                end,
+                gestureScope);
+    }
+
+    private void sendUpPointerToEnterOverviewToLauncher() {
+        final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+        final int startY = getSwipeStartY();
+        final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).getInt(
+                TestProtocol.TEST_INFO_RESPONSE_FIELD);
+        final Point end =
+                new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
+        final long downTime = SystemClock.uptimeMillis();
+        final LauncherInstrumentation.GestureScope gestureScope =
+                zeroButtonToOverviewGestureStartsInLauncher()
+                        ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+                        : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+
+        mLauncher.sendPointer(downTime, SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_UP, end, gestureScope);
+    }
+
     @NonNull
     public Background quickSwitchToPreviousApp() {
         boolean toRight = true;
@@ -224,11 +310,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..3eb8cf1 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
@@ -56,10 +58,15 @@
                      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, 0, 0), 20,
+                    false);
+            try (LauncherInstrumentation.Closable c2 =
+                         mLauncher.addContextLayer("flung forwards")) {
+                verifyActiveContainer();
+                verifyActionsViewVisibility();
+            }
         }
     }
 
@@ -80,6 +87,8 @@
 
             mLauncher.clickLauncherObject(
                     mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector));
+
+            mLauncher.waitUntilLauncherObjectGone(clearAllSelector);
         }
     }
 
@@ -92,10 +101,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);
+            mLauncher.scrollLeftByDistance(verifyActiveContainer(), task.getVisibleWidth());
+
+            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 +167,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 +191,10 @@
         }
     }
 
+    int getTaskCount() {
+        return getTasks().size();
+    }
+
     /**
      * Returns whether Overview has tasks.
      */
@@ -146,8 +212,64 @@
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to get overview actions")) {
             verifyActiveContainer();
-            UiObject2 overviewActions = mLauncher.waitForLauncherObject("action_buttons");
+            UiObject2 overviewActions = mLauncher.waitForOverviewObject("action_buttons");
             return new OverviewActions(overviewActions, mLauncher);
         }
     }
+
+    /**
+     * Returns if clear all button is visible.
+     */
+    public boolean isClearAllVisible() {
+        return mLauncher.hasLauncherObject(mLauncher.getOverviewObjectSelector("clear_all"));
+    }
+
+    private void verifyActionsViewVisibility() {
+        if (!hasTasks()) {
+            return;
+        }
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to assert overview actions view visibility")) {
+            if (mLauncher.isTablet() && !isOverviewSnappedToFocusedTaskForTablet()) {
+                mLauncher.waitUntilOverviewObjectGone("action_buttons");
+            } else {
+                mLauncher.waitForOverviewObject("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/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java
new file mode 100644
index 0000000..dba308d
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Folder.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+public class Folder {
+
+    protected static final String FOLDER_CONTENT_RES_ID = "folder_content";
+
+    private final UiObject2 mContainer;
+    private final LauncherInstrumentation mLauncher;
+
+    Folder(LauncherInstrumentation launcher) {
+        this.mLauncher = launcher;
+        this.mContainer = launcher.waitForLauncherObject(FOLDER_CONTENT_RES_ID);
+    }
+
+    /**
+     * Find an app icon with given name or raise assertion error.
+     */
+    @NonNull
+    public AppIcon getAppIcon(String appName) {
+        try (LauncherInstrumentation.Closable ignored = mLauncher.addContextLayer(
+                "Want to get app icon in folder")) {
+            return new AppIcon(mLauncher,
+                    mLauncher.waitForObjectInContainer(
+                            mContainer,
+                            AppIcon.getAppIconSelector(appName, mLauncher)));
+        }
+    }
+
+    private void touchOutsideFolder() {
+        Rect containerBounds = mLauncher.getVisibleBounds(this.mContainer);
+        final long downTime = SystemClock.uptimeMillis();
+        Point containerLeftTopCorner = new Point(containerBounds.left - 1, containerBounds.top - 1);
+        mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
+                containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE);
+        mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP,
+                containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE);
+    }
+
+    /**
+     * CLose opened folder if possible. It throws assertion error if the folder is already closed.
+     */
+    public Workspace close() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "Want to close opened folder")) {
+            mLauncher.waitForLauncherObject(FOLDER_CONTENT_RES_ID);
+            touchOutsideFolder();
+            mLauncher.waitUntilLauncherObjectGone(FOLDER_CONTENT_RES_ID);
+            return mLauncher.getWorkspace();
+        }
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java b/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java
new file mode 100644
index 0000000..d797418
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+import android.graphics.Rect;
+
+public interface FolderDragTarget {
+    Rect getDropLocationBounds();
+
+    FolderIcon getTargetFolder(Rect bounds);
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/FolderIcon.java b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java
new file mode 100644
index 0000000..2e79d70
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.TestProtocol;
+
+/**
+ * Folder Icon, an app folder in workspace.
+ */
+public class FolderIcon implements FolderDragTarget {
+
+    protected final UiObject2 mObject;
+    protected final LauncherInstrumentation mLauncher;
+
+    FolderIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+        mObject = icon;
+        mLauncher = launcher;
+    }
+
+    /**
+     * Open and return a folder or raise assertion error.
+     */
+    @NonNull
+    public Folder open() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer("open folder")) {
+            mLauncher.executeAndWaitForLauncherEvent(() -> mLauncher.clickLauncherObject(mObject),
+                    event -> TestProtocol.FOLDER_OPENED_MESSAGE.equals(
+                            event.getClassName().toString()),
+                    () -> "Fail to open folder.",
+                    "open folder");
+        }
+        return new Folder(mLauncher);
+    }
+
+    @Override
+    public Rect getDropLocationBounds() {
+        return mLauncher.getVisibleBounds(mObject.getParent());
+    }
+
+    @Override
+    public FolderIcon getTargetFolder(Rect bounds) {
+        return this;
+    }
+}
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..631e8f1 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -21,6 +21,7 @@
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 
+import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID;
 import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
 
@@ -75,11 +76,14 @@
 import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -96,15 +100,20 @@
     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");
+
+    static final Pattern EVENT_KEY_BACK_DOWN = getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
+    static final Pattern EVENT_KEY_BACK_UP = getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
+
     private final String mLauncherPackage;
     private Boolean mIsLauncher3;
     private long mTestStartTime = -1;
@@ -120,11 +129,10 @@
     // Where the gesture happens: outside of Launcher, inside or from inside to outside and
     // whether the gesture recognition triggers pilfer.
     public enum GestureScope {
-        OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE
+        OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE,
+        INSIDE_TO_OUTSIDE_WITHOUT_PILFER,
     }
 
-    ;
-
     // Base class for launcher containers.
     static abstract class VisibleContainer {
         protected final LauncherInstrumentation mLauncher;
@@ -192,6 +200,10 @@
         return getTouchEventPattern("TouchInteractionService.onInputEvent", action);
     }
 
+    private static Pattern getKeyEventPattern(String action, String keyCode) {
+        return Pattern.compile("Key event: KeyEvent.*action=" + action + ".*keyCode=" + keyCode);
+    }
+
     /**
      * Constructs the root of TAPL hierarchy. You get all other objects from it.
      */
@@ -207,6 +219,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 +259,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 +297,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 +313,52 @@
     }
 
     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 void setEnableRotation(boolean on) {
+        getTestInfo(TestProtocol.REQUEST_ENABLE_ROTATION, Boolean.toString(on));
+    }
+
+    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 +421,8 @@
         }
     }
 
-    private String getSystemAnomalyMessage() {
+    private String getSystemAnomalyMessage(
+            boolean ignoreNavmodeChangeStates, boolean ignoreOnlySystemUiViews) {
         try {
             {
                 final StringBuilder sb = new StringBuilder();
@@ -368,8 +445,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 +469,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 +485,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) {
@@ -447,11 +544,11 @@
                     : TestHelpers.getSystemHealthMessage(getContext(), mTestStartTime);
 
             if (systemHealth != null) {
-                return message
-                        + ";\nPerhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+                message += ";\nPerhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
                         + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
             }
         }
+        Log.d(TAG, "About to throw the error: " + message, new Exception());
         return message;
     }
 
@@ -499,9 +596,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 +650,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();
 
@@ -683,12 +783,54 @@
     }
 
     /**
+     * Get the resource ID of visible floating view.
+     */
+    private Optional<String> getFloatingResId() {
+        if (hasLauncherObject(CONTEXT_MENU_RES_ID)) {
+            return Optional.of(CONTEXT_MENU_RES_ID);
+        }
+        if (hasLauncherObject(FOLDER_CONTENT_RES_ID)) {
+            return Optional.of(FOLDER_CONTENT_RES_ID);
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Using swiping up gesture to dismiss closable floating views, such as Menu or Folder Content.
+     */
+    private void swipeUpToCloseFloatingView(boolean gestureStartFromLauncher) {
+        final Point displaySize = getRealDisplaySize();
+
+        final Optional<String> floatingRes = getFloatingResId();
+
+        if (!floatingRes.isPresent()) {
+            return;
+        }
+
+        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);
+
+        try (LauncherInstrumentation.Closable c1 = addContextLayer(
+                String.format("Swiped up from floating view %s to home", floatingRes.get()))) {
+            waitUntilLauncherObjectGone(floatingRes.get());
+            waitForLauncherObject(getAnyObjectSelector());
+        }
+    }
+
+    /**
      * Presses nav bar home button.
      *
      * @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,27 +838,18 @@
             // 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)) {
-                    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(
-                            "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());
-                    }
-                }
+                // CLose floating views before going back to home.
+                swipeUpToCloseFloatingView(gestureStartFromLauncher);
+
                 if (hasLauncherObject(WORKSPACE_RES_ID)) {
                     log(action = "already at home");
                 } else {
@@ -728,8 +861,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,22 +872,58 @@
                     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();
             }
         }
     }
 
+    /**
+     * Press navbar back button or swipe back if in gesture navigation mode.
+     */
+    public void pressBack() {
+        try (Closable e = eventsCheck(); Closable c = addContextLayer("want to press back")) {
+            waitForLauncherInitialized();
+            final boolean launcherVisible =
+                    isTablet() ? isLauncherContainerVisible() : isLauncherVisible();
+            if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
+                final Point displaySize = getRealDisplaySize();
+                final GestureScope gestureScope =
+                        launcherVisible ? GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER
+                                : GestureScope.OUTSIDE_WITHOUT_PILFER;
+                linearGesture(0, displaySize.y / 2, displaySize.x / 2, displaySize.y / 2,
+                        10, false, gestureScope);
+            } else {
+                waitForNavigationUiObject("back").click();
+                if (isTablet()) {
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
+                } else if (!isLauncher3() && getNavigationModel() == NavigationModel.TWO_BUTTON) {
+                    expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+                    expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+                }
+            }
+            if (launcherVisible) {
+                expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
+                expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
+            }
+        }
+    }
+
     private static BySelector getAnyObjectSelector() {
         return By.textStartsWith("");
     }
@@ -765,6 +933,11 @@
         return hasLauncherObject(getAnyObjectSelector());
     }
 
+    boolean isLauncherContainerVisible() {
+        final String[] containerResources = {WORKSPACE_RES_ID, OVERVIEW_RES_ID, APPS_RES_ID};
+        return Arrays.stream(containerResources).anyMatch(r -> hasLauncherObject(r));
+    }
+
     /**
      * Gets the Workspace object if the current state is "active home", i.e. workspace. Fails if the
      * launcher is not in that state.
@@ -852,24 +1025,14 @@
         }
     }
 
-    /**
-     * Gets the Options Popup Menu object if the current state is showing the popup menu. Fails if
-     * the launcher is not in that state.
-     *
-     * @return Options Popup Menu object.
-     */
-    @NonNull
-    public OptionsPopupMenu getOptionsPopupMenu() {
-        try (LauncherInstrumentation.Closable c = addContextLayer(
-                "want to get context menu object")) {
-            return new OptionsPopupMenu(this);
-        }
-    }
-
     void waitUntilLauncherObjectGone(String resId) {
         waitUntilGoneBySelector(getLauncherObjectSelector(resId));
     }
 
+    void waitUntilOverviewObjectGone(String resId) {
+        waitUntilGoneBySelector(getOverviewObjectSelector(resId));
+    }
+
     void waitUntilLauncherObjectGone(BySelector selector) {
         waitUntilGoneBySelector(makeLauncherSelector(selector));
     }
@@ -892,6 +1055,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 +1102,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 +1124,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));
     }
@@ -965,6 +1146,11 @@
     }
 
     @NonNull
+    UiObject2 waitForOverviewObject(String resName) {
+        return waitForObjectBySelector(getOverviewObjectSelector(resName));
+    }
+
+    @NonNull
     UiObject2 waitForLauncherObject(String resName) {
         return waitForObjectBySelector(getLauncherObjectSelector(resName));
     }
@@ -1049,7 +1235,7 @@
                 () -> "Failed to receive an event for the state change: expected ["
                         + TestProtocol.stateOrdinalToString(expectedState)
                         + "], actual: " + eventListToString(actualEvents),
-                        actionName);
+                actionName);
     }
 
     private boolean isSwitchToStateEvent(
@@ -1070,9 +1256,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) {
@@ -1080,10 +1266,19 @@
         return getVisibleBounds(container).bottom - bottomGestureStartOnScreen;
     }
 
+    int getRightGestureMarginInContainer(UiObject2 container) {
+        final int rightGestureStartOnScreen = getRightGestureStartOnScreen();
+        return getVisibleBounds(container).right - rightGestureStartOnScreen;
+    }
+
     int getBottomGestureStartOnScreen() {
         return getRealDisplaySize().y - getBottomGestureSize();
     }
 
+    int getRightGestureStartOnScreen() {
+        return getRealDisplaySize().x - getWindowInsets().right;
+    }
+
     void clickLauncherObject(UiObject2 object) {
         waitForObjectEnabled(object, "clickLauncherObject");
         expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
@@ -1125,6 +1320,21 @@
                 true);
     }
 
+    void scrollLeftByDistance(UiObject2 container, int distance) {
+        final Rect containerRect = getVisibleBounds(container);
+        final int rightGestureMarginInContainer = getRightGestureMarginInContainer(container);
+        scroll(
+                container,
+                Direction.LEFT,
+                new Rect(
+                        0,
+                        containerRect.width() - distance - rightGestureMarginInContainer,
+                        0,
+                        rightGestureMarginInContainer),
+                10,
+                true);
+    }
+
     void scroll(
             UiObject2 container, Direction direction, Rect margins, int steps, boolean slowDown) {
         final Rect rect = getVisibleBounds(container);
@@ -1176,7 +1386,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
@@ -1189,14 +1399,15 @@
         final Point start = new Point(startX, startY);
         final Point end = new Point(endX, endY);
         sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
-        final long endTime = movePointer(start, end, steps, downTime, slowDown, gestureScope);
+        final long endTime = movePointer(
+                start, end, steps, false, downTime, slowDown, gestureScope);
         sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end, gestureScope);
     }
 
-    long movePointer(Point start, Point end, int steps, long downTime, boolean slowDown,
-            GestureScope gestureScope) {
-        long endTime = movePointer(
-                downTime, downTime, steps * GESTURE_STEP_MS, start, end, gestureScope);
+    long movePointer(Point start, Point end, int steps, boolean isDecelerating,
+            long downTime, boolean slowDown, GestureScope gestureScope) {
+        long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS,
+                isDecelerating, start, end, gestureScope);
         if (slowDown) {
             endTime = movePointer(downTime, endTime + GESTURE_STEP_MS, 5 * GESTURE_STEP_MS, end,
                     end, gestureScope);
@@ -1249,6 +1460,7 @@
                 break;
             case MotionEvent.ACTION_UP:
                 if (notLauncher3 && gestureScope != GestureScope.INSIDE
+                        && gestureScope != GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER
                         && (gestureScope == GestureScope.OUTSIDE_WITH_PILFER
                         || gestureScope == GestureScope.INSIDE_TO_OUTSIDE)) {
                     expectEvent(TestProtocol.SEQUENCE_PILFER, EVENT_PILFER_POINTERS);
@@ -1267,13 +1479,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();
@@ -1281,21 +1486,55 @@
 
     public long movePointer(long downTime, long startTime, long duration, Point from, Point to,
             GestureScope gestureScope) {
+        return movePointer(
+                downTime, startTime, duration, false, from, to, gestureScope);
+    }
+
+    public long movePointer(long downTime, long startTime, long duration, boolean isDecelerating,
+            Point from, Point to, GestureScope gestureScope) {
         log("movePointer: " + from + " to " + to);
         final Point point = new Point();
         long steps = duration / GESTURE_STEP_MS;
+
         long currentTime = startTime;
-        for (long i = 0; i < steps; ++i) {
-            sleep(GESTURE_STEP_MS);
 
-            currentTime += GESTURE_STEP_MS;
-            final float progress = (currentTime - startTime) / (float) duration;
+        if (isDecelerating) {
+            // formula: V = V0 - D*T, assuming V = 0 when T = duration
 
-            point.x = from.x + (int) (progress * (to.x - from.x));
-            point.y = from.y + (int) (progress * (to.y - from.y));
+            // vx0: initial speed at the x-dimension, set as twice the avg speed
+            // dx: the constant deceleration at the x-dimension
+            double vx0 = 2 * (to.x - from.x) / duration;
+            double dx = vx0 / duration;
+            // vy0: initial speed at the y-dimension, set as twice the avg speed
+            // dy: the constant deceleration at the y-dimension
+            double vy0 = 2 * (to.y - from.y) / duration;
+            double dy = vy0 / duration;
 
-            sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope);
+            for (long i = 0; i < steps; ++i) {
+                sleep(GESTURE_STEP_MS);
+                currentTime += GESTURE_STEP_MS;
+
+                // formula: P = P0 + V0*T - (D*T^2/2)
+                final double t = (i + 1) * GESTURE_STEP_MS;
+                point.x = from.x + (int) (vx0 * t - 0.5 * dx * t * t);
+                point.y = from.y + (int) (vy0 * t - 0.5 * dy * t * t);
+
+                sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope);
+            }
+        } else {
+            for (long i = 0; i < steps; ++i) {
+                sleep(GESTURE_STEP_MS);
+                currentTime += GESTURE_STEP_MS;
+
+                final float progress = (currentTime - startTime) / (float) duration;
+                point.x = from.x + (int) (progress * (to.x - from.x));
+                point.y = from.y + (int) (progress * (to.y - from.y));
+
+                sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point, gestureScope);
+
+            }
         }
+
         return currentTime;
     }
 
@@ -1366,16 +1605,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 +1642,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 +1718,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 +1727,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 +1750,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/OptionsPopupMenu.java b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java
deleted file mode 100644
index 787dc70..0000000
--- a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java
+++ /dev/null
@@ -1,41 +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.tapl;
-
-import androidx.annotation.NonNull;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
-
-public class OptionsPopupMenu {
-
-    private final LauncherInstrumentation mLauncher;
-    private final UiObject2 mDeepShortcutsContainer;
-
-    OptionsPopupMenu(LauncherInstrumentation launcher) {
-        mLauncher = launcher;
-        mDeepShortcutsContainer = launcher.waitForLauncherObject("popup_container");
-    }
-
-    /**
-     * Returns a menu item with a given label. Fails if it doesn't exist.
-     */
-    @NonNull
-    public OptionsPopupMenuItem getMenuItem(@NonNull final String label) {
-        final UiObject2 menuItem = mLauncher.waitForObjectInContainer(mDeepShortcutsContainer,
-                By.text(label));
-        return new OptionsPopupMenuItem(mLauncher, menuItem);
-    }
-}
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..a860e7d 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,81 @@
         mOverview.verifyActiveContainer();
     }
 
+    int getVisibleHeight() {
+        return mTask.getVisibleBounds().height();
+    }
+
+    int getVisibleWidth() {
+        return mTask.getVisibleBounds().width();
+    }
+
+    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..0bac2ca 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -77,7 +77,8 @@
             mLauncher.scroll(
                     widgetsContainer,
                     Direction.UP,
-                    new Rect(0, 0, mLauncher.getVisibleBounds(widgetsContainer).width(), 0),
+                    new Rect(0, 0, mLauncher.getRightGestureMarginInContainer(widgetsContainer) + 1,
+                            0),
                     FLING_STEPS, false);
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
                 verifyActiveContainer();
@@ -116,9 +117,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..3f0d7fd 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -34,16 +34,21 @@
 import androidx.test.uiautomator.Direction;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.testing.TestProtocol;
 
+import java.util.List;
+import java.util.function.Supplier;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * Operations on the workspace screen.
  */
 public final class Workspace extends Home {
     private static final int FLING_STEPS = 10;
+    private static final int DEFAULT_DRAG_STEPS = 10;
+    private static final String DROP_BAR_RES_ID = "drop_target_bar";
+    private static final String DELETE_TARGET_TEXT_ID = "delete_target_text";
 
     static final Pattern EVENT_CTRL_W_DOWN = Pattern.compile(
             "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_W"
@@ -63,7 +68,7 @@
     /**
      * Swipes up to All Apps.
      *
-     * @return the App Apps object.
+     * @return the All Apps object.
      */
     @NonNull
     public AllApps switchToAllApps() {
@@ -72,8 +77,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 +89,9 @@
                             + mLauncher.getTouchSlop());
 
             mLauncher.swipeToState(
-                    0,
+                    windowCornerRadius,
                     startY,
-                    0,
+                    windowCornerRadius,
                     startY - swipeHeight - mLauncher.getTouchSlop(),
                     12,
                     ALL_APPS_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE);
@@ -144,16 +148,7 @@
             if (!isWorkspaceScrollable(workspace)) {
                 try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                         "dragging icon to a second page of workspace to make it scrollable")) {
-                    dragIconToWorkspace(
-                            mLauncher,
-                            getHotseatAppIcon("Chrome"),
-                            new Point(mLauncher.getDevice().getDisplayWidth(),
-                                    mLauncher.getVisibleBounds(workspace).centerY()),
-                            "popup_container",
-                            false,
-                            false,
-                            () -> mLauncher.expectEvent(
-                                    TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT));
+                    dragIcon(workspace, getHotseatAppIcon("Chrome"), pagesPerScreen());
                     verifyActiveContainer();
                 }
             }
@@ -162,8 +157,50 @@
         }
     }
 
+    /**
+     * Returns the number of pages that are visible on the screen simultaneously.
+     */
+    public int pagesPerScreen() {
+        return mLauncher.isTwoPanels() ? 2 : 1;
+    }
+
+    /**
+     * Drags an icon to the (currentPage + pageDelta) page if the page already exists.
+     * If the target page doesn't exist, the icon will be put onto an existing page that is the
+     * closest to the target page.
+     *
+     * @param appIcon   - icon to drag.
+     * @param pageDelta - how many pages should the icon be dragged from the current page.
+     *                    It can be a negative value.
+     */
+    public void dragIcon(AppIcon appIcon, int pageDelta) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            final UiObject2 workspace = verifyActiveContainer();
+            try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                    "dragging icon to page with delta: " + pageDelta)) {
+                dragIcon(workspace, appIcon, pageDelta);
+                verifyActiveContainer();
+            }
+        }
+    }
+
+    private void dragIcon(UiObject2 workspace, AppIcon appIcon, int pageDelta) {
+        int pageWidth = mLauncher.getDevice().getDisplayWidth() / pagesPerScreen();
+        int targetX = (pageWidth / 2) + pageWidth * pageDelta;
+        dragIconToWorkspace(
+                mLauncher,
+                appIcon,
+                new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()),
+                "popup_container",
+                false,
+                false,
+                () -> mLauncher.expectEvent(
+                        TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT));
+        verifyActiveContainer();
+    }
+
     private boolean isWorkspaceScrollable(UiObject2 workspace) {
-        return workspace.getChildCount() > 1;
+        return workspace.getChildCount() > (mLauncher.isTwoPanels() ? 2 : 1);
     }
 
     @NonNull
@@ -172,40 +209,150 @@
                 mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
     }
 
-    static void dragIconToWorkspace(
-            LauncherInstrumentation launcher, Launchable launchable, Point dest,
-            String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
-            Runnable expectLongClickEvents) {
-        LauncherInstrumentation.log("dragIconToWorkspace: begin");
-        final Point launchableCenter = launchable.getObject().getVisibleCenter();
-        final long downTime = SystemClock.uptimeMillis();
-        launcher.runToState(
-                () -> {
-                    launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
-                            launchableCenter, LauncherInstrumentation.GestureScope.INSIDE);
-                    LauncherInstrumentation.log("dragIconToWorkspace: sent down");
-                    expectLongClickEvents.run();
-                    launcher.waitForLauncherObject(longPressIndicator);
-                    LauncherInstrumentation.log("dragIconToWorkspace: indicator");
-                    launcher.movePointer(launchableCenter, dest, 10, downTime, true,
-                            LauncherInstrumentation.GestureScope.INSIDE);
-                },
-                SPRING_LOADED_STATE_ORDINAL,
-                "long-pressing and moving");
-        LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
+    private static int getStartDragThreshold(LauncherInstrumentation launcher) {
+        return launcher.getTestInfo(TestProtocol.REQUEST_START_DRAG_THRESHOLD).getInt(
+                TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
+    /*
+     * Get the center point of the delete icon in the drop target bar.
+     */
+    private Point getDeleteDropPoint() {
+        return mLauncher.waitForObjectInContainer(
+                mLauncher.waitForLauncherObject(DROP_BAR_RES_ID),
+                DELETE_TARGET_TEXT_ID).getVisibleCenter();
+    }
+
+    /**
+     * Delete the appIcon from the workspace.
+     *
+     * @param appIcon to be deleted.
+     * @return validated workspace after the existing appIcon being deleted.
+     */
+    public Workspace deleteAppIcon(AppIcon appIcon) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "removing app icon from workspace")) {
+            dragIconToWorkspace(
+                    mLauncher, appIcon,
+                    () -> getDeleteDropPoint(),
+                    true, /* decelerating */
+                    appIcon.getLongPressIndicator(),
+                    () -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
+                    null);
+
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "dragged the app to the drop bar")) {
+                return new Workspace(mLauncher);
+            }
+        }
+    }
+
+    /**
+     * Finds folder icons in the current workspace.
+     *
+     * @return a list of folder icons.
+     */
+    List<FolderIcon> getFolderIcons() {
+        final UiObject2 workspace = verifyActiveContainer();
+        return mLauncher.getObjectsInContainer(workspace, "folder_icon_name").stream().map(
+                o -> new FolderIcon(mLauncher, o)).collect(Collectors.toList());
+    }
+
+    /**
+     * Drag an icon up with a short distance that makes workspace go to spring loaded state.
+     *
+     * @return the position after dragging.
+     */
+    private static Point dragIconToSpringLoaded(LauncherInstrumentation launcher, long downTime,
+            UiObject2 icon,
+            String longPressIndicator, Runnable expectLongClickEvents) {
+        final Point iconCenter = icon.getVisibleCenter();
+        final Point dragStartCenter = new Point(iconCenter.x,
+                iconCenter.y - getStartDragThreshold(launcher));
+
+        launcher.runToState(() -> {
+            launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
+                    iconCenter, LauncherInstrumentation.GestureScope.INSIDE);
+            LauncherInstrumentation.log("dragIconToSpringLoaded: sent down");
+            expectLongClickEvents.run();
+            launcher.waitForLauncherObject(longPressIndicator);
+            LauncherInstrumentation.log("dragIconToSpringLoaded: indicator");
+            launcher.movePointer(iconCenter, dragStartCenter, DEFAULT_DRAG_STEPS, false,
+                    downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
+        }, SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
+        return dragStartCenter;
+    }
+
+    private static void dropDraggedIcon(LauncherInstrumentation launcher, Point dest, long downTime,
+            @Nullable Runnable expectedEvents) {
         launcher.runToState(
                 () -> launcher.sendPointer(
                         downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest,
                         LauncherInstrumentation.GestureScope.INSIDE),
                 NORMAL_STATE_ORDINAL,
                 "sending UP event");
-        if (startsActivity || isWidgetShortcut) {
-            launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START);
+        if (expectedEvents != null) {
+            expectedEvents.run();
         }
-        LauncherInstrumentation.log("dragIconToWorkspace: end");
+        LauncherInstrumentation.log("dropIcon: end");
         launcher.waitUntilLauncherObjectGone("drop_target_bar");
     }
 
+    static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
+            Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
+            Runnable expectLongClickEvents) {
+        Runnable expectDropEvents = null;
+        if (startsActivity || isWidgetShortcut) {
+            expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN,
+                    LauncherInstrumentation.EVENT_START);
+        }
+        dragIconToWorkspace(launcher, launchable, () -> dest, false, longPressIndicator,
+                expectLongClickEvents, expectDropEvents);
+    }
+
+    /**
+     * Drag icon in workspace to else where.
+     * This function expects the launchable is inside the workspace and there is no drop event.
+     */
+    static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
+            Supplier<Point> destSupplier, String longPressIndicator) {
+        dragIconToWorkspace(launcher, launchable, destSupplier, false, longPressIndicator,
+                () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null);
+    }
+
+    static void dragIconToWorkspace(
+            LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> dest,
+            boolean isDecelerating, String longPressIndicator, Runnable expectLongClickEvents,
+            @Nullable Runnable expectDropEvents) {
+        try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
+                "want to drag icon to workspace")) {
+            final long downTime = SystemClock.uptimeMillis();
+            Point dragStart = dragIconToSpringLoaded(launcher, downTime,
+                    launchable.getObject(), longPressIndicator, expectLongClickEvents);
+            Point targetDest = dest.get();
+            int displayX = launcher.getRealDisplaySize().x;
+
+            // Since the destination can be on another page, we need to drag to the edge first
+            // until we reach the target page
+            while (targetDest.x > displayX || targetDest.x < 0) {
+                int edgeX = targetDest.x > 0 ? displayX : 0;
+                Point screenEdge = new Point(edgeX, targetDest.y);
+                launcher.movePointer(dragStart, screenEdge, DEFAULT_DRAG_STEPS, isDecelerating,
+                        downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
+                launcher.waitForIdle(); // Wait for the page change to happen
+                targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1);
+                dragStart = screenEdge;
+            }
+
+            // targetDest.x is now between 0 and displayX so we found the target page,
+            // we just have to put move the icon to the destination and drop it
+            launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
+                    downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
+            dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
+        }
+    }
+
     /**
      * Flings to get to screens on the right. Waits for scrolling and a possible overscroll
      * recoil to complete.