Merge Android 12

Bug: 202323961
Merged-In: I3958bd9fb5521d09fa7cae642128b9f5e11b8e2e
Change-Id: I5c1ce9f07b41e45ae38dc81f553c5e64a9abe8ff
diff --git a/Android.bp b/Android.bp
index 50c8556..1b6ffe4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -16,6 +16,8 @@
     default_applicable_licenses: ["packages_apps_Launcher3_license"],
 }
 
+min_launcher3_sdk_version = "26"
+
 // Added automatically by a large-scale-change
 // See: http://go/android-license-faq
 license {
@@ -36,14 +38,15 @@
         "androidx.test.runner",
         "androidx.test.rules",
         "androidx.test.uiautomator_uiautomator",
+        "androidx.preference_preference",
         "SystemUISharedLib",
     ],
     srcs: [
         "tests/tapl/**/*.java",
-        "src/com/android/launcher3/util/SecureSettingsObserver.java",
         "src/com/android/launcher3/ResourceUtils.java",
         "src/com/android/launcher3/testing/TestProtocol.java",
     ],
+    resource_dirs: [ ],
     manifest: "tests/tapl/AndroidManifest.xml",
     platform_apis: true,
 }
@@ -52,19 +55,37 @@
     name: "launcher_log_protos_lite",
     srcs: [
         "protos/*.proto",
-        "proto_overrides/*.proto",
+        "protos_overrides/*.proto",
     ],
     sdk_version: "current",
     proto: {
         type: "lite",
         local_include_dirs:[
             "protos",
-            "proto_overrides",
+            "protos_overrides",
         ],
     },
     static_libs: ["libprotobuf-java-lite"],
 }
 
+java_library_static {
+    name: "launcher_quickstep_log_protos_lite",
+    srcs: [
+        "quickstep/protos_overrides/*.proto",
+    ],
+    sdk_version: "current",
+    proto: {
+        type: "lite",
+        local_include_dirs:[
+            "quickstep/protos_overrides",
+        ],
+    },
+    static_libs: [
+      "libprotobuf-java-lite",
+      "launcher_log_protos_lite"
+      ],
+}
+
 java_library {
     name: "LauncherPluginLib",
 
@@ -73,5 +94,161 @@
     srcs: ["src_plugins/**/*.java"],
 
     sdk_version: "current",
-    min_sdk_version: "28",
+    min_sdk_version: min_launcher3_sdk_version,
 }
+
+// Library with all the dependencies for building Launcher3
+android_library {
+    name: "Launcher3ResLib",
+    srcs: [ ],
+    resource_dirs: ["res"],
+    static_libs: [
+        "LauncherPluginLib",
+        "launcher_quickstep_log_protos_lite",
+        "androidx-constraintlayout_constraintlayout",
+        "androidx.recyclerview_recyclerview",
+        "androidx.dynamicanimation_dynamicanimation",
+        "androidx.fragment_fragment",
+        "androidx.preference_preference",
+        "androidx.slice_slice-view",
+        "androidx.cardview_cardview",
+        "iconloader_base",
+    ],
+    manifest: "AndroidManifest-common.xml",
+    sdk_version: "current",
+    min_sdk_version: min_launcher3_sdk_version,
+    lint: {
+        baseline_filename: "lint-baseline-res-lib.xml",
+    },
+}
+
+//
+// Build rule for Launcher3 dependencies lib.
+//
+android_library {
+    name: "Launcher3CommonDepsLib",
+    srcs: ["src_build_config/**/*.java"],
+    static_libs: ["Launcher3ResLib"],
+    sdk_version: "current",
+    min_sdk_version: min_launcher3_sdk_version,
+    manifest: "AndroidManifest-common.xml",
+    lint: {
+        baseline_filename: "lint-baseline-common-deps-lib.xml",
+    },
+}
+
+//
+// Build rule for Launcher3 app.
+//
+android_app {
+    name: "Launcher3",
+
+    static_libs: [
+        "Launcher3CommonDepsLib",
+    ],
+    srcs: [
+        "src/**/*.java",
+        "src_shortcuts_overrides/**/*.java",
+        "src_ui_overrides/**/*.java",
+        "ext_tests/src/**/*.java",
+    ],
+    resource_dirs: [
+        "ext_tests/res",
+    ],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+        // Proguard is disable for testing. Derivarive prjects to keep proguard enabled
+        enabled: false,
+    },
+
+    sdk_version: "current",
+    min_sdk_version: min_launcher3_sdk_version,
+    target_sdk_version: "current",
+    privileged: true,
+    system_ext_specific: true,
+
+    overrides: [
+        "Home",
+        "Launcher2",
+    ],
+    required: ["privapp_whitelist_com.android.launcher3"],
+
+    jacoco: {
+        include_filter: ["com.android.launcher3.**"],
+    },
+    additional_manifests: [
+        "AndroidManifest-common.xml",
+    ],
+    lint: {
+        baseline_filename: "lint-baseline-launcher3.xml",
+    },
+}
+
+// Library with all the dependencies for building quickstep
+android_library {
+    name: "QuickstepResLib",
+    srcs: [ ],
+    resource_dirs: [
+        "quickstep/res",
+    ],
+    static_libs: [
+        "Launcher3ResLib",
+        "SystemUISharedLib",
+        "SystemUI-statsd",
+    ],
+    manifest: "quickstep/AndroidManifest.xml",
+    min_sdk_version: "current",
+}
+
+
+// Source code used for test helpers
+filegroup {
+    name: "launcher-src-ext-tests",
+    srcs: ["ext_tests/src/**/*.java"],
+}
+
+// Common source files used to build launcher
+filegroup {
+    name: "launcher-src-no-build-config",
+    srcs: [
+        "src/**/*.java",
+        "src_shortcuts_overrides/**/*.java",
+        "quickstep/src/**/*.java",
+    ],
+}
+
+// Proguard files for Launcher3
+filegroup {
+    name: "launcher-proguard-rules",
+    srcs: ["proguard.flags"],
+}
+
+
+// Library with all the dependencies for building Launcher Go
+android_library {
+    name: "LauncherGoResLib",
+    srcs: [
+        "src/**/*.java",
+        "quickstep/src/**/*.java",
+        "go/src/**/*.java",
+        "go/quickstep/src/**/*.java",
+    ],
+    resource_dirs: [
+        "go/res",
+        "go/quickstep/res",
+    ],
+    static_libs: [
+        "Launcher3CommonDepsLib",
+        "QuickstepResLib",
+    ],
+    manifest: "quickstep/AndroidManifest-launcher.xml",
+    additional_manifests: [
+        "go/AndroidManifest.xml",
+        "AndroidManifest-common.xml",
+    ],
+    min_sdk_version: "current",
+    lint: {
+        baseline_filename: "lint-baseline-go-res-lib.xml",
+    },
+}
+
diff --git a/Android.mk b/Android.mk
index aff12c5..c222f24 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,86 +17,6 @@
 LOCAL_PATH := $(call my-dir)
 
 #
-# Build rule for Launcher3 dependencies lib.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT2_ONLY := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.recyclerview_recyclerview \
-    androidx.dynamicanimation_dynamicanimation \
-    androidx.preference_preference \
-    iconloader_base
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    LauncherPluginLib \
-    launcher_log_protos_lite
-
-LOCAL_SRC_FILES := \
-    $(call all-proto-files-under, protos) \
-    $(call all-proto-files-under, proto_overrides) \
-    $(call all-java-files-under, src_build_config) \
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
-LOCAL_MODULE := Launcher3CommonDepsLib
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_MANIFEST_FILE := AndroidManifest-common.xml
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#
-# Build rule for Launcher3 app.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, src_shortcuts_overrides) \
-    $(call all-java-files-under, src_ui_overrides) \
-    $(call all-java-files-under, ext_tests/src)
-    
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/ext_tests/res
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-# Proguard is disable for testing. Derivarive prjects to keep proguard enabled
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
-LOCAL_PACKAGE_NAME := Launcher3
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-include $(BUILD_PACKAGE)
-
-#
 # Build rule for Launcher3 Go app for Android Go devices.
 #
 include $(CLEAR_VARS)
@@ -114,7 +34,7 @@
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
 LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
+LOCAL_MIN_SDK_VERSION := 26
 LOCAL_PACKAGE_NAME := Launcher3Go
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_SYSTEM_EXT_MODULE := true
@@ -142,9 +62,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     SystemUI-statsd \
-    SystemUISharedLib \
-    launcherprotosnano \
-    launcher_log_protos_lite
+    SystemUISharedLib
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
@@ -161,12 +79,9 @@
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
     $(call all-java-files-under, quickstep/src) \
-    $(call all-java-files-under, quickstep/recents_ui_overrides/src) \
     $(call all-java-files-under, src_shortcuts_overrides)
 
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/quickstep/res \
-    $(LOCAL_PATH)/quickstep/recents_ui_overrides/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
 LOCAL_PROGUARD_ENABLED := disabled
 
 
@@ -195,9 +110,7 @@
 LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
 
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/quickstep/res \
-    $(LOCAL_PATH)/quickstep/recents_ui_overrides/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
     $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
@@ -221,9 +134,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     SystemUI-statsd \
-    SystemUISharedLib \
-    launcherprotosnano \
-    launcher_log_protos_lite
+    SystemUISharedLib
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
@@ -235,13 +146,13 @@
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
     $(call all-java-files-under, quickstep/src) \
-    $(call all-java-files-under, quickstep/recents_ui_overrides/src) \
-    $(call all-java-files-under, go/src)
+    $(call all-java-files-under, go/src) \
+    $(call all-java-files-under, go/quickstep/src)
 
 LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/quickstep/res \
-    $(LOCAL_PATH)/quickstep/recents_ui_overrides/res \
-    $(LOCAL_PATH)/go/res
+    $(LOCAL_PATH)/go/quickstep/res \
+    $(LOCAL_PATH)/go/res \
+    $(LOCAL_PATH)/quickstep/res
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 LOCAL_PROGUARD_ENABLED := full
@@ -254,7 +165,7 @@
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
     $(LOCAL_PATH)/go/AndroidManifest.xml \
-    $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
+    $(LOCAL_PATH)/go/AndroidManifest-launcher.xml \
     $(LOCAL_PATH)/AndroidManifest-common.xml
 
 LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 77f2428..3b6141b 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -19,6 +19,7 @@
 -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.android.launcher3">
 
     <!--
@@ -29,13 +30,8 @@
     at compile time. Note that the components defined in AndroidManifest.xml are also required,
     with some minor changed based on the derivative app.
     -->
-    <permission
-        android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="dangerous"
-        android:label="@string/permlab_install_shortcut"
-        android:description="@string/permdesc_install_shortcut" />
 
+    <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" />
@@ -45,9 +41,8 @@
     <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
-
-    <!-- TODO(b/150802536): Enabled only for ENABLE_FIXED_ROTATION_TRANSFORM feature flag -->
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+    <!-- for rotating surface by arbitrary degree -->
+    <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
     
     <!--
     Permissions required for read/write access to the workspace data. These permission name
@@ -82,41 +77,27 @@
         android:restoreAnyVersion="true"
         android:supportsRtl="true" >
 
-        <!-- Intent received used to install shortcuts from other applications -->
-        <receiver
-            android:name="com.android.launcher3.InstallShortcutReceiver"
-            android:permission="com.android.launcher.permission.INSTALL_SHORTCUT"
-            android:enabled="@bool/enable_install_shortcut_api" >
-            <intent-filter>
-                <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
-            </intent-filter>
-        </receiver>
-
         <!-- Intent received when a session is committed -->
         <receiver
-            android:name="com.android.launcher3.SessionCommitReceiver" >
+            android:name="com.android.launcher3.SessionCommitReceiver"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.content.pm.action.SESSION_COMMITTED" />
             </intent-filter>
         </receiver>
 
         <!-- Intent received used to initialize a restored widget -->
-        <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" >
+        <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/>
             </intent-filter>
         </receiver>
 
         <service
-            android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
-            android:exported="false"
-            android:process=":wallpaper_chooser"
-            android:permission="android.permission.BIND_JOB_SERVICE" />
-
-        <service
             android:name="com.android.launcher3.notification.NotificationListener"
             android:label="@string/notification_dots_service_title"
-            android:enabled="@bool/notification_dots_enabled"
+            android:exported="true"
             android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
             <intent-filter>
                 <action android:name="android.service.notification.NotificationListenerService" />
@@ -127,10 +108,10 @@
                        android:value="true" />
 
         <activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
-            android:theme="@style/AppItemActivityTheme"
+            android:theme="@style/AddItemActivityTheme"
             android:excludeFromRecents="true"
             android:autoRemoveFromRecents="true"
-            android:label="@string/action_add_to_workspace" >
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
                 <action android:name="android.content.pm.action.CONFIRM_PIN_APPWIDGET" />
@@ -165,6 +146,7 @@
             android:name="com.android.launcher3.settings.SettingsActivity"
             android:label="@string/settings_button_text"
             android:theme="@style/HomeSettingsTheme"
+            android:exported="true"
             android:autoRemoveFromRecents="true">
             <intent-filter>
                 <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
@@ -187,6 +169,7 @@
             android:name="com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher"
             android:theme="@style/AppTheme"
             android:launchMode="singleTop"
+            android:exported="true"
             android:enabled="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -194,5 +177,11 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+
+        <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
+        <provider
+            android:name="androidx.startup.InitializationProvider"
+            android:authorities="${applicationId}.androidx-startup"
+            tools:node="remove" />
     </application>
 </manifest>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b031ffb..f793131 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3">
-    <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+    <uses-sdk android:targetSdkVersion="30" android:minSdkVersion="26"/>
     <!--
     Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
     Refer comments around specific entries on how to extend individual components.
@@ -49,10 +49,11 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
+            android:exported="true"
             android:enabled="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/OWNERS b/OWNERS
index 21adf60..05fa502 100644
--- a/OWNERS
+++ b/OWNERS
@@ -38,6 +38,7 @@
 peanutbutter@google.com
 xuqiu@google.com
 sreyasr@google.com
+thiruram@google.com
 
 per-file FeatureFlags.java, globs = set noparent
 per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, zakcohen@google.com, mrcasey@google.com, adamcohen@google.com, hyunyoungs@google.com
diff --git a/SharedLibWrapper/build.gradle b/SharedLibWrapper/build.gradle
deleted file mode 100644
index 674e38a..0000000
--- a/SharedLibWrapper/build.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-apply plugin: 'java'
-
-final String ANDROID_TOP = "${rootDir}/../../.."
-final String FRAMEWORK_PREBUILTS_DIR = "${ANDROID_TOP}/prebuilts/framework_intermediates/"
-
-sourceSets {
-    main {
-        java.srcDirs = ["${ANDROID_TOP}/frameworks/lib/systemui/SharedLibWrapper/src"]
-    }
-}
-
-sourceCompatibility = 1.8
-
-dependencies {
-    implementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/quickstep/libs", include: 'sysui_shared.jar')
-    compileOnly fileTree(dir: "$ANDROID_TOP/prebuilts/fullsdk-${org.gradle.internal.os.OperatingSystem.current().isMacOsX() ? "darwin" : "linux"}/platforms/${COMPILE_SDK}", include: 'android.jar')
-}
diff --git a/buglist.txt b/buglist.txt
new file mode 100644
index 0000000..53dcc35
--- /dev/null
+++ b/buglist.txt
@@ -0,0 +1,42 @@
+171450807
+170675311
+170338029
+170338170
+160544577
+171171594
+170488559
+171131394
+171131394
+171026321
+170648272
+170752716
+170611866
+170702596
+170487752
+170665892
+168608912
+170636685
+169771796
+141126144
+166614700
+168805872
+170263425
+169221288
+143965596
+169221287
+167259591
+156044202
+169438169
+164926736
+168653219
+169963211
+170121063
+169988381
+169980192
+169221288
+169385783
+168167693
+169796517
+169330678
+168818961
+168608912
diff --git a/buglist_unique.txt b/buglist_unique.txt
new file mode 100644
index 0000000..93dbefb
--- /dev/null
+++ b/buglist_unique.txt
@@ -0,0 +1,39 @@
+141126144
+143965596
+156044202
+160544577
+164926736
+166614700
+167259591
+168167693
+168608912
+168653219
+168805872
+168818961
+169221287
+169221288
+169330678
+169385783
+169438169
+169771796
+169796517
+169963211
+169980192
+169988381
+170121063
+170263425
+170338029
+170338170
+170487752
+170488559
+170611866
+170636685
+170648272
+170665892
+170675311
+170702596
+170752716
+171026321
+171131394
+171171594
+171450807
diff --git a/buglist_with_title.txt b/buglist_with_title.txt
new file mode 100644
index 0000000..aa8b413
--- /dev/null
+++ b/buglist_with_title.txt
@@ -0,0 +1,24 @@
+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 534ca65..0622d87 100644
--- a/build.gradle
+++ b/build.gradle
@@ -21,8 +21,8 @@
     buildToolsVersion BUILD_TOOLS_VERSION
 
     defaultConfig {
-        minSdkVersion 25
-        targetSdkVersion 28
+        minSdkVersion 26
+        targetSdkVersion 30
         versionCode 1
         versionName "1.0"
 
@@ -81,8 +81,7 @@
             java.srcDirs = ['src', 'src_plugins']
             manifest.srcFile 'AndroidManifest-common.xml'
             proto {
-                srcDir 'protos/'
-                srcDir 'proto_overrides/'
+                srcDirs = ['protos/', 'protos_overrides/']
             }
         }
 
@@ -150,7 +149,6 @@
     implementation "androidx.preference:preference:${ANDROID_X_VERSION}"
     implementation project(':IconLoader')
     withQuickstepImplementation project(':SharedLibWrapper')
-    implementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/libs", include: 'launcher_protos.jar')
 
     // Recents lib dependency
     withQuickstepImplementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/quickstep/libs", include: 'sysui_shared.jar')
@@ -171,18 +169,14 @@
 protobuf {
     // Configure the protoc executable
     protoc {
-        artifact = 'com.google.protobuf:protoc:3.0.0'
-
-        generateProtoTasks {
-            all().each { task ->
-                task.builtins {
-                    remove java
-                    javanano {
-                        option "java_package=launcher_log_extension.proto|com.android.launcher3.userevent.nano"
-                        option "java_package=launcher_log.proto|com.android.launcher3.userevent.nano"
-                        option "java_package=launcher_dump.proto|com.android.launcher3.model.nano"
-                        option "enum_style=java"
-                    }
+        artifact = "com.google.protobuf:protoc:${protocVersion}"
+    }
+    generateProtoTasks {
+        all().each { task ->
+            task.builtins {
+                remove java
+                java {
+                    option "lite"
                 }
             }
         }
diff --git a/commitlist.txt b/commitlist.txt
new file mode 100644
index 0000000..27b8bac
--- /dev/null
+++ b/commitlist.txt
@@ -0,0 +1,934 @@
+COMMAND>> git log f99351888c3e5a128559678304fefd647472bc7f..4c3952dc60fc78d3816012a86d7e71747ef34c74
+commit 4c3952dc60fc78d3816012a86d7e71747ef34c74
+Merge: cb403d9e5 70e8b1572
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Fri Oct 23 00:27:39 2020 +0000
+
+    Merge "Minor stylistic changes in Workspace.java." into ub-launcher3-master
+
+commit cb403d9e5235c7323bc2fdffe6a264d17bb6d0a6
+Author: Pinyao Ting <pinyaoting@google.com>
+Date:   Thu Oct 22 16:07:08 2020 -0700
+
+    flip default value of minimal device feature flag
+    
+    Test: manual
+    Change-Id: Iaf46dffb935bdf4b46e7c57d547bdc697250ec56
+
+commit a97557a15eb111616d868120a9f4659f1b451fa2
+Merge: f5ce80b8a 932a327eb
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Thu Oct 22 18:22:56 2020 +0000
+
+    Merge "Consider overscroll adjustment of RecentsView for live tile" into ub-launcher3-master
+
+commit 932a327ebf0587b8324b9fea7d31328b2f6719a8
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Wed Oct 21 23:29:00 2020 -0700
+
+    Consider overscroll adjustment of RecentsView for live tile
+    
+    Fixes: 171450807
+    Test: manual
+    Change-Id: I83eebf1f6b61c67f289db51aabe5a971815d0df1
+
+commit f5ce80b8a0a1636fc8159475177a07b281492c88
+Author: Hilary Huo <hhuo@google.com>
+Date:   Wed Oct 14 16:35:55 2020 -0700
+
+    [pixel-search] Latency analysis, add logging statement in launcher
+    
+    Bug: b/170675311
+    Change-Id: I229ace399085bea1c3f9535eb713edd329dff8bd
+
+commit 31b03941ef3aa17edc08c1b509d4fa23766f2d2c
+Merge: e0a50c9e3 0731273d5
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Wed Oct 21 20:03:57 2020 +0000
+
+    Merge "Track live tile better by considering resistance animation" into ub-launcher3-master
+
+commit 0731273d5409149fca32dfb2ad76eab45f6ea79a
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Wed Oct 21 12:03:40 2020 -0700
+
+    Track live tile better by considering resistance animation
+    
+    Fixes: 170338029
+    Test: Manual
+    Change-Id: I66536bae567aa94385d5e0352cec9d46d512927a
+
+commit e0a50c9e3f1d4b9f113d6afae01ff2c4ed452fba
+Merge: d2c27a595 acfac6187
+Author: Alex Chau <alexchau@google.com>
+Date:   Wed Oct 21 17:02:57 2020 +0000
+
+    Merge "Use Diplay.getMetrics in DisplayController" into ub-launcher3-master
+
+commit d2c27a595065d43bbea37dd2a512d37080f5233e
+Merge: ff8febabb 8b488ccc2
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Wed Oct 21 07:19:48 2020 +0000
+
+    Merge "[Live Tile] Support launching running task animation" into ub-launcher3-master
+
+commit 8b488ccc2e433a708c8b06f0b6866f2a305e4b0a
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Wed Oct 14 12:13:04 2020 -0700
+
+    [Live Tile] Support launching running task animation
+    
+    Fixes: 170338170
+    Test: manual
+    Change-Id: I2526b7cfbacaea7899b8e2ed233f913630071d36
+
+commit 70e8b157219e9090ba5e47fdfa51b2b92e98449d
+Author: Andy Wickham <awickham@google.com>
+Date:   Wed Oct 7 23:00:06 2020 -0700
+
+    Minor stylistic changes in Workspace.java.
+    
+    Change-Id: Ib07611f27cbc427d11abccd8b74ea144485752f7
+
+commit acfac6187dd9d13d55b566a77a5da867a1813573
+Author: Alex Chau <alexchau@google.com>
+Date:   Mon Oct 19 18:00:39 2020 +0100
+
+    Use Diplay.getMetrics in DisplayController
+    
+    - This is a workaround of b/163815566, where DisplayMetrics is stale
+      when onDisplayChanged is called.
+    - Instead of relying on stale DisplayConext, get the DisplayMetrics
+      from the Display directly.
+    - Also optimized how DisplayController.Info is created by passing in
+      Display only
+    - Use mDisplayContext.getDisplay directly if availalbe
+    
+    Bug: 163815566, 160544577
+    Test: DPI looks correct on device boot
+    Change-Id: I2a7454bb8cf2073ce592e8662781b87fc998444f
+    (cherry picked from commit 177c38243dc3bf245d1f7db3c265dfb56522f441)
+
+commit ff8febabb039a3c27ee068f85119860a048b917c
+Merge: b03d2b416 102746823
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Tue Oct 20 17:46:09 2020 +0000
+
+    Merge "Makes Plugin Settings gear adjust to dark mode." into ub-launcher3-master
+
+commit b03d2b41616d479ba360fa4f97e57722c7f57b8e
+Merge: fb79f5541 caa1e9c39
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date:   Tue Oct 20 15:25:56 2020 +0000
+
+    Merge "Search query method should support multiple consumers" into ub-launcher3-master
+
+commit fb79f5541dcbe587002756bb40a3c632d38cc25a
+Merge: f6b05068d cf0b275a4
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date:   Tue Oct 20 13:51:06 2020 +0000
+
+    Merge "Add the ability to specify a list of tutorial steps in the gesture sandbox tutorial intent." into ub-launcher3-master
+
+commit f6b05068d901d4e989b2e107c06f9c7a6e7b113f
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date:   Tue Oct 20 00:19:29 2020 -0700
+
+    Invert the badging
+    
+    Bug: 171171594
+    Change-Id: If84fdc03254105c843e16f39f479505b16e1cd5f
+
+commit caa1e9c39978cb3b467b5ac441eb39b5e883fa2e
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date:   Mon Oct 12 13:56:02 2020 -0700
+
+    Search query method should support multiple consumers
+    
+    Bug: 170488559
+    Change-Id: I64bef9523d3c3950c4ca3a4b9ce1d506d1672200
+
+commit 10274682339bb60cb24c50536b4f48f921970f3c
+Author: Andy Wickham <awickham@google.com>
+Date:   Mon Oct 19 19:06:52 2020 -0700
+
+    Makes Plugin Settings gear adjust to dark mode.
+    
+    It wasn't visible in dark mode before because it was
+    black on black. This makes it adjust automatically.
+    
+    Change-Id: I5176cffc01842509ddafc4f30ff5029a0c4b8050
+
+commit 744a0fbeae8efaa942d21c61e25012d86f5ff81e
+Merge: 29c79947e 71f24588c
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Mon Oct 19 22:17:26 2020 +0000
+
+    Merge "Call click event on IME quick select for SearchResultIcon" into ub-launcher3-master
+
+commit 29c79947ecf82f662d02004ba9a7289017fc0783
+Merge: 13a2a010d a68ac3e5d
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Mon Oct 19 18:42:05 2020 +0000
+
+    Merge "Removing condition for CUJ tracing/metrics" into ub-launcher3-master
+
+commit 71f24588c0a66449a0c68bcb360a8c671914ce75
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Mon Oct 19 10:19:31 2020 -0700
+
+    Call click event on IME quick select for SearchResultIcon
+    
+    Bug: 171131394
+    Change-Id: I8a703e8d0ca10570e3f774510610d3fb4c0eaab8
+
+commit 13a2a010decd87eeaf8932430c692f587d2de165
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Sun Oct 18 21:19:57 2020 -0700
+
+    Handle IME event for SearchResultIcon
+    
+    Bug: 171131394
+    Test: Manual
+    Change-Id: I2ed1c61053c78aaecc3324418229d69634a72ae4
+
+commit 1f79eeda76246534697e92740defc7f73c3c8d14
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Fri Oct 16 02:01:31 2020 -0700
+
+    Remove hardcoded itemTypes from SearchTarget
+    
+    - Introduces componentName and userHandle members to SearchTarget
+    - SearchTargetEvent now has searchTarget member
+    - Builder pattern for SearchTarget and SearchTargetEvent
+    - Search backend should add headers manually instead of launcher inferring sections
+    
+    Bug: 171026321
+    Test: Manual
+    Change-Id: I28e0455e82b925277a17703b9aa061c8f9f15262
+
+commit a68ac3e5dd23095cea7c872c0ff1c5042d1695ba
+Author: vadimt <vadimt@google.com>
+Date:   Fri Oct 16 10:48:28 2020 -0700
+
+    Removing condition for CUJ tracing/metrics
+    
+    Is doesn't reflect whether jank monitors is collecting metrics,
+    which will eventually be always true anyways.
+    
+    Change-Id: Iaebdc838ed2b2cebd32c8c48d7e45bdd93f76fb4
+
+commit 9228ff53c2fb26850b7bd92d86214a6aaebb11d3
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date:   Mon Oct 12 13:43:51 2020 -0700
+
+    Trimming activity and task label
+    
+    Bug: 170648272
+    Change-Id: Icd099acee65305e0aa0f98a2a301a0df8a27cf07
+
+commit 7a09177e500a53205f9969bb6cbd4251d54e8fde
+Merge: 37ed5ead3 314761a80
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Thu Oct 15 22:36:14 2020 +0000
+
+    Merge "Setup SearchResultIcon for single cell results" into ub-launcher3-master
+
+commit 37ed5ead391df5747003b2d3a345be0347362f19
+Merge: d5bbe6809 702ed2788
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Thu Oct 15 22:06:12 2020 +0000
+
+    Merge "Fix the issue where shortcuts are removed in minimal device mode" into ub-launcher3-master
+
+commit 314761a80819a6e64a136161f51eebb0f0528c4d
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Wed Oct 14 10:15:07 2020 -0700
+
+    Setup SearchResultIcon for single cell results
+    
+    SearchResultIcon will be able to render apps, shortcuts and remote actions. It can also handle its own focused state drawing.
+    
+    Screenshot: https://screenshot.googleplex.com/C3KgjJtLQTBPgaf
+    
+    Bug: 170752716
+    Test: Manual
+    Change-Id: I460a9c128ea3f5814784e342c5d5fa5b7e310882
+
+commit 702ed2788678ac744c768aad6a6302e7cf91a26b
+Author: Pinyao Ting <pinyaoting@google.com>
+Date:   Wed Oct 14 11:17:04 2020 -0700
+
+    Fix the issue where shortcuts are removed in minimal device mode
+    
+    When loading the workspace, Launcher pins/unpins shortcuts in comply
+    with the loaded workspace. Since minimal device mode creates a mostly
+    empty workspace, existing shortcuts are getting unpinned as a result.
+    
+    To mitigate the issue this CL compares the db name and only invoke
+    sanitizeData when it matches the one defined in InvariantDeviceProfile.
+    
+    Bug: 170611866
+    Test: manual
+    1. add some deep shortcut in workspace (e.g. long tap on chrome, drag
+    "incognito tab" to workspace)
+    2. opt-in to sunshine fishfood (g/sunshine-teamfood)
+    3. enable bedtime mode with minimal device in Settings -> Digital
+    Wellbeing -> Show Your Data -> Bedtime mode -> Customize -> minimal
+    device
+    4. toggle bedtime mode, wait for apps in minimal device to show, then
+    toggle off bedtime mode
+    5. verify the deep shortcut still exist
+    
+    Change-Id: Ie18216ecb288e7481aa2404c4cb3ea418aee85cb
+
+commit cf0b275a48d3c9f91a346f7fc24b9604f6dde25a
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date:   Tue Oct 6 09:33:40 2020 -0400
+
+    Add the ability to specify a list of tutorial steps in the gesture sandbox tutorial intent.
+    
+    Added tutorial_steps string array in the intent to allow specifying an ordered list of tutorial steps.
+    
+    Change-Id: Ic42a65598a74a64f8441a22f58c6cd988a5762e3
+
+commit d5bbe6809dcc056fbfc307909b171651f0fb3044
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Wed Oct 14 15:39:38 2020 -0700
+
+    Rename shrotcut container to deep-shrotcuts
+    
+    Change-Id: If94f0dfa447235f3b1a652f7b6c749695b42d97c
+
+commit 26c1105fa04c2bcc156051e51df90a6a253349bb
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Tue Oct 13 01:12:03 2020 -0700
+
+    [search api part 1] Setup centralized SearchEventTracker
+    
+    - Rename AdapterItemWIthPayload to SearchAdapterItem, PayloadResultHandler to SearchTargetHandler
+    - Setup SliceViewWrapper for self contained slices
+    
+    Bug: 170702596
+    Change-Id: I0baf984ec8123c95011abcc17372f8d055e98ad7
+
+commit 057f2d0d7df67e3680e479ac76b48b30d8bcf884
+Merge: 4bb65ff51 9a6145efb
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Tue Oct 13 01:57:47 2020 +0000
+
+    Merge "Introduce shortcut container for hotseat event reporting" into ub-launcher3-master
+
+commit 4bb65ff516c6d9a429971ab7e04780792d5cb751
+Merge: 69740e62b 2afcab804
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Tue Oct 13 00:07:36 2020 +0000
+
+    Merge "Search UI clean up" into ub-launcher3-master
+
+commit 2afcab804b638ff3b9da5bad40c8f70bdcaae78d
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Mon Oct 12 15:38:14 2020 -0700
+
+    Search UI clean up
+    
+    - Resolve spacing issue when work profile is installed
+    - Cache play icons and use icon shape
+    - Only draw focus indicator for the first result
+    
+    Bug: 170487752
+    Bug: 170665892
+    Change-Id: I864d2e796786637132e127ef9b418c0a76c74d6e
+
+commit 69740e62be3800fc918648009645f7a8e52cb73d
+Merge: 2d7bfc878 979da64d8
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Mon Oct 12 20:53:29 2020 +0000
+
+    Merge "Add app start source info of apps launched from launcher" into ub-launcher3-master
+
+commit 2d7bfc8782e9ed01178672aeb09ba2a6a07f4f4c
+Author: Jon Miranda <jonmiranda@google.com>
+Date:   Mon Oct 12 12:09:22 2020 -0700
+
+    Fix shadowRadius not being used in swipe up animation.
+    
+    Bug: 168608912
+    Change-Id: I08f7bb057237e5061d5f1fc29afb488b204ee385
+
+commit a433fe1fb34715efb38ed094f39da49fce8cd51e
+Merge: 2de606fe7 0471b9836
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date:   Mon Oct 12 18:08:35 2020 +0000
+
+    Merge "Using FrameCallbacks instead of windowCallbacks for surface removal" into ub-launcher3-master
+
+commit 9a6145efb85f2bbdaccc07166a55e22c15fe27db
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Mon Oct 12 09:33:00 2020 -0700
+
+    Introduce shortcut container for hotseat event reporting
+    
+    Bug: 170636685
+    Test: Manual
+    Change-Id: I5abeb17976bbafdc8cc74fb8b9a586d544c682fc
+
+commit 2de606fe731573c081fd2d6ba166e21ea6aa2e9c
+Author: Yogisha Dixit <ydixit@google.com>
+Date:   Mon Oct 12 15:36:07 2020 +0100
+
+    Delete the minimal database to force refresh.
+    
+    Bug: 169771796
+    Test: manual
+    Change-Id: Ic2188bb162f295c208346861fddc137ace19ddcb
+
+commit 0471b9836c9e382dc14bdc3abdf8502fb2b9f266
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date:   Wed Sep 23 13:54:37 2020 -0700
+
+    Using FrameCallbacks instead of windowCallbacks for surface removal
+    
+    WindowCallbacks is called during the draw pass, before the frame has
+    been sent to the surfaceFlinger. Frame callback will provide a closer
+    approximation for when the frame is actually rendered on screen.
+    
+    Bug: 141126144
+    Change-Id: I62aab526c2ca24b00b5e7b312b36080f26c7b439
+
+commit 2727434c44d06882925369bf4b43687a06be4a3f
+Merge: 59f532fe9 1b9e199b3
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date:   Fri Oct 9 20:09:08 2020 +0000
+
+    Merge "Fix hotseat and prediction row to allow updates when empty." into ub-launcher3-master
+
+commit 59f532fe9e2b1817c094641f3c7c517f42e4faf0
+Merge: d2bfce71f b5334e3f0
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Fri Oct 9 19:52:54 2020 +0000
+
+    Merge "Improve search section header" into ub-launcher3-master
+
+commit 979da64d8254599c332d83bf94f3f1fc3fe45fef
+Author: Riddle Hsu <riddlehsu@google.com>
+Date:   Tue Sep 22 21:52:40 2020 +0800
+
+    Add app start source info of apps launched from launcher
+    
+    Bug: 166614700
+    Test: Enable statsd log: "adb shell cmd stats print-logs"
+          adb logcat | grep statsd | grep "(48)"
+          The line may contain 0x100000->1[I] 0x110000->10[I]
+          that means 1=from launcher and 10=latency 10ms.
+    Change-Id: Iddaff7066b66e241ba58ec87129ddbe2c531dc7e
+    (cherry picked from commit 7bdf3574a3bff06a377b4364877687bfa7619d06)
+
+commit d2bfce71f776fd05633dfd915dfc664309274677
+Merge: ed4530fed 222afb970
+Author: Winson Chung <winsonc@google.com>
+Date:   Fri Oct 9 16:39:06 2020 +0000
+
+    Merge "Comply with the ISystemUiProxy.aidl change" into ub-launcher3-master
+
+commit ed4530fedda0bf876f91d0745fc70d0f30d42991
+Merge: 692d2109a 9d4a96ed0
+Author: Winson Chung <winsonc@google.com>
+Date:   Fri Oct 9 16:39:06 2020 +0000
+
+    Merge "Add latency metrics for recents gesture" into ub-launcher3-master
+
+commit 1b9e199b3d9c81c793758d96bb03e0c51c1b3fb1
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date:   Thu Oct 8 15:50:22 2020 -0400
+
+    Fix hotseat and prediction row to allow updates when empty.
+    
+    Rotating the screen in the homescreen empties the hotseat, however it does not get populated while it is visible to the user. The user should not be able to see an empty hotseat or prediction row if predictions are available. It should therefore be possible to populate these when they are empty even if they are visible to the user.
+    
+    Change-Id: I8e5252bd29050c2cd9d443aedcb3f3e305c0e2d7
+
+commit b5334e3f07f0561808a2d6e9bba55f1e3a89191e
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date:   Fri Oct 9 00:50:48 2020 -0700
+
+    Improve search section header
+    
+    Change-Id: I47cf207f0d0ab792c0e7a47c9d1185eec087ec88
+
+commit 692d2109a6702706d24b3b819d115882f7362509
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Thu Oct 8 18:42:48 2020 -0700
+
+    invalidate itemDecoration on predictedRow focus draw
+    
+    Change-Id: I66c731f00ae1c1292c51ff281957f05fd2d70dfa
+
+commit 8d5b118060bff7f7518a9a14c0be5d265621f14c
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Thu Oct 8 13:11:25 2020 -0700
+
+    Revert PredictionRow shuoldDraw check
+    
+    + Show Rounded play result icons
+    
+    Bug: 168805872
+    Test: Manual
+    Change-Id: I663c7f7ca1f1ac072e5e9c441deabef7c3fbd97b
+
+commit 86f8df6cf954ac27ab092b9ef8a4db3c9979c4cb
+Merge: 4d19854b2 16045060c
+Author: Hilary Huo <hhuo@google.com>
+Date:   Thu Oct 8 18:43:51 2020 +0000
+
+    Merge "[pixel-search] add escape hatch" into ub-launcher3-master
+
+commit 4d19854b25a54599fe9b0ac8be9d60cf6c21d7ba
+Merge: 0827e1e32 ab9ad20be
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Thu Oct 8 18:40:56 2020 +0000
+
+    Merge "Search UI cleanup" into ub-launcher3-master
+
+commit 16045060c35639aea85afc572bea768d16e6c9f9
+Author: Hilary Huo <hhuo@google.com>
+Date:   Thu Oct 8 10:18:41 2020 -0700
+
+    [pixel-search] add escape hatch
+    
+    Change-Id: I33ffea1fc0859564955380d7d1db317293d1a2cb
+
+commit 0827e1e32a5f99fa02418dae37270c6db8c989d2
+Merge: 3463f0a87 68d7a6e5b
+Author: Andy Wickham <awickham@google.com>
+Date:   Thu Oct 8 16:53:29 2020 +0000
+
+    Merge "Adds feature flag for BC Smartspace." into ub-launcher3-master
+
+commit ab9ad20be600d1cbdc6b54a491d5fbb4c2cf9c16
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Wed Oct 7 15:18:24 2020 -0700
+
+    Search UI cleanup
+    
+    - offset all apps header padding with search input margin
+    - avoid check shouldDraw check on HeaderRow. (race condition)
+    
+    Bug: 170263425
+    Change-Id: I11a1fbb448aa6afd18ec0984af9bb8b1d7600f69
+
+commit 68d7a6e5b28af8cc55bdae7efc24cc7ebee81257
+Author: Andy Wickham <awickham@google.com>
+Date:   Wed Oct 7 14:27:17 2020 -0700
+
+    Adds feature flag for BC Smartspace.
+    
+    Change-Id: Iaf9fb7507d0ccd004a4e00188c75dadd6a059246
+
+commit 3463f0a876ff486ce03e160134e0504158271a92
+Merge: 2470d812a 4b7f38b8f
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Wed Oct 7 20:09:04 2020 +0000
+
+    Merge "Align fallback result query with result text" into ub-launcher3-master
+
+commit 2470d812a1ae989e67781e5056b534ad9a960819
+Merge: cae7d74d8 7a6e4c931
+Author: Vadim Tryshev <vadimt@google.com>
+Date:   Wed Oct 7 20:04:09 2020 +0000
+
+    Merge "Annotating Quick Switch CUJ for 3-button mode" into ub-launcher3-master
+
+commit cae7d74d898769727105850ea5473c2c0ae25fdb
+Merge: e9bf2bd14 1fddddb4f
+Author: Tony Wickham <twickham@google.com>
+Date:   Wed Oct 7 18:32:48 2020 +0000
+
+    Merge "Update launcher_trace.proto for quick switch" into ub-launcher3-master
+
+commit 7a6e4c931f13b369bfa4328196b4632d6d848a19
+Author: vadimt <vadimt@google.com>
+Date:   Tue Oct 6 14:09:16 2020 -0700
+
+    Annotating Quick Switch CUJ for 3-button mode
+    
+    Bug: 169221288
+    Change-Id: Ief62345fe6004dde699f44aa0c90329b7cd84e8b
+
+commit 4b7f38b8fa004b514244304fcc07ff514a2fa46b
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Tue Oct 6 18:37:46 2020 -0700
+
+    Align fallback result query with result text
+    
+    screenshot: https://screenshot.googleplex.com/6Daj5vdmz2jmznX
+    bug: 169438169
+    test: Manual
+    Change-Id: Ie621ed3c834aec5e9467607da4f685d05d152183
+
+commit 222afb970434c7972589adfc509bd2c256ca6556
+Author: Hongwei Wang <hwwang@google.com>
+Date:   Fri Oct 2 13:51:36 2020 -0700
+
+    Comply with the ISystemUiProxy.aidl change
+    
+    Two methods are added to support communications between Launcher and
+    SysUI when user swipes an auto PiP-able Activity to home.
+    
+    Bug: 143965596
+    Test: N/A
+    Change-Id: I2c73a287a094e882bde3cd71c27f9f66ae20e64a
+    (cherry picked from commit 88ddae38db924f700082a113670ce5a719116a95)
+
+commit 9d4a96ed029fdad1e369d5eedd082938f0dc9e01
+Author: Riddle Hsu <riddlehsu@google.com>
+Date:   Wed Sep 30 00:32:04 2020 +0800
+
+    Add latency metrics for recents gesture
+    
+    Pass the touch down time to RecentsAnimation#startRecentsActivity.
+    
+    Bug: 169221287
+    Test: Enable statsd log: "adb shell cmd stats print-logs"
+          Touch gesture navigation bar.
+          adb logcat | grep statsd | grep "(48)"
+          The line may contain 0x100000->4[I] 0x110000->20[I]
+          that means 4=by recents and 20=latency 20ms.
+    Change-Id: I81ee804895b7712f4d925736f5b4694c11a12cbe
+    (cherry picked from commit 63623967b83edad56db58173ebb6687c685b9177)
+
+commit e9bf2bd14c9a7a48f8f93687932d41b1418cf4e4
+Merge: 73ae75474 d028937e7
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Wed Oct 7 02:19:04 2020 +0000
+
+    Merge "[Live tile] Finish recents animation when the phone goes to sleep in live tile mode" into ub-launcher3-master
+
+commit 1fddddb4f30505e0fc9bb2e7c0d88b38ad900e54
+Author: Tony Wickham <twickham@google.com>
+Date:   Tue Sep 29 17:29:06 2020 -0700
+
+    Update launcher_trace.proto for quick switch
+    
+    Sample output from one entry:
+    entry {
+      elapsed_realtime_nanos: 440461382888540
+      launcher {
+        touch_interaction_service {
+          service_connected: true
+          overview_component_obvserver {
+            overview_activity_started: true
+            overview_activity_resumed: false
+          }
+          input_consumer {
+            name: "TYPE_OTHER_ACTIVITY:TYPE_ONE_HANDED"
+            swipe_handler {
+              gesture_state {
+                endTarget: NEW_TASK
+              }
+              is_recents_attached_to_app_window: true
+              scroll_offset: 846
+              app_to_overview_progress: 0
+            }
+          }
+        }
+      }
+    }
+    
+    Bug: 167259591
+    Change-Id: I7f199d88f1d736efcea6b9165b8c4b77a5d27c58
+
+commit 73ae75474ec1dd8807d814ea6c22323905d2070c
+Merge: 8a6f3e40d 0ebbc1880
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Tue Oct 6 23:18:44 2020 +0000
+
+    Merge "Removing tracing for a gone flake" into ub-launcher3-master
+
+commit 8a6f3e40d0321217c624055db7929c397e455e0c
+Merge: e29a9f796 565ed4ff6
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Tue Oct 6 22:49:40 2020 +0000
+
+    Merge "Update Search UI" into ub-launcher3-master
+
+commit 0ebbc18803aaf8ef2f6db7d628d7ae1ce322e842
+Author: vadimt <vadimt@google.com>
+Date:   Tue Oct 6 14:52:27 2020 -0700
+
+    Removing tracing for a gone flake
+    
+    Bug: 156044202
+    Change-Id: Ice142bb941fee7b731f46c2073fab17d83bbc871
+
+commit 565ed4ff69b534812818a2b9aa8789a1aea210eb
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Wed Sep 30 10:42:07 2020 -0700
+
+    Update Search UI
+    
+    [preview attached to bug]
+    
+    Bug: 169438169
+    Test: Manual
+    Change-Id: I085f3dd38ac373c1afab82a637ec08715a6e0cc5
+
+commit e29a9f7961e6db0915bc028ef7e871dcb2c8bde0
+Merge: 2c5ed10ff be17bdcd2
+Author: Jayaprakash Sundararaj <jayaprakashs@google.com>
+Date:   Tue Oct 6 21:00:20 2020 +0000
+
+    Merge "[Search] Add logging to People and badding as to icons." into ub-launcher3-master
+
+commit be17bdcd221f501c45876abe2249c1007858d0c0
+Author: jayaprakashs <jayaprakashs@google.com>
+Date:   Mon Oct 5 09:01:52 2020 -0700
+
+    [Search] Add logging to People and badding as to icons.
+    
+    Change-Id: I65948a2faca436216a94aa46139d425b8eade827
+
+commit 2c5ed10ffa1a870de35f9b3c0c558270aff498dd
+Merge: b2b65a1ef 8ed9707cf
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Tue Oct 6 18:40:57 2020 +0000
+
+    Merge "[Live Tile] Support launching another task (other than the current running task) in Overview" into ub-launcher3-master
+
+commit b2b65a1ef58b020923d112051535b6eb83b582df
+Merge: 3cf264f49 4c14f4b9e
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Tue Oct 6 17:45:35 2020 +0000
+
+    Merge "Avoid double search item highlight" into ub-launcher3-master
+
+commit 8ed9707cf3a4300cb61942f08f0752c80eed086b
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Mon Sep 14 23:25:37 2020 -0700
+
+    [Live Tile] Support launching another task (other than the current running task) in Overview
+    
+    - Get rid of the defer cancelation logic
+    - Render animation on the task view of the task being launched upon task view appeared callback
+    - Finish the recents animation upon the end of the recents window animation
+    
+    Fixes: 164926736
+    Test: manual
+    Change-Id: Ibffb6a9c74c235efc8615a22b0306551532c7b61
+
+commit 3cf264f498e37c482fa4c559bf48ffa791279585
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date:   Tue Sep 22 12:58:38 2020 -0700
+
+    Prevent hotseat updates if it is visible to the user.
+    
+    Test: manual
+    
+    Fixes: 168653219
+    
+    Changing app icons under the user's finger could be disruptive. Added a checks for whether the hotseatand all apps predictions are visible and callbacks to update them when they become hidden.
+    
+    Change-Id: Ib9e6e904e9f662ecfaeea6a2fe21d1d81ba39b96
+
+commit b6aff1f56d55a36256446ec3970d92e9da39b98c
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date:   Mon Oct 5 16:08:35 2020 -0700
+
+    Fix NPE inside RecentsOrientedState
+    
+    Bug: 169963211
+    Change-Id: I86dd337dc1b862f3fa99b91b47fa250076233f96
+
+commit eab40983b9a48b933bde5ca95a82ebd4d83b233d
+Merge: 83ce7c0b5 020e628f2
+Author: Jonathan Miranda <jonmiranda@google.com>
+Date:   Mon Oct 5 22:20:27 2020 +0000
+
+    Merge "Add shadow radius to windows during app launch / close animations." into ub-launcher3-master
+
+commit 83ce7c0b5e461386bb92883a8d6cefe8365cd9ae
+Merge: 679d920bf d6b1f3c08
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Mon Oct 5 19:18:39 2020 +0000
+
+    Merge "Action icon should be used as a badge instead of main icon" into ub-launcher3-master
+
+commit 679d920bf5151cffed4e8186c12c25d8d7907af9
+Merge: e108cc609 0c943966d
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Mon Oct 5 19:13:50 2020 +0000
+
+    Merge "Add null check for input receiver before updating batching" into ub-launcher3-master
+
+commit e108cc609d0a7fd58f0c7e16ce45fa79be6dd272
+Merge: 470403eb5 f622e42bf
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Mon Oct 5 18:39:58 2020 +0000
+
+    Merge "Removing unused proto extensions" into ub-launcher3-master
+
+commit 470403eb58879380e2edac2262dc7f40327b2a15
+Merge: a5130482a 1d7ed30db
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Mon Oct 5 18:29:54 2020 +0000
+
+    Merge "Remove widgets that no longer fit the workspace in their current spans." into ub-launcher3-master
+
+commit 4c14f4b9eda8332347c81e0cf51c5de4dbc06399
+Author: Samuel Fufa <sfufa@google.com>
+Date:   Mon Oct 5 10:50:00 2020 -0700
+
+    Avoid double search item highlight
+    
+    Change-Id: Ic2e28b18f6d5e3ed32cd5646bc3bb4789c378e57
+
+commit 0c943966d373d8ae7eef2b08e88ac44bf57d8a8d
+Author: Winson Chung <winsonc@google.com>
+Date:   Mon Oct 5 10:23:27 2020 -0700
+
+    Add null check for input receiver before updating batching
+    
+    - A change in the system (ie. sysui crash or nav mode change) could
+      cause the input monitor to be disposed before the swipe animation
+      settles
+    
+    Bug: 170121063
+    Test: Kill sysui while swiping up
+    
+    Change-Id: I1417b109fecdb98fae6197c7038dbe9307470853
+
+commit a5130482aee1b0592661bc1c6e178a0de7a163da
+Merge: b21819e18 7fcd74abb
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Mon Oct 5 17:14:21 2020 +0000
+
+    Merge "Suggest result should launch Bug: 169980192" into ub-launcher3-master
+
+commit d028937e74a9ea6d36e463de4c87ed37283bbdf6
+Author: Tracy Zhou <tracyzhou@google.com>
+Date:   Sat Oct 3 00:36:53 2020 -0700
+
+    [Live tile] Finish recents animation when the phone goes to sleep in live tile mode
+    
+    Fixes: 169988381
+    Test: manual
+    Change-Id: Ic71d3e6767eadb6854dbd46581bf9d3242c161a4
+
+commit 7fcd74abb399100ac8243be6ca28c09cc8adc8c8
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date:   Fri Oct 2 19:20:11 2020 -0700
+
+    Suggest result should launch
+    Bug: 169980192
+    
+    Change-Id: I762245a5cc4740d093c9cb3b44a508e9e3f2b763
+
+commit b21819e181e99504c22c6ca028261a1f2665c6f9
+Merge: 931bce369 a762b0241
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Fri Oct 2 22:07:12 2020 +0000
+
+    Merge "Annotating Quick Switch CUJ for non-3-button modes" into ub-launcher3-master
+
+commit a762b02418695f5a1ff2f96586660de8c3610280
+Author: vadimt <vadimt@google.com>
+Date:   Fri Oct 2 13:56:28 2020 -0700
+
+    Annotating Quick Switch CUJ for non-3-button modes
+    
+    Bug: 169221288
+    Change-Id: I7145a9e28a2f0a789d19d2a0e3d15630c6e50f6a
+
+commit 931bce3697595a214023bc72923dad47a61d5711
+Merge: c935ba6b8 733e3c609
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date:   Fri Oct 2 19:19:50 2020 +0000
+
+    Merge "Moving some initializations to the background thread" into ub-launcher3-master
+
+commit c935ba6b8a2ec163533c0b19309dacb6199e6552
+Merge: a4111f250 58804ac52
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date:   Fri Oct 2 18:26:06 2020 +0000
+
+    Merge "Adding stats log for add item flow" into ub-launcher3-master
+
+commit 733e3c609b7653a36e58747c881458ec00d98df8
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date:   Tue Sep 29 10:32:32 2020 -0700
+
+    Moving some initializations to the background thread
+    
+    HandlerThread.getLooper blocks until the thread is ready. Instead
+    moving all looper dependency to the new thread itself.
+    
+    Change-Id: I240e8c56b855a991433a7fe93875059e6dab146b
+
+commit 58804ac5257f45dddbf7a6db35cf8f369ee1e88e
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date:   Wed Sep 16 16:27:40 2020 -0700
+
+    Adding stats log for add item flow
+    
+    Bug: 169385783
+    Bug: 168167693
+    Change-Id: I37395f1b118727f67e0f14c02f945b8213b165c8
+
+commit a4111f250003328d1aef8bbaab59512208ec46cb
+Merge: 8d14dbe04 f6b72c4ad
+Author: Hilary Huo <hhuo@google.com>
+Date:   Fri Oct 2 17:41:22 2020 +0000
+
+    Merge "[pixel-search] Bug fix: automatically launch screenshot + center&crop remoteaction icon" into ub-launcher3-master
+
+commit f622e42bf6983d3adb95386bfd6375d281f1d4f2
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date:   Fri Oct 2 10:35:56 2020 -0700
+
+    Removing unused proto extensions
+    
+    Change-Id: I6d0319c99934dad5176b6f70b895a4ca772ec45f
+
+commit d6b1f3c086f9ac097cd03e1ee898b153478ec11a
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date:   Fri Oct 2 00:26:35 2020 -0700
+
+    Action icon should be used as a badge instead of main icon
+    
+    Bug: 169796517
+    Change-Id: I3f07fdc2ae6e1af463701f942c26c3ca5d836ee2
+
+commit f6b72c4ad1d2e082441a64c4d6a5a02ee8a251ca
+Author: Hilary Huo <hhuo@google.com>
+Date:   Thu Oct 1 12:26:48 2020 -0700
+
+    [pixel-search] Bug fix: automatically launch screenshot + center&crop remoteaction icon
+    
+    Bug: b/169330678
+    Change-Id: Id5f8a0ce6d68f7ed9e4d1ff258ee3772229eb63b
+
+commit 1d7ed30dba4b2c71fc7b0981532a872a13e5aedb
+Author: Jon Miranda <jonmiranda@google.com>
+Date:   Wed Sep 23 12:15:43 2020 -0700
+
+    Remove widgets that no longer fit the workspace in their current spans.
+    
+    This can happen when display size changes.
+    We compare span sizes of widget in the db to the min sizes of the widget
+    in the current display size. If the widget can no longer fit in its existing
+    spans, we remove it.
+    
+    Also update test widgets to have minWidth/minHeight of 1dp. This ensures that
+    the spanX, spanY, min* values remain consistent between different test devices.
+    
+    Bug: 168818961
+    Change-Id: I723372e4582658f78b2f23ced9073cb77977a6b8
+
+commit 020e628f22cc7975beab439c6da26af2f9ebc15b
+Author: Jon Miranda <jonmiranda@google.com>
+Date:   Mon Sep 28 17:01:42 2020 -0700
+
+    Add shadow radius to windows during app launch / close animations.
+    
+    Bug: 168608912
+    Change-Id: I2ec50b0b3711c0861659f9c641bbc05fcdeaab45
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index e649ce1..72b8d3f 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -16,16 +16,11 @@
 
 package com.android.launcher3.testing;
 
-import static android.graphics.Bitmap.Config.ARGB_8888;
-
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Debug;
 import android.system.Os;
 import android.view.View;
 
@@ -106,40 +101,15 @@
                 return response;
             }
 
-            case TestProtocol.REQUEST_TOTAL_PSS_KB: {
+            case TestProtocol.REQUEST_FORCE_GC: {
                 runGcAndFinalizersSync();
-                Debug.MemoryInfo mem = new Debug.MemoryInfo();
-                Debug.getMemoryInfo(mem);
-                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss());
-                return response;
-            }
-
-            case TestProtocol.REQUEST_JAVA_LEAK: {
-                if (sLeaks == null) sLeaks = new LinkedList();
-
-                // Allocate and dirty the memory.
-                final int leakSize = 1024 * 1024;
-                final byte[] bytes = new byte[leakSize];
-                for (int i = 0; i < leakSize; i += 239) {
-                    bytes[i] = (byte) (i % 256);
-                }
-                sLeaks.add(bytes);
-                return response;
-            }
-
-            case TestProtocol.REQUEST_NATIVE_LEAK: {
-                if (sLeaks == null) sLeaks = new LinkedList();
-
-                // Allocate and dirty a bitmap.
-                final Bitmap bitmap = Bitmap.createBitmap(512, 512, ARGB_8888);
-                bitmap.eraseColor(Color.RED);
-                sLeaks.add(bitmap);
                 return response;
             }
 
             case TestProtocol.REQUEST_VIEW_LEAK: {
                 if (sLeaks == null) sLeaks = new LinkedList();
                 sLeaks.add(new View(mContext));
+                sLeaks.add(new View(mContext));
                 return response;
             }
 
@@ -164,6 +134,12 @@
             }
 
             case TestProtocol.REQUEST_GET_TEST_EVENTS: {
+                if (sEvents == null) {
+                    // sEvents can be null if Launcher died and restarted after
+                    // REQUEST_START_EVENT_LOGGING.
+                    return response;
+                }
+
                 synchronized (sEvents) {
                     response.putStringArrayList(
                             TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
diff --git a/go/AndroidManifest-launcher.xml b/go/AndroidManifest-launcher.xml
new file mode 100644
index 0000000..6a8f715
--- /dev/null
+++ b/go/AndroidManifest-launcher.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.launcher3">
+    <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+    <!--
+    Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
+    Refer comments around specific entries on how to extend individual components.
+    -->
+
+    <application
+        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+        android:fullBackupOnly="true"
+        android:fullBackupContent="@xml/backupscheme"
+        android:hardwareAccelerated="true"
+        android:icon="@drawable/ic_launcher_home"
+        android:label="@string/derived_app_name"
+        android:theme="@style/AppTheme"
+        android:largeHeap="@bool/config_largeHeap"
+        android:restoreAnyVersion="true"
+        android:supportsRtl="true" >
+
+        <!--
+        Main launcher activity. When extending only change the name, and keep all the
+        attributes and intent filters the same
+        -->
+        <activity
+            android:name="com.android.launcher3.Launcher3QuickStepGo"
+            android:launchMode="singleTask"
+            android:clearTaskOnLaunch="true"
+            android:stateNotNeeded="true"
+            android:windowSoftInputMode="adjustPan"
+            android:screenOrientation="unspecified"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density|uiMode"
+            android:resizeableActivity="true"
+            android:resumeWhilePausing="true"
+            android:taskAffinity=""
+            android:exported="true"
+            android:enabled="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.HOME" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.MONKEY"/>
+                <category android:name="android.intent.category.LAUNCHER_APP" />
+            </intent-filter>
+            <meta-data
+                android:name="com.android.launcher3.grid.control"
+                android:value="${packageName}.grid_control" />
+        </activity>
+
+    </application>
+</manifest>
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index f84a82e..2671604 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -24,6 +24,8 @@
 
     <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
 
+    <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
+
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
         android:fullBackupOnly="true"
@@ -46,6 +48,12 @@
             tools:node="replace" >
         </activity>
 
+        <service
+            android:name="com.android.launcher3.notification.NotificationListener"
+            android:label="@string/notification_dots_service_title"
+            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+            android:enabled="false"
+            tools:node="replace" />
     </application>
 
 </manifest>
diff --git a/go/quickstep/res/drawable/arrow_toast_rounded_background.xml b/go/quickstep/res/drawable/arrow_toast_rounded_background.xml
new file mode 100644
index 0000000..9c815fd
--- /dev/null
+++ b/go/quickstep/res/drawable/arrow_toast_rounded_background.xml
@@ -0,0 +1,19 @@
+<!--
+    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/arrow_tip_view_bg" />
+    <corners android:radius="@dimen/tooltip_corner_radius" />
+</shape>
diff --git a/go/quickstep/res/drawable/ic_listen.xml b/go/quickstep/res/drawable/ic_listen.xml
new file mode 100644
index 0000000..16bb597
--- /dev/null
+++ b/go/quickstep/res/drawable/ic_listen.xml
@@ -0,0 +1,24 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M15.08,7.05c0.84,1.18 0.84,2.71 0,3.89l1.68,1.69c2.02,-2.02 2.02,-5.07 0,-7.27l-1.68,1.69zM20.07,2l-1.63,1.63c2.77,3.02 2.77,7.56 0,10.74L20.07,16c3.9,-3.89 3.91,-9.95 0,-14zM9,13c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM9,7c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zM9,14c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4zM15,19L3,19v-0.99C3.2,17.29 6.3,16 9,16s5.8,1.29 6,2v1z"/>
+</vector>
diff --git a/go/quickstep/res/drawable/ic_search.xml b/go/quickstep/res/drawable/ic_search.xml
new file mode 100644
index 0000000..ce290d9
--- /dev/null
+++ b/go/quickstep/res/drawable/ic_search.xml
@@ -0,0 +1,36 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M16,3h-2v2h2c1.65,0 3,1.35 3,3v2h2V8C21,5.24 18.76,3 16,3z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M5,16v-2H3v2c0,2.76 2.24,5 5,5h2v-2H8C6.35,19 5,17.65 5,16z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12,8.5c-1.93,0 -3.5,1.57 -3.5,3.5s1.57,3.5 3.5,3.5s3.5,-1.57 3.5,-3.5S13.93,8.5 12,8.5z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M18,18m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M5,8c0,-1.65 1.35,-3 3,-3h2V3H8C5.24,3 3,5.24 3,8v2h2V8z"/>
+</vector>
diff --git a/go/quickstep/res/drawable/ic_translate.xml b/go/quickstep/res/drawable/ic_translate.xml
new file mode 100644
index 0000000..e3df56f
--- /dev/null
+++ b/go/quickstep/res/drawable/ic_translate.xml
@@ -0,0 +1,24 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z"/>
+</vector>
diff --git a/go/res/values/dimens.xml b/go/quickstep/res/drawable/round_rect_button.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to go/quickstep/res/drawable/round_rect_button.xml
index f1b1053..b276686 100644
--- a/go/res/values/dimens.xml
+++ b/go/quickstep/res/drawable/round_rect_button.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners android:radius="@dimen/go_button_corner_radius" />
+</shape>
diff --git a/go/res/values/dimens.xml b/go/quickstep/res/drawable/round_rect_dialog.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to go/quickstep/res/drawable/round_rect_dialog.xml
index f1b1053..bbb7c5b 100644
--- a/go/res/values/dimens.xml
+++ b/go/quickstep/res/drawable/round_rect_dialog.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners android:radius="@dimen/modal_dialog_corner_radius" />
+</shape>
diff --git a/go/quickstep/res/layout/niu_actions_dialog.xml b/go/quickstep/res/layout/niu_actions_dialog.xml
new file mode 100644
index 0000000..6e944f8
--- /dev/null
+++ b/go/quickstep/res/layout/niu_actions_dialog.xml
@@ -0,0 +1,103 @@
+<?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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/niu_actions_dialog_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:layout_gravity="center">
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="1dp"
+        android:layout_weight="1"/>
+
+    <LinearLayout
+        android:layout_width="@dimen/modal_dialog_width"
+        android:layout_height="wrap_content"
+        android:background="@drawable/round_rect_dialog"
+        android:backgroundTint="?attr/modalDialogBackground"
+        android:orientation="vertical"
+        android:layout_gravity="center"
+        android:paddingTop="@dimen/modal_dialog_padding"
+        android:paddingLeft="@dimen/modal_dialog_padding"
+        android:paddingRight="@dimen/modal_dialog_padding"
+        android:paddingBottom="@dimen/modal_dialog_padding_bottom">
+
+        <TextView
+            style="@style/ModalDialogTitle"
+            android:id="@+id/niu_actions_dialog_header"/>
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="@dimen/modal_dialog_vertical_spacer"/>
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ScrollView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constrainedHeight="true"
+                app:layout_constraintHeight_max="@dimen/modal_dialog_text_height">
+
+                <TextView
+                    style="@style/ModalDialogText"
+                    android:id="@+id/niu_actions_dialog_description"/>
+            </ScrollView>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="@dimen/modal_dialog_vertical_spacer"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1"/>
+
+            <Button
+                style="@style/ModalDialogButton"
+                android:id="@+id/niu_actions_dialog_button_1"
+                android:text="@string/dialog_cancel"/>
+
+            <Button
+                style="@style/ModalDialogButton"
+                android:id="@+id/niu_actions_dialog_button_2"
+                android:text="@string/dialog_acknowledge"/>
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="1dp"
+        android:layout_weight="1" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/go/quickstep/res/layout/overview_actions_container.xml b/go/quickstep/res/layout/overview_actions_container.xml
new file mode 100644
index 0000000..0e718ca
--- /dev/null
+++ b/go/quickstep/res/layout/overview_actions_container.xml
@@ -0,0 +1,119 @@
+<?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 / 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">
+
+    <LinearLayout
+        android:id="@+id/action_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="top|center_horizontal"
+        android:orientation="horizontal">
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="1" />
+
+        <LinearLayout
+            android:id="@+id/layout_translate"
+            style="@style/GoOverviewActionButtonContainer">
+            <ImageButton
+                android:id="@+id/action_translate"
+                style="@style/GoOverviewActionButton"
+                android:src="@drawable/ic_translate"
+                android:contentDescription="@string/action_translate" />
+            <TextView
+                style="@style/GoOverviewActionButtonCaption"
+                android:text="@string/action_translate" />
+        </LinearLayout>
+
+        <Space
+            android:id="@+id/spacer_translate"
+            android:layout_width="@dimen/go_overview_button_container_margin"
+            android:layout_height="1dp" />
+
+        <LinearLayout
+            android:id="@+id/layout_listen"
+            style="@style/GoOverviewActionButtonContainer">
+            <ImageButton
+                android:id="@+id/action_listen"
+                style="@style/GoOverviewActionButton"
+                android:src="@drawable/ic_listen"
+                android:contentDescription="@string/action_listen"
+                android:background="@drawable/round_rect_button" />
+            <TextView
+                style="@style/GoOverviewActionButtonCaption"
+                android:text="@string/action_listen" />
+        </LinearLayout>
+
+        <Space
+            android:id="@+id/spacer_listen"
+            android:layout_width="@dimen/go_overview_button_container_margin"
+            android:layout_height="1dp" />
+
+        <LinearLayout
+            android:id="@+id/layout_screenshot"
+            style="@style/GoOverviewActionButtonContainer">
+            <ImageButton
+                android:id="@+id/action_screenshot"
+                style="@style/GoOverviewActionButton"
+                android:src="@drawable/ic_screenshot"
+                android:contentDescription="@string/action_screenshot"
+                android:background="@drawable/round_rect_button" />
+            <TextView
+                style="@style/GoOverviewActionButtonCaption"
+                android:text="@string/action_screenshot" />
+        </LinearLayout>
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="1dp"
+            android:layout_weight="1" />
+
+        <!-- Will be enabled in a future version. -->
+        <LinearLayout
+            style="@style/GoOverviewActionButtonContainer"
+            android:visibility="gone" >
+            <ImageButton
+                android:id="@+id/action_search"
+                style="@style/GoOverviewActionButton"
+                android:src="@drawable/ic_search"
+                android:contentDescription="@string/action_search"
+                android:background="@drawable/round_rect_button" />
+            <TextView
+                style="@style/GoOverviewActionButtonCaption"
+                android:text="@string/action_search" />
+        </LinearLayout>
+
+        <!-- Unused. Included only for compatibility with parent class. -->
+        <Button
+            android:id="@+id/action_share"
+            style="@style/GoOverviewActionButton"
+            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" />
+    </LinearLayout>
+
+</com.android.quickstep.views.GoOverviewActionsView>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/go/quickstep/res/values-land/dimens.xml
similarity index 76%
copy from go/res/values/dimens.xml
copy to go/quickstep/res/values-land/dimens.xml
index f1b1053..6b5d0b4 100644
--- a/go/res/values/dimens.xml
+++ b/go/quickstep/res/values-land/dimens.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
@@ -15,6 +16,7 @@
 -->
 
 <resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+    <!-- Modal Dialogs -->
+    <dimen name="modal_dialog_width">360dp</dimen>
+    <dimen name="modal_dialog_text_height">168dp</dimen>
+</resources>
diff --git a/go/res/values/dimens.xml b/go/quickstep/res/values/attrs.xml
similarity index 62%
copy from go/res/values/dimens.xml
copy to go/quickstep/res/values/attrs.xml
index f1b1053..cdbdc2a 100644
--- a/go/res/values/dimens.xml
+++ b/go/quickstep/res/values/attrs.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
+    <!-- Attributes used for Overview theming -->
+    <attr name="overviewButtonTextColor" format="color" />
+    <attr name="overviewButtonIconColor" format="color" />
+    <attr name="overviewButtonBackgroundColor" format="color" />
+    <!-- Modal dialog theming -->
+    <attr name="modalDialogBackground" format="color" />
 </resources>
\ No newline at end of file
diff --git a/go/quickstep/res/values/colors.xml b/go/quickstep/res/values/colors.xml
new file mode 100644
index 0000000..4ce7669
--- /dev/null
+++ b/go/quickstep/res/values/colors.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.
+-->
+<resources>
+    <!-- Overview -->
+    <color name="go_overview_text_color">#3C4043</color>
+    <color name="go_overview_text_color_dark">#F8F9FA</color>
+    <color name="go_overview_button_color">#70FFFFFF</color>
+    <color name="go_overview_button_color_dark">#474747</color>
+    <!-- Modal Dialogs -->
+    <color name="go_modal_dialog_background">#FFFFFF</color>
+    <color name="go_modal_dialog_background_dark">#424242</color>
+
+    <!-- Tooltip Color -->
+    <color name="arrow_tip_view_bg">#1A73E8</color>
+    <color name="arrow_tip_view_content">#FFFFFF</color>
+</resources>
diff --git a/quickstep/recents_ui_overrides/res/values/config.xml b/go/quickstep/res/values/config.xml
similarity index 68%
rename from quickstep/recents_ui_overrides/res/values/config.xml
rename to go/quickstep/res/values/config.xml
index 120e034..796d14d 100644
--- a/quickstep/recents_ui_overrides/res/values/config.xml
+++ b/go/quickstep/res/values/config.xml
@@ -14,5 +14,11 @@
      limitations under the License.
 -->
 <resources>
-    <integer name="max_depth_blur_radius">150</integer>
+    <!-- The component to receive app sharing Intents -->
+    <string name="app_sharing_component" translatable="false"/>
+
+    <!-- Feature Flags -->
+    <bool name="enable_niu_actions">true</bool>
+
+    <string name="task_overlay_factory_class" translatable="false">com.android.quickstep.TaskOverlayFactoryGo</string>
 </resources>
\ No newline at end of file
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
new file mode 100644
index 0000000..84d7fe5
--- /dev/null
+++ b/go/quickstep/res/values/dimens.xml
@@ -0,0 +1,43 @@
+<?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="go_button_corner_radius">20dp</dimen>
+
+    <!-- Overview -->
+    <dimen name="go_overview_button_width">60dp</dimen>
+    <dimen name="go_overview_button_height">60dp</dimen>
+    <dimen name="go_overview_button_container_width">80dp</dimen>
+    <dimen name="go_overview_button_container_margin">16dp</dimen>
+    <dimen name="go_overview_button_caption_margin">8dp</dimen>
+    <dimen name="overview_actions_height">96dp</dimen>
+    <dimen name="overview_proactive_row_height">0dp</dimen>
+    <dimen name="overview_proactive_row_bottom_margin">24dp</dimen>
+    <dimen name="task_corner_radius_override">28dp</dimen>
+
+    <!-- Modal Dialogs -->
+    <dimen name="modal_dialog_width">288dp</dimen>
+    <dimen name="modal_dialog_padding">24dp</dimen>
+    <dimen name="modal_dialog_padding_bottom">8dp</dimen>
+    <dimen name="modal_dialog_vertical_spacer">12dp</dimen>
+    <dimen name="modal_dialog_corner_radius">8dp</dimen>
+    <dimen name="modal_dialog_text_height">216dp</dimen>
+
+    <!-- Tooltip -->
+    <dimen name="tooltip_corner_radius">8dp</dimen>
+    <dimen name="tooltip_top_margin">3dp</dimen>
+</resources>
diff --git a/go/quickstep/res/values/strings.xml b/go/quickstep/res/values/strings.xml
new file mode 100644
index 0000000..8429f6a
--- /dev/null
+++ b/go/quickstep/res/values/strings.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Label for app share drop target. [CHAR_LIMIT=20] -->
+    <string name="app_share_drop_target_label">Share App</string>
+
+    <!-- ******* Overview ******* -->
+    <!-- Label for a button that lets the user listen to the content of the current app. [CHAR_LIMIT=40] -->
+    <string name="action_listen">Listen</string>
+    <!-- Label for a button that translates a screenshot of the current app. [CHAR_LIMIT=40] -->
+    <string name="action_translate">Translate</string>
+    <!-- Label for a button that triggers Search on a screenshot of the current app. [CHAR_LIMIT=40] -->
+    <string name="action_search">Lens</string>
+
+    <!-- ******* Dialogs ******* -->
+    <!-- Label for a button that acknowledges the contents of the dialog. [CHAR_LIMIT=40] -->
+    <string name="dialog_acknowledge">GOT IT</string>
+    <!-- Label for a button that cancels the dialog. [CHAR_LIMIT=40] -->
+    <string name="dialog_cancel">CANCEL</string>
+    <!-- Label for a button that redirects the user to Settings. [CHAR_LIMIT=40] -->
+    <string name="dialog_settings">SETTINGS</string>
+
+    <!-- ******* NIU Actions First-Run Confirmation Dialog ******* -->
+    <!-- Dialog title -->
+    <string name="niu_actions_confirmation_title">Translate or listen to text on screen</string>
+    <!-- Dialog content -->
+    <string name="niu_actions_confirmation_text">Information such as text on your screen, web addresses, and screenshots may be shared with Google.\n\nTo change what information you share, go to <b>Settings > Apps > Default apps > Digital assistant app</b>.</string>
+
+    <!-- ******* NIU Actions Default Assistant Error Dialogs ******* -->
+    <!-- Assistant not selected: Dialog title -->
+    <string name="assistant_not_selected_title">Choose an assistant to use this feature</string>
+    <!-- Assistant not selected: Dialog content -->
+    <string name="assistant_not_selected_text">To listen to or translate text on your screen, choose a digital assistant app in Settings</string>
+    <!-- Assistant not supported: Dialog title -->
+    <string name="assistant_not_supported_title">Change your assistant to use this feature</string>
+    <!-- Assistant not supported: Dialog content -->
+    <string name="assistant_not_supported_text">To listen to or translate text on your screen, change your digital assistant app in Settings</string>
+
+    <!-- ******* NIU Actions Tooltip ******* -->
+    <!-- Tooltip to highlight and explain the Listen button -->
+    <string name="tooltip_listen">Tap here to listen to text on this screen</string>
+    <!-- Tooltip to highlight and explain the Translate button -->
+    <string name="tooltip_translate">Tap here to translate text on this screen</string>
+</resources>
diff --git a/go/quickstep/res/values/styles.xml b/go/quickstep/res/values/styles.xml
new file mode 100644
index 0000000..442c413
--- /dev/null
+++ b/go/quickstep/res/values/styles.xml
@@ -0,0 +1,90 @@
+<?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>
+    <!-- App themes -->
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+        <item name="overviewButtonTextColor">@color/go_overview_text_color</item>
+        <item name="overviewButtonIconColor">@color/go_overview_text_color</item>
+        <item name="overviewButtonBackgroundColor">@color/go_overview_button_color</item>
+        <item name="modalDialogBackground">@color/go_modal_dialog_background</item>
+    </style>
+
+    <style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
+        <item name="overviewButtonTextColor">@color/go_overview_text_color_dark</item>
+        <item name="overviewButtonIconColor">@color/go_overview_text_color_dark</item>
+        <item name="overviewButtonBackgroundColor">@color/go_overview_button_color_dark</item>
+        <item name="modalDialogBackground">@color/go_modal_dialog_background_dark</item>
+    </style>
+
+    <!-- Overview -->
+    <style name="GoOverviewActionButton">
+        <item name="android:tint">?attr/overviewButtonIconColor</item>
+        <item name="android:backgroundTint">?attr/overviewButtonBackgroundColor</item>
+        <item name="android:background">@drawable/round_rect_button</item>
+        <item name="android:layout_width">@dimen/go_overview_button_width</item>
+        <item name="android:layout_height">@dimen/go_overview_button_height</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+
+    <style name="GoOverviewActionButtonCaption">
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textSize">14dp</item>
+        <item name="android:textColor">?attr/overviewButtonTextColor</item>
+        <item name="android:lineHeight">20dp</item>
+        <item name="android:textAlignment">center</item>
+        <item name="android:importantForAccessibility">no</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">@dimen/go_overview_button_caption_margin</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+
+    <style name="GoOverviewActionButtonContainer">
+        <item name="android:layout_width">@dimen/go_overview_button_container_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">vertical</item>
+    </style>
+
+    <!-- Modal Dialogs -->
+    <style name="ModalDialogTitle">
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textSize">20sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:lineHeight">24dp</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+
+    <style name="ModalDialogText">
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:lineHeight">24dp</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+
+    <style name="ModalDialogButton" parent="@style/Widget.AppCompat.Button.Borderless">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">?android:attr/colorAccent</item>
+        <item name="android:lineHeight">20dp</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/launcher3/AppSharing.java b/go/quickstep/src/com/android/launcher3/AppSharing.java
new file mode 100644
index 0000000..b72e71c
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/AppSharing.java
@@ -0,0 +1,132 @@
+/*
+ * 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;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import androidx.core.content.FileProvider;
+
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.SystemShortcut;
+
+import java.io.File;
+
+/**
+ * Defines the Share system shortcut and its factory.
+ * This shortcut can be added to the app long-press menu on the home screen.
+ * Clicking the button will initiate peer-to-peer sharing of the app.
+ */
+public final class AppSharing {
+    /**
+     * This flag enables this feature. It is defined here rather than in launcher3's FeatureFlags
+     * because it is unique to Go and not toggleable at runtime.
+     */
+    public static final boolean ENABLE_APP_SHARING = true;
+
+    private static final String TAG = "AppSharing";
+    private static final String FILE_PROVIDER_SUFFIX = ".overview.fileprovider";
+    private static final String APP_EXSTENSION = ".apk";
+    private static final String APP_MIME_TYPE = "application/application";
+
+    private final String mSharingComponent;
+
+    private AppSharing(Launcher launcher) {
+        mSharingComponent = launcher.getText(R.string.app_sharing_component).toString();
+    }
+
+    private boolean canShare(ItemInfo info) {
+        /**
+         * TODO: Implement once b/168831749 has been resolved
+         * The implementation should check the validity of the app.
+         * It should also check whether the app is free or paid, returning false in the latter case.
+         * For now, all checks occur in the sharing app.
+         * So, we simply check whether the sharing app is defined.
+         */
+        return !TextUtils.isEmpty(mSharingComponent);
+    }
+
+    private Uri getShareableUri(Context context, String path, String displayName) {
+        String authority = BuildConfig.APPLICATION_ID + FILE_PROVIDER_SUFFIX;
+        File pathFile = new File(path);
+        return FileProvider.getUriForFile(context, authority, pathFile, displayName);
+    }
+
+    private SystemShortcut<Launcher> getShortcut(Launcher launcher, ItemInfo info) {
+        if (!canShare(info)) {
+            return null;
+        }
+
+        return new Share(launcher, info);
+    }
+
+    /**
+     * The Share App system shortcut, used to initiate p2p sharing of a given app
+     */
+    public final class Share extends SystemShortcut<Launcher> {
+        public Share(Launcher target, ItemInfo itemInfo) {
+            super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo);
+        }
+
+        @Override
+        public void onClick(View view) {
+            Intent sendIntent = new Intent();
+            sendIntent.setAction(Intent.ACTION_SEND);
+
+            ComponentName targetComponent = mItemInfo.getTargetComponent();
+            if (targetComponent == null) {
+                Log.e(TAG, "Item missing target component");
+                return;
+            }
+            String packageName = targetComponent.getPackageName();
+            PackageManager packageManager = view.getContext().getPackageManager();
+            String sourceDir, appLabel;
+            try {
+                PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
+                sourceDir = packageInfo.applicationInfo.sourceDir;
+                appLabel = packageManager.getApplicationLabel(packageInfo.applicationInfo)
+                        .toString() + APP_EXSTENSION;
+            } catch (Exception e) {
+                Log.e(TAG, "Could not find info for package \"" + packageName + "\"");
+                return;
+            }
+            Uri uri = getShareableUri(view.getContext(), sourceDir, appLabel);
+            sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
+            sendIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+
+            sendIntent.setType(APP_MIME_TYPE);
+            sendIntent.setComponent(ComponentName.unflattenFromString(mSharingComponent));
+
+            mTarget.startActivitySafely(view, sendIntent, mItemInfo);
+
+            AbstractFloatingView.closeAllOpenViews(mTarget);
+        }
+    }
+
+    /**
+     * Shortcut factory for generating the Share App button
+     */
+    public static final SystemShortcut.Factory<Launcher> SHORTCUT_FACTORY = (launcher, itemInfo) ->
+            (new AppSharing(launcher)).getShortcut(launcher, itemInfo);
+}
diff --git a/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java b/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java
new file mode 100644
index 0000000..8bd0fec
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+
+import java.util.stream.Stream;
+
+/**
+ * The Launcher variant used for Android Go Edition
+ */
+public class Launcher3QuickStepGo extends QuickstepLauncher {
+    private static final String TAG = "Launcher3QuickStepGo";
+
+    @Override
+    public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+        Stream<SystemShortcut.Factory> shortcuts = super.getSupportedShortcuts();
+
+        if (AppSharing.ENABLE_APP_SHARING) {
+            shortcuts = Stream.concat(shortcuts, Stream.of(AppSharing.SHORTCUT_FACTORY));
+        }
+
+        return shortcuts;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
new file mode 100644
index 0000000..91cab3c
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -0,0 +1,427 @@
+/*
+ * 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 android.view.Surface.ROTATION_0;
+
+import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
+import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.assist.AssistContent;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.util.AssistContentRequester;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.views.GoOverviewActionsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Go-specific extension of the factory class that adds an overlay to TaskView
+ */
+public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
+    public static final String ACTION_LISTEN = "com.android.quickstep.ACTION_LISTEN";
+    public static final String ACTION_TRANSLATE = "com.android.quickstep.ACTION_TRANSLATE";
+    public static final String ACTION_SEARCH = "com.android.quickstep.ACTION_SEARCH";
+    public static final String ELAPSED_NANOS = "niu_actions_elapsed_realtime_nanos";
+    public static final String ACTIONS_URL = "niu_actions_app_url";
+    public static final String ACTIONS_APP_PACKAGE = "niu_actions_app_package";
+    public static final String ACTIONS_ERROR_CODE = "niu_actions_app_error_code";
+    public static final int ERROR_PERMISSIONS_STRUCTURE = 1;
+    public static final int ERROR_PERMISSIONS_SCREENSHOT = 2;
+    private static final String NIU_ACTIONS_CONFIRMED = "launcher_go.niu_actions_confirmed";
+    private static final String ASSIST_SETTINGS_ARGS_BUNDLE = ":settings:show_fragment_args";
+    private static final String ASSIST_SETTINGS_ARGS_KEY = ":settings:fragment_args_key";
+    private static final String ASSIST_SETTINGS_PREFERENCE_KEY = "default_assist";
+    private static final String TAG = "TaskOverlayFactoryGo";
+
+    public static final String LISTEN_TOOL_TIP_SEEN = "launcher.go_listen_tip_seen";
+    public static final String TRANSLATE_TOOL_TIP_SEEN = "launcher.go_translate_tip_seen";
+
+    @Retention(SOURCE)
+    @IntDef({PRIVACY_CONFIRMATION, ASSISTANT_NOT_SELECTED, ASSISTANT_NOT_SUPPORTED})
+    private @interface DialogType{}
+    private static final int PRIVACY_CONFIRMATION = 0;
+    private static final int ASSISTANT_NOT_SELECTED = 1;
+    private static final int ASSISTANT_NOT_SUPPORTED = 2;
+
+    private AssistContentRequester mContentRequester;
+
+    public TaskOverlayFactoryGo(Context context) {
+        mContentRequester = new AssistContentRequester(context);
+    }
+
+    /**
+     * Create a new overlay instance for the given View
+     */
+    public TaskOverlayGo createOverlay(TaskThumbnailView thumbnailView) {
+        return new TaskOverlayGo(thumbnailView, mContentRequester);
+    }
+
+    /**
+     * Overlay on each task handling Overview Action Buttons.
+     * @param <T> The type of View in which the overlay will be placed
+     */
+    public static final class TaskOverlayGo<T extends GoOverviewActionsView> extends TaskOverlay {
+        private String mNIUPackageName;
+        private String mTaskPackageName;
+        private String mWebUrl;
+        private boolean mAssistStructurePermitted;
+        private boolean mAssistScreenshotPermitted;
+        private AssistContentRequester mFactoryContentRequester;
+        private SharedPreferences mSharedPreferences;
+        private OverlayDialogGo mDialog;
+
+        private TaskOverlayGo(TaskThumbnailView taskThumbnailView,
+                AssistContentRequester assistContentRequester) {
+            super(taskThumbnailView);
+            mFactoryContentRequester = assistContentRequester;
+            mSharedPreferences = Utilities.getPrefs(mApplicationContext);
+        }
+
+        /**
+         * Called when the current task is interactive for the user
+         */
+        @Override
+        public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
+                boolean rotated) {
+            if (mDialog != null && mDialog.isShowing()) {
+                // Redraw the dialog in case the layout changed
+                mDialog.dismiss();
+                showDialog(mDialog.getAction(), mDialog.getType());
+            }
+
+            getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
+            if (thumbnail == null) {
+                return;
+            }
+
+            getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+            // Disable Overview Actions for Work Profile apps
+            boolean isManagedProfileTask =
+                    UserManager.get(mApplicationContext).isManagedProfile(task.key.userId);
+            boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot() && !isManagedProfileTask;
+            getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
+            mTaskPackageName = task.key.getPackageName();
+            mSharedPreferences = Utilities.getPrefs(mApplicationContext);
+            checkSettings();
+
+            if (!mAssistStructurePermitted || !mAssistScreenshotPermitted
+                    || TextUtils.isEmpty(mNIUPackageName)) {
+                return;
+            }
+
+            int taskId = task.key.id;
+            mFactoryContentRequester.requestAssistContent(taskId, this::onAssistContentReceived);
+
+            RecentsOrientedState orientedState =
+                    mThumbnailView.getTaskView().getRecentsView().getPagedViewOrientedState();
+            boolean isInLandscape = orientedState.getDisplayRotation() != ROTATION_0;
+
+            // show tooltips in portrait mode only
+            // TODO: remove If check once b/183714277 is fixed
+            if (!isInLandscape) {
+                new Handler().post(() -> {
+                    showTooltipsIfUnseen();
+                });
+            }
+        }
+
+        /** Provide Assist Content to the overlay. */
+        @VisibleForTesting
+        public void onAssistContentReceived(AssistContent assistContent) {
+            mWebUrl = assistContent.getWebUri() != null
+                    ? assistContent.getWebUri().toString() : null;
+        }
+
+        @Override
+        public void reset() {
+            super.reset();
+            mWebUrl = null;
+        }
+
+        @Override
+        public void updateOrientationState(RecentsOrientedState state) {
+            super.updateOrientationState(state);
+            ((GoOverviewActionsView) getActionsView()).updateOrientationState(state);
+        }
+
+        /**
+         * Creates and sends an Intent corresponding to the button that was clicked
+         */
+        private void sendNIUIntent(String actionType) {
+            if (TextUtils.isEmpty(mNIUPackageName)) {
+                showDialog(actionType, ASSISTANT_NOT_SELECTED);
+                return;
+            }
+
+            if (!mSharedPreferences.getBoolean(NIU_ACTIONS_CONFIRMED, false)) {
+                showDialog(actionType, PRIVACY_CONFIRMATION);
+                return;
+            }
+
+            Intent intent = createNIUIntent(actionType);
+            // Only add and send the image if the appropriate permissions are held
+            if (mAssistStructurePermitted && mAssistScreenshotPermitted) {
+                mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent);
+            } else {
+                // If both permissions are disabled, the structure error code takes priority
+                // The user must enable that one before they can enable screenshots
+                int code = mAssistStructurePermitted ? ERROR_PERMISSIONS_SCREENSHOT
+                        : ERROR_PERMISSIONS_STRUCTURE;
+                intent.putExtra(ACTIONS_ERROR_CODE, code);
+                try {
+                    mApplicationContext.startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Log.e(TAG, "No activity found to receive permission error intent");
+                    showDialog(actionType, ASSISTANT_NOT_SUPPORTED);
+                }
+            }
+        }
+
+        private Intent createNIUIntent(String actionType) {
+            Intent intent = new Intent(actionType)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                    .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
+                    .setPackage(mNIUPackageName)
+                    .putExtra(ACTIONS_APP_PACKAGE, mTaskPackageName)
+                    .putExtra(ELAPSED_NANOS, SystemClock.elapsedRealtimeNanos());
+
+            if (mWebUrl != null) {
+                intent.putExtra(ACTIONS_URL, mWebUrl);
+            }
+
+            return intent;
+        }
+
+        /**
+         * Checks whether the Assistant has screen context permissions
+         */
+        @VisibleForTesting
+        public void checkSettings() {
+            ContentResolver contentResolver = mApplicationContext.getContentResolver();
+            mAssistStructurePermitted = Settings.Secure.getInt(contentResolver,
+                    Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0;
+            mAssistScreenshotPermitted = Settings.Secure.getInt(contentResolver,
+                    Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1) != 0;
+
+            String assistantPackage =
+                    Settings.Secure.getString(contentResolver, Settings.Secure.ASSISTANT);
+            mNIUPackageName = assistantPackage.split("/", 2)[0];
+        }
+
+        protected class OverlayUICallbacksGoImpl extends OverlayUICallbacksImpl
+                implements OverlayUICallbacksGo {
+            public OverlayUICallbacksGoImpl(boolean isAllowedByPolicy, Task task) {
+                super(isAllowedByPolicy, task);
+            }
+
+            @SuppressLint("NewApi")
+            public void onListen() {
+                if (mIsAllowedByPolicy) {
+                    endLiveTileMode(() -> sendNIUIntent(ACTION_LISTEN));
+                } else {
+                    showBlockedByPolicyMessage();
+                }
+            }
+
+            @SuppressLint("NewApi")
+            public void onTranslate() {
+                if (mIsAllowedByPolicy) {
+                    endLiveTileMode(() -> sendNIUIntent(ACTION_TRANSLATE));
+                } else {
+                    showBlockedByPolicyMessage();
+                }
+            }
+
+            @SuppressLint("NewApi")
+            public void onSearch() {
+                if (mIsAllowedByPolicy) {
+                    endLiveTileMode(() -> sendNIUIntent(ACTION_SEARCH));
+                } else {
+                    showBlockedByPolicyMessage();
+                }
+            }
+        }
+
+        @VisibleForTesting
+        public void setImageActionsAPI(ImageActionsApi imageActionsApi) {
+            mImageApi = imageActionsApi;
+        }
+
+        // TODO (b/192406446): Test that these dialogs are shown at the appropriate times
+        private void showDialog(String action, @DialogType int type) {
+            switch (type) {
+                case PRIVACY_CONFIRMATION:
+                    showDialog(action, PRIVACY_CONFIRMATION,
+                            R.string.niu_actions_confirmation_title,
+                            R.string.niu_actions_confirmation_text, R.string.dialog_cancel,
+                            this::onDialogClickCancel, R.string.dialog_acknowledge,
+                            this::onNiuActionsConfirmationAccept);
+                    break;
+                case ASSISTANT_NOT_SELECTED:
+                    showDialog(action, ASSISTANT_NOT_SELECTED,
+                            R.string.assistant_not_selected_title,
+                            R.string.assistant_not_selected_text, R.string.dialog_cancel,
+                            this::onDialogClickCancel, R.string.dialog_settings,
+                            this::onDialogClickSettings);
+                    break;
+                case ASSISTANT_NOT_SUPPORTED:
+                    showDialog(action, ASSISTANT_NOT_SUPPORTED,
+                            R.string.assistant_not_supported_title,
+                            R.string.assistant_not_supported_text, R.string.dialog_cancel,
+                            this::onDialogClickCancel, R.string.dialog_settings,
+                            this::onDialogClickSettings);
+                    break;
+                default:
+                    Log.e(TAG, "Unexpected dialog type");
+            }
+        }
+
+        private void showDialog(String action, @DialogType int type, int titleTextID,
+                                int bodyTextID, int button1TextID,
+                                View.OnClickListener button1Callback, int button2TextID,
+                                View.OnClickListener button2Callback) {
+            BaseDraggingActivity activity = BaseActivity.fromContext(getActionsView().getContext());
+            LayoutInflater inflater = LayoutInflater.from(activity);
+            View view = inflater.inflate(R.layout.niu_actions_dialog, /* root */ null);
+
+            TextView dialogTitle = view.findViewById(R.id.niu_actions_dialog_header);
+            dialogTitle.setText(titleTextID);
+
+            TextView dialogBody = view.findViewById(R.id.niu_actions_dialog_description);
+            dialogBody.setText(bodyTextID);
+
+            Button button1 = view.findViewById(R.id.niu_actions_dialog_button_1);
+            button1.setText(button1TextID);
+            button1.setOnClickListener(button1Callback);
+
+            Button button2 = view.findViewById(R.id.niu_actions_dialog_button_2);
+            button2.setText(button2TextID);
+            button2.setOnClickListener(button2Callback);
+
+            mDialog = new OverlayDialogGo(activity, type, action);
+            mDialog.setView(view);
+            mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+            mDialog.show();
+        }
+
+        private void onNiuActionsConfirmationAccept(View v) {
+            mDialog.dismiss();
+            mSharedPreferences.edit().putBoolean(NIU_ACTIONS_CONFIRMED, true).apply();
+            sendNIUIntent(mDialog.getAction());
+        }
+
+        private void onDialogClickCancel(View v) {
+            mDialog.cancel();
+        }
+
+        private void onDialogClickSettings(View v) {
+            mDialog.dismiss();
+
+            Bundle fragmentArgs = new Bundle();
+            fragmentArgs.putString(ASSIST_SETTINGS_ARGS_KEY, ASSIST_SETTINGS_PREFERENCE_KEY);
+            Intent intent = new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                    .putExtra(ASSIST_SETTINGS_ARGS_BUNDLE, fragmentArgs);
+            try {
+                mApplicationContext.startActivity(intent);
+            } catch (ActivityNotFoundException e) {
+                Log.e(TAG, "No activity found to receive assistant settings intent");
+            }
+        }
+
+        /**
+         * Checks and Shows the tooltip if they are not seen by user
+         * Order of tooltips are translate and then listen
+         */
+        private void showTooltipsIfUnseen() {
+            if (!mSharedPreferences.getBoolean(TRANSLATE_TOOL_TIP_SEEN, false)) {
+                ((GoOverviewActionsView) getActionsView()).showTranslateToolTip();
+                mSharedPreferences.edit().putBoolean(TRANSLATE_TOOL_TIP_SEEN, true).apply();
+            } else if (!mSharedPreferences.getBoolean(LISTEN_TOOL_TIP_SEEN, false)) {
+                ((GoOverviewActionsView) getActionsView()).showListenToolTip();
+                mSharedPreferences.edit().putBoolean(LISTEN_TOOL_TIP_SEEN, true).apply();
+            }
+        }
+    }
+
+    private static final class OverlayDialogGo extends AlertDialog {
+        private final String mAction;
+        private final @DialogType int mType;
+
+        OverlayDialogGo(Context context, @DialogType int type, String action) {
+            super(context);
+            mType = type;
+            mAction = action;
+        }
+
+        public String getAction() {
+            return mAction;
+        }
+        public @DialogType int getType() {
+            return mType;
+        }
+    }
+
+    /**
+     * Callbacks the Ui can generate. This is the only way for a Ui to call methods on the
+     * controller.
+     */
+    public interface OverlayUICallbacksGo extends OverlayUICallbacks {
+        /** User has requested to listen to the current content read aloud */
+        void onListen();
+
+        /** User has requested a translation of the current content */
+        void onTranslate();
+
+        /** User has requested a visual search of the current content */
+        void onSearch();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java b/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
new file mode 100644
index 0000000..97ba590
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
@@ -0,0 +1,125 @@
+/*
+ * 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 static android.view.View.GONE;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.Px;
+
+import com.android.launcher3.R;
+import com.android.launcher3.views.ArrowTipView;
+import com.android.quickstep.TaskOverlayFactoryGo.OverlayUICallbacksGo;
+import com.android.quickstep.util.RecentsOrientedState;
+
+/**
+ * View for showing Go-specific action buttons in Overview
+ */
+public class GoOverviewActionsView extends OverviewActionsView<OverlayUICallbacksGo> {
+
+    private ArrowTipView mArrowTipView;
+
+    public GoOverviewActionsView(Context context) {
+        this(context, null);
+    }
+
+    public GoOverviewActionsView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public GoOverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        if (getResources().getBoolean(R.bool.enable_niu_actions)) {
+            findViewById(R.id.action_listen).setOnClickListener(this);
+            findViewById(R.id.action_translate).setOnClickListener(this);
+        } else {
+            findViewById(R.id.layout_listen).setVisibility(GONE);
+            findViewById(R.id.spacer_listen).setVisibility(GONE);
+            findViewById(R.id.layout_translate).setVisibility(GONE);
+            findViewById(R.id.spacer_translate).setVisibility(GONE);
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        super.onClick(view);
+
+        if (mCallbacks == null) {
+            return;
+        }
+        int id = view.getId();
+        if (id == R.id.action_listen) {
+            mCallbacks.onListen();
+        } else if (id == R.id.action_translate) {
+            mCallbacks.onTranslate();
+        } else if (id == R.id.action_search) {
+            mCallbacks.onSearch();
+        }
+    }
+
+    /**
+     * Shows Tooltip for action icons
+     */
+    private void showToolTip(int viewId, int textResourceId) {
+        int[] location = new int[2];
+        @Px int topMargin = getResources().getDimensionPixelSize(R.dimen.tooltip_top_margin);
+        findViewById(viewId).getLocationOnScreen(location);
+        mArrowTipView = new ArrowTipView(getContext(),  /* isPointingUp= */ false)
+            .showAtLocation(getResources().getString(textResourceId),
+                /* arrowXCoord= */ location[0] + findViewById(viewId).getWidth() / 2,
+                /* yCoord= */ location[1] - topMargin);
+
+        mArrowTipView.bringToFront();
+    }
+
+    /**
+     * Shows Tooltip for listen action icon
+     */
+    public void showListenToolTip() {
+        showToolTip(/* viewId= */ R.id.action_listen,
+                /* textResourceId= */ R.string.tooltip_listen);
+    }
+
+    /**
+     * Shows Tooltip for translate action icon
+     */
+    public void showTranslateToolTip() {
+        showToolTip(/* viewId= */ R.id.action_translate,
+                /* textResourceId= */ R.string.tooltip_translate);
+    }
+
+    /**
+     * Called when device orientation is changed
+     */
+    public void updateOrientationState(RecentsOrientedState orientedState) {
+        // dismiss tooltip
+        boolean canLauncherRotate = orientedState.canRecentsActivityRotate();
+        if (mArrowTipView != null && !canLauncherRotate) {
+            mArrowTipView.close(/* animate= */ false);
+        }
+    }
+}
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LoaderResults.java
index 7130531..5f71061 100644
--- a/go/src/com/android/launcher3/model/LoaderResults.java
+++ b/go/src/com/android/launcher3/model/LoaderResults.java
@@ -20,7 +20,6 @@
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.model.BgDataModel.Callbacks;
-import com.android.launcher3.util.LooperExecutor;
 
 /**
  * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
@@ -29,12 +28,7 @@
 
     public LoaderResults(LauncherAppState app, BgDataModel dataModel,
             AllAppsList allAppsList, Callbacks[] callbacks) {
-        this(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
-    }
-
-    public LoaderResults(LauncherAppState app, BgDataModel dataModel,
-            AllAppsList allAppsList, Callbacks[] callbacks, LooperExecutor executor) {
-        super(app, dataModel, allAppsList, callbacks, executor);
+        super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
     }
 
     @Override
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 3b3dc01..f8448da 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -24,12 +24,14 @@
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
+import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -41,21 +43,27 @@
 
     // True is the widget support is disabled.
     public static final boolean GO_DISABLE_WIDGETS = true;
+    public static final boolean GO_DISABLE_NOTIFICATION_DOTS = true;
 
-    private static final ArrayList<WidgetListRowEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
+    private static final ArrayList<WidgetsListBaseEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
 
     /**
-     * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
-     * are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
-     * is not sorted. This list is sorted at the UI when using
-     * {@link com.android.launcher3.widget.WidgetsDiffReporter}
+     * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row are
+     * sorted (based on label and user), but the overall list of {@link WidgetsListBaseEntry}s is
+     * not sorted. This list is sorted at the UI when using
+     * {@link com.android.launcher3.widget.picker.WidgetsDiffReporter}
      *
-     * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+     * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
      */
-    public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
+    public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
         return EMPTY_WIDGET_LIST;
     }
 
+    /** Returns a mapping of packages to their widgets without static shortcuts. */
+    public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
+        return Map.of();
+    }
+
     /**
      * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
      *                    only widgets and shortcuts associated with the package/user are.
@@ -74,4 +82,9 @@
             ComponentName providerName) {
         return null;
     }
+
+    /** Returns {@link PackageItemInfo} of a pending widget. */
+    public static PackageItemInfo newPendingItemInfo(ComponentName provider) {
+        return new PackageItemInfo(provider.getPackageName());
+    }
 }
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 7a51375..7f4c609 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -10,4 +10,4 @@
 PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
 
 BUILD_TOOLS_VERSION=28.0.3
-COMPILE_SDK=android-R
+COMPILE_SDK=android-S
diff --git a/lint-baseline-common-deps-lib.xml b/lint-baseline-common-deps-lib.xml
new file mode 100644
index 0000000..e52f8fb
--- /dev/null
+++ b/lint-baseline-common-deps-lib.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1="        android:topLeftRadius=&quot;?android:attr/dialogCornerRadius&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="6"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1="        android:topRightRadius=&quot;?android:attr/dialogCornerRadius&quot; />"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="7"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:style/Widget.DeviceDefault.Button.Colored` requires API level 28 (current min is 26)"
+        errorLine1="    &lt;style name=&quot;Widget.DeviceDefault.Button.Rounded.Colored&quot; parent=&quot;@android:style/Widget.DeviceDefault.Button.Colored&quot;>"
+        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/values/styles.xml"
+            line="287"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
+        errorLine1="    &lt;corners android:radius=&quot;@android:dimen/system_app_widget_background_radius&quot; />"
+        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
+            line="20"
+            column="14"/>
+    </issue>
+
+</issues>
diff --git a/lint-baseline-go-res-lib.xml b/lint-baseline-go-res-lib.xml
new file mode 100644
index 0000000..c5669f2
--- /dev/null
+++ b/lint-baseline-go-res-lib.xml
@@ -0,0 +1,576 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.View#getWindowInsetsController`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets.Type#ime`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                           ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsetsController#hide`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                    ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.View#getWindowInsetsController`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets.Type#ime`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                                   ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsetsController#hide`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                            ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.appwidget.AppWidgetHostView#updateAppWidgetSize`"
+        errorLine1="            widgetView.updateAppWidgetSize(new Bundle(), sizes);"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/AppWidgetResizeFrame.java"
+            line="399"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.graphics.Rect#inset`"
+        errorLine1="        potentialTaskRect.inset(insets.left, insets.top, insets.right, insets.bottom);"
+        errorLine2="                          ~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/BaseActivityInterface.java"
+            line="248"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.graphics.Rect#inset`"
+        errorLine1="        potentialTaskRect.inset("
+        errorLine2="                          ~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/BaseActivityInterface.java"
+            line="249"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.graphics.Rect#inset`"
+        errorLine1="        outRect.inset(Math.max(insets.left, sideMargin), Math.max(insets.top, topMargin),"
+        errorLine2="                ~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/BaseActivityInterface.java"
+            line="291"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.graphics.Rect#inset`"
+        errorLine1="        gridRect.inset(0, dp.overviewTaskThumbnailTopMarginPx, 0, 0);"
+        errorLine2="                 ~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/BaseActivityInterface.java"
+            line="315"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowManager#getCurrentWindowMetrics`"
+        errorLine1="                    .getCurrentWindowMetrics().getWindowInsets();"
+        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="236"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowMetrics#getWindowInsets`"
+        errorLine1="                    .getCurrentWindowMetrics().getWindowInsets();"
+        errorLine2="                                               ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="236"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.content.Context#getDisplay`"
+        errorLine1="            if (mContext.getDisplay() != null) {"
+        errorLine2="                         ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java"
+            line="115"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.content.Context#getDisplay`"
+        errorLine1="                mContext.getDisplay().getRealSize(mDisplaySize);"
+        errorLine2="                         ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java"
+            line="116"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.content.ContextWrapper#getDisplay`"
+        errorLine1="        Display display = getDisplay();"
+        errorLine2="                          ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java"
+            line="153"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="                    List.of(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)));"
+        errorLine2="                         ~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java"
+            line="158"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.appwidget.AppWidgetHostView#resetColorResources`"
+        errorLine1="            resetColorResources();"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="137"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="            mColorExtractor.addLocation(List.of(mLastLocationRegistered));"
+        errorLine2="                                             ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="367"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="                mColorExtractor.addLocation(List.of(mLastLocationRegistered));"
+        errorLine2="                                                 ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="390"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#maxResizeWidth`"
+        errorLine1="                (ATLEAST_S &amp;&amp; maxResizeWidth > 0)"
+        errorLine2="                              ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="91"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#maxResizeWidth`"
+        errorLine1="                        ? getSpanX(widgetPadding, maxResizeWidth, smallestCellWidth)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="92"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#maxResizeHeight`"
+        errorLine1="                (ATLEAST_S &amp;&amp; maxResizeHeight > 0)"
+        errorLine2="                              ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="95"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#maxResizeHeight`"
+        errorLine1="                        ? getSpanY(widgetPadding, maxResizeHeight, smallestCellHeight)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="96"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="        if (ATLEAST_S &amp;&amp; targetCellWidth >= minSpanX &amp;&amp; targetCellWidth &lt;= maxSpanX"
+        errorLine2="                         ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="101"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="        if (ATLEAST_S &amp;&amp; targetCellWidth >= minSpanX &amp;&amp; targetCellWidth &lt;= maxSpanX"
+        errorLine2="                                                        ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="101"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="                &amp;&amp; targetCellHeight >= minSpanY &amp;&amp; targetCellHeight &lt;= maxSpanY) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="102"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="                &amp;&amp; targetCellHeight >= minSpanY &amp;&amp; targetCellHeight &lt;= maxSpanY) {"
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="102"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="            spanX = targetCellWidth;"
+        errorLine2="                    ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="103"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="            spanY = targetCellHeight;"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="104"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.content.Context#getDisplay`"
+        errorLine1="        final Display display = mContext.getDisplay();"
+        errorLine2="                                         ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java"
+            line="94"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.content.pm.LauncherActivityInfo#getLoadingProgress`"
+        errorLine1="            return (int) (100 * info.getLoadingProgress());"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/util/PackageManagerHelper.java"
+            line="338"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="    private List&lt;WidgetsListBaseEntry> mAllWidgets = List.of();"
+        errorLine2="                                                          ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/PopupDataProvider.java"
+            line="64"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="    private List&lt;ItemInfo> mRecommendedWidgets = List.of();"
+        errorLine2="                                                      ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/PopupDataProvider.java"
+            line="66"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `new android.view.SurfaceControlViewHost`"
+        errorLine1="                    .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="91"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.SurfaceControlViewHost#getSurfacePackage`"
+        errorLine1="            surfacePackage = mSurfaceControlViewHost.getSurfacePackage();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="93"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.SurfaceControlViewHost#setView`"
+        errorLine1="                host.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight());"
+        errorLine2="                     ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="127"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `SurfacePackage` to `Parcelable` requires API level 30 (current min is 29)"
+        errorLine1="        result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);"
+        errorLine2="                                                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="132"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.SurfaceControlViewHost#release`"
+        errorLine1="                mSurfaceControlViewHost.release();"
+        errorLine2="                                        ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="149"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.graphics.Outline#setPath`"
+        errorLine1="        outline.setPath(mPath);"
+        errorLine2="                ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/RoundedArrowDrawable.java"
+            line="88"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `new android.widget.EdgeEffect`"
+        errorLine1="                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);"
+        errorLine2="                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/views/SpringRelativeLayout.java"
+            line="49"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `new android.widget.EdgeEffect`"
+        errorLine1="                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);"
+        errorLine2="                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/views/SpringRelativeLayout.java"
+            line="51"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowManager.LayoutParams#setFitInsetsTypes`"
+        errorLine1="        mWindowLayoutParams.setFitInsetsTypes(0);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java"
+            line="316"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets#getInsets`"
+        errorLine1="            Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());"
+        errorLine2="                                         ~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java"
+            line="118"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets.Type#systemBars`"
+        errorLine1="            Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());"
+        errorLine2="                                                                     ~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java"
+            line="118"
+            column="70"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#loadDescription`"
+        errorLine1="            CharSequence description = mItem.widgetInfo.loadDescription(getContext());"
+        errorLine2="                                                        ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="193"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#previewLayout`"
+        errorLine1="                &amp;&amp; item.widgetInfo.previewLayout != Resources.ID_NULL) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="214"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 29): `android.appwidget.AppWidgetProviderInfo#previewLayout`"
+        errorLine1="            launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;"
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="222"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.View#getWindowInsetsController`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsets.Type#ime`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                           ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `android.view.WindowInsetsController#hide`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                    ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 29): `java.util.List#of`"
+        errorLine1="            return new RecommendationTableData(List.of(), previewScale);"
+        errorLine2="                                                    ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java"
+            line="139"
+            column="53"/>
+    </issue>
+
+</issues>
diff --git a/lint-baseline-launcher3.xml b/lint-baseline-launcher3.xml
new file mode 100644
index 0000000..9a68405
--- /dev/null
+++ b/lint-baseline-launcher3.xml
@@ -0,0 +1,598 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.View#getWindowInsetsController`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsets.Type#ime`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                           ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsetsController#hide`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                    ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java"
+            line="203"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.View#getWindowInsetsController`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsets.Type#ime`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                                   ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsetsController#hide`"
+        errorLine1="                getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                            ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsRecyclerView.java"
+            line="193"
+            column="45"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#updateAppWidgetSize`"
+        errorLine1="            widgetView.updateAppWidgetSize(new Bundle(), sizes);"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/AppWidgetResizeFrame.java"
+            line="399"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowManager#getCurrentWindowMetrics`"
+        errorLine1="                    .getCurrentWindowMetrics().getWindowInsets();"
+        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="236"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowMetrics#getWindowInsets`"
+        errorLine1="                    .getCurrentWindowMetrics().getWindowInsets();"
+        errorLine2="                                               ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="236"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`"
+        errorLine1="        folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);"
+        errorLine2="                                   ~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/DeviceProfile.java"
+            line="256"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`"
+        errorLine1="        return mContext.getResources().getFloat(resId);"
+        errorLine2="                                       ~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/util/DynamicResource.java"
+            line="73"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#resetColorResources`"
+        errorLine1="            resetColorResources();"
+        errorLine2="            ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="137"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="            mColorExtractor.addLocation(List.of(mLastLocationRegistered));"
+        errorLine2="                                             ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="367"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="                mColorExtractor.addLocation(List.of(mLastLocationRegistered));"
+        errorLine2="                                                 ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java"
+            line="390"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#maxResizeWidth`"
+        errorLine1="                (ATLEAST_S &amp;&amp; maxResizeWidth > 0)"
+        errorLine2="                              ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="91"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#maxResizeWidth`"
+        errorLine1="                        ? getSpanX(widgetPadding, maxResizeWidth, smallestCellWidth)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="92"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#maxResizeHeight`"
+        errorLine1="                (ATLEAST_S &amp;&amp; maxResizeHeight > 0)"
+        errorLine2="                              ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="95"
+            column="31"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#maxResizeHeight`"
+        errorLine1="                        ? getSpanY(widgetPadding, maxResizeHeight, smallestCellHeight)"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="96"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="        if (ATLEAST_S &amp;&amp; targetCellWidth >= minSpanX &amp;&amp; targetCellWidth &lt;= maxSpanX"
+        errorLine2="                         ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="101"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="        if (ATLEAST_S &amp;&amp; targetCellWidth >= minSpanX &amp;&amp; targetCellWidth &lt;= maxSpanX"
+        errorLine2="                                                        ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="101"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="                &amp;&amp; targetCellHeight >= minSpanY &amp;&amp; targetCellHeight &lt;= maxSpanY) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="102"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="                &amp;&amp; targetCellHeight >= minSpanY &amp;&amp; targetCellHeight &lt;= maxSpanY) {"
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="102"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellWidth`"
+        errorLine1="            spanX = targetCellWidth;"
+        errorLine2="                    ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="103"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#targetCellHeight`"
+        errorLine1="            spanY = targetCellHeight;"
+        errorLine2="                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java"
+            line="104"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 28 (current min is 26): `android.app.Person#getKey`"
+        errorLine1="        return people.stream().filter(person -> person.getKey() != null)"
+        errorLine2="                                                       ~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
+            line="72"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Method reference requires API level 28 (current min is 26): `Person::getKey`"
+        errorLine1="                .map(Person::getKey).sorted().toArray(String[]::new);"
+        errorLine2="                     ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
+            line="73"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.content.pm.LauncherActivityInfo#getLoadingProgress`"
+        errorLine1="            return (int) (100 * info.getLoadingProgress());"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/util/PackageManagerHelper.java"
+            line="338"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`"
+        errorLine1="                AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1752"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`"
+        errorLine1="                : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);"
+        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1753"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`"
+        errorLine1="                AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1760"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`"
+        errorLine1="                : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);"
+        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
+            line="1761"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="    private List&lt;WidgetsListBaseEntry> mAllWidgets = List.of();"
+        errorLine2="                                                          ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/PopupDataProvider.java"
+            line="64"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="    private List&lt;ItemInfo> mRecommendedWidgets = List.of();"
+        errorLine2="                                                      ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/PopupDataProvider.java"
+            line="66"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `new android.view.SurfaceControlViewHost`"
+        errorLine1="                    .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="91"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#getSurfacePackage`"
+        errorLine1="            surfacePackage = mSurfaceControlViewHost.getSurfacePackage();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="93"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#setView`"
+        errorLine1="                host.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight());"
+        errorLine2="                     ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="127"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `SurfacePackage` to `Parcelable` requires API level 30 (current min is 26)"
+        errorLine1="        result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);"
+        errorLine2="                                                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="132"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.SurfaceControlViewHost#release`"
+        errorLine1="                mSurfaceControlViewHost.release();"
+        errorLine2="                                        ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java"
+            line="149"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.graphics.Outline#setPath`"
+        errorLine1="        outline.setPath(mPath);"
+        errorLine2="                ~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/popup/RoundedArrowDrawable.java"
+            line="88"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `new android.widget.EdgeEffect`"
+        errorLine1="                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);"
+        errorLine2="                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/views/SpringRelativeLayout.java"
+            line="49"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `new android.widget.EdgeEffect`"
+        errorLine1="                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);"
+        errorLine2="                  ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/views/SpringRelativeLayout.java"
+            line="51"
+            column="19"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 29 (current min is 26): `android.view.WindowInsets#getTappableElementInsets`"
+        errorLine1="            return windowInsets.getTappableElementInsets().bottom > 0;"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/SysUiScrim.java"
+            line="190"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 29 (current min is 26): `android.graphics.Insets#bottom`"
+        errorLine1="            return windowInsets.getTappableElementInsets().bottom > 0;"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/SysUiScrim.java"
+            line="190"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 28 (current min is 26): `android.appwidget.AppWidgetProviderInfo#widgetFeatures`"
+        errorLine1="        int featureFlags = mProviderInfo.widgetFeatures;"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetAddFlowHandler.java"
+            line="93"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#loadDescription`"
+        errorLine1="            CharSequence description = mItem.widgetInfo.loadDescription(getContext());"
+        errorLine2="                                                        ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="193"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#previewLayout`"
+        errorLine1="                &amp;&amp; item.widgetInfo.previewLayout != Resources.ID_NULL) {"
+        errorLine2="                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="214"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 31 (current min is 26): `android.appwidget.AppWidgetProviderInfo#previewLayout`"
+        errorLine1="            launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;"
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetCell.java"
+            line="222"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.View#getWindowInsetsController`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsets.Type#ime`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                                           ~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `android.view.WindowInsetsController#hide`"
+        errorLine1="        getWindowInsetsController().hide(WindowInsets.Type.ime());"
+        errorLine2="                                    ~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
+            line="558"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level R (current min is 26): `java.util.List#of`"
+        errorLine1="            return new RecommendationTableData(List.of(), previewScale);"
+        errorLine2="                                                    ~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java"
+            line="139"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Method reference requires API level 28 (current min is 26): `Person::getKey`"
+        errorLine1="            : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);"
+        errorLine2="                                         ~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/model/data/WorkspaceItemInfo.java"
+            line="178"
+            column="42"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 26): `android.appwidget.AppWidgetHostView#setColorResources`"
+        errorLine1="            view.setColorResources(mWallpaperColorResources);"
+        errorLine2="                 ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
+            line="381"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 27 (current min is 26): `android.app.WallpaperManager#getWallpaperColors`"
+        errorLine1="                    : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);"
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java"
+            line="270"
+            column="61"/>
+    </issue>
+
+</issues>
diff --git a/lint-baseline-res-lib.xml b/lint-baseline-res-lib.xml
new file mode 100644
index 0000000..e52f8fb
--- /dev/null
+++ b/lint-baseline-res-lib.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1="        android:topLeftRadius=&quot;?android:attr/dialogCornerRadius&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="6"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
+        errorLine1="        android:topRightRadius=&quot;?android:attr/dialogCornerRadius&quot; />"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
+            line="7"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:style/Widget.DeviceDefault.Button.Colored` requires API level 28 (current min is 26)"
+        errorLine1="    &lt;style name=&quot;Widget.DeviceDefault.Button.Rounded.Colored&quot; parent=&quot;@android:style/Widget.DeviceDefault.Button.Colored&quot;>"
+        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/values/styles.xml"
+            line="287"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
+        errorLine1="    &lt;corners android:radius=&quot;@android:dimen/system_app_widget_background_radius&quot; />"
+        errorLine2="             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
+            line="20"
+            column="14"/>
+    </issue>
+
+</issues>
diff --git a/proguard.flags b/proguard.flags
index 37b8093..a450183 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -45,9 +45,10 @@
 
 # BUG(70852369): Surpress additional warnings after changing from Proguard to R8
 -dontwarn android.app.**
--dontwarn android.view.**
--dontwarn android.os.**
 -dontwarn android.graphics.**
+-dontwarn android.os.**
+-dontwarn android.view.**
+-dontwarn android.window.**
 
 # Ignore warnings for hidden utility classes referenced from the shared lib
 -dontwarn com.android.internal.util.**
diff --git a/proto_overrides/launcher_log_extension.proto b/proto_overrides/launcher_log_extension.proto
deleted file mode 100644
index 2995aa2..0000000
--- a/proto_overrides/launcher_log_extension.proto
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-syntax = "proto2";
-
-option java_package = "com.android.launcher3.userevent";
-option java_outer_classname = "LauncherLogExtensions";
-
-package userevent;
-
-//
-// Use this to add any app specific extensions to the proto.
-//
-message LauncherEventExtension {
-}
-
-message TargetExtension {
-}
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 1a8ab65..6d49d75 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -18,6 +18,8 @@
 option java_package = "com.android.launcher3.logger";
 option java_outer_classname = "LauncherAtom";
 
+import "launcher_atom_extension.proto";
+
 //
 // ItemInfos
 message ItemInfo {
@@ -27,6 +29,8 @@
     Shortcut shortcut = 3;
     Widget widget = 4;
     FolderIcon folder_icon = 9;
+    Slice slice = 10;
+    SearchActionItem search_action_item = 11;
   }
   // When used for launch event, stores the global predictive rank
   optional int32 rank = 5;
@@ -55,7 +59,7 @@
     SettingsContainer settings_container = 9;
     PredictedHotseatContainer predicted_hotseat_container = 10;
     TaskSwitcherContainer task_switcher_container = 11;
-    TaskForegroundContainer task_foreground_container = 12;
+    ExtendedContainers extended_containers = 20;
   }
 }
 
@@ -93,14 +97,8 @@
 message SettingsContainer {
 }
 
-// Container for tasks in the Overview UI.
-// Typically entered using either the overview gesture or overview button.
-message TaskSwitcherContainer {}
-
-// Container for tasks from another foreground app, when not on launcher screen.
-// Typically home gesture or overview gesture can be triggered from
-// this container.
-message TaskForegroundContainer {}
+message TaskSwitcherContainer {
+}
 
 enum Attribute {
   UNKNOWN = 0;
@@ -127,6 +125,25 @@
   // Folder's label is empty(i.e., title == "").
   // Not eligible for auto-labeling.
   EMPTY_LABEL = 12;
+
+  ALL_APPS_SEARCH_RESULT_APPLICATION = 13;
+  ALL_APPS_SEARCH_RESULT_SHORTCUT = 14;
+  ALL_APPS_SEARCH_RESULT_PEOPLE = 15;
+  ALL_APPS_SEARCH_RESULT_ACTION = 16;
+  ALL_APPS_SEARCH_RESULT_SETTING = 17;
+  ALL_APPS_SEARCH_RESULT_SCREENSHOT = 18;
+  ALL_APPS_SEARCH_RESULT_SLICE = 19;
+  ALL_APPS_SEARCH_RESULT_WIDGETS = 20;
+  ALL_APPS_SEARCH_RESULT_PLAY = 21;
+  ALL_APPS_SEARCH_RESULT_SUGGEST = 22;
+  ALL_APPS_SEARCH_RESULT_ASSISTANT = 23;
+  ALL_APPS_SEARCH_RESULT_CHROMETAB = 24;
+  ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25;
+  ALL_APPS_SEARCH_RESULT_TIPS = 26;
+  ALL_APPS_SEARCH_RESULT_PEOPLE_TILE = 27;
+
+  WIDGETS_BOTTOM_TRAY = 28;
+  WIDGETS_TRAY_PREDICTION = 29;
 }
 
 // Main app icons
@@ -138,6 +155,7 @@
 // Legacy shortcuts and shortcuts handled by ShortcutManager
 message Shortcut {
   optional string shortcut_name = 1;
+  optional string shortcut_id = 2;
 }
 
 // AppWidgets handled by AppWidgetManager
@@ -147,6 +165,7 @@
   optional int32 app_widget_id = 3;
   optional string package_name = 4; // only populated during snapshot if from workspace
   optional string component_name = 5; // only populated during snapshot if from workspace
+  optional int32 widget_features = 6;
 }
 
 // Tasks handled by PackageManager
@@ -172,6 +191,17 @@
   optional string label_info = 4;
 }
 
+// Contains Slice details for logging.
+message Slice{
+  optional string uri = 1;
+}
+
+// Represents SearchAction with in launcher
+message SearchActionItem{
+  optional string package_name = 1;
+  optional string title = 2;
+}
+
 //////////////////////////////////////////////
 // Containers
 
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
deleted file mode 100644
index 9423cb2..0000000
--- a/protos/launcher_log.proto
+++ /dev/null
@@ -1,245 +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.
- */
-syntax = "proto2";
-
-import "launcher_log_extension.proto";
-
-option java_package = "com.android.launcher3.userevent";
-option java_outer_classname = "LauncherLogProto";
-
-package userevent;
-
-message Target {
-  enum Type {
-    NONE = 0;
-    ITEM = 1;
-    CONTROL = 2;
-    CONTAINER = 3;
-  }
-
-  optional Type type = 1;
-
-  // For container type and item type
-  // Used mainly for ContainerType.FOLDER, ItemType.*
-  optional int32 page_index = 2;
-  optional int32 rank = 3;
-  optional int32 grid_x = 4;
-  optional int32 grid_y = 5;
-
-  // For container types only
-  optional ContainerType container_type = 6;
-  optional int32 cardinality = 7;
-
-  // For control types only
-  optional ControlType control_type = 8;
-
-  // For item types only
-  optional ItemType item_type = 9;
-  optional int32 package_name_hash = 10;
-  optional int32 component_hash = 11;      // Used for ItemType.WIDGET
-  optional int32 intent_hash = 12;         // Used for ItemType.SHORTCUT
-  optional int32 span_x = 13 [default = 1];// Used for ItemType.WIDGET
-  optional int32 span_y = 14 [default = 1];// Used for ItemType.WIDGET
-  optional int32 predictedRank = 15;
-  optional TargetExtension extension = 16;
-  optional TipType tip_type = 17;
-  optional int32 search_query_length = 18;
-  optional bool is_work_app = 19;
-  optional FromFolderLabelState from_folder_label_state = 20;
-  optional ToFolderLabelState to_folder_label_state = 21;
-
-  // Note: proto does not support duplicate enum values, even if they belong to different enum type.
-  // Hence "FROM" and "TO" prefix added.
-  enum FromFolderLabelState {
-    FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
-    FROM_EMPTY = 1;
-    FROM_CUSTOM = 2;
-    FROM_SUGGESTED = 3;
-  }
-
-  enum ToFolderLabelState {
-    TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
-    TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
-    TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
-    TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 3;
-    TO_SUGGESTION2_WITH_VALID_PRIMARY = 4;
-    TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
-    TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
-    TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
-    TO_EMPTY_WITH_VALID_SUGGESTIONS = 8 [deprecated = true];
-    TO_EMPTY_WITH_VALID_PRIMARY = 15;
-    TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 16;
-    TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
-    TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
-    TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11 [deprecated = true];
-    TO_CUSTOM_WITH_VALID_PRIMARY = 17;
-    TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 18;
-    TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
-    TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
-    UNCHANGED = 14;
-  }
-}
-
-// Used to define what type of item a Target would represent.
-enum ItemType {
-  DEFAULT_ITEMTYPE = 0;
-  APP_ICON = 1;
-  SHORTCUT = 2;
-  WIDGET = 3;
-  FOLDER_ICON = 4;
-  DEEPSHORTCUT = 5;
-  SEARCHBOX = 6;
-  EDITTEXT = 7;
-  NOTIFICATION = 8;
-  TASK = 9;         // Each page of Recents UI (QuickStep)
-  WEB_APP = 10;
-  TASK_ICON = 11;
-}
-
-// Used to define what type of container a Target would represent.
-enum ContainerType {
-  DEFAULT_CONTAINERTYPE = 0;
-  WORKSPACE = 1;
-  HOTSEAT = 2;
-  FOLDER = 3;
-  ALLAPPS = 4;
-  WIDGETS = 5;
-  OVERVIEW = 6;   // Zoomed out workspace (without QuickStep)
-  PREDICTION = 7;
-  SEARCHRESULT = 8;
-  DEEPSHORTCUTS = 9;
-  PINITEM = 10;    // confirmation screen
-  NAVBAR = 11;
-  TASKSWITCHER = 12; // Recents UI Container (QuickStep)
-  APP = 13; // Foreground activity is another app (QuickStep)
-  TIP = 14; // Onboarding texts (QuickStep)
-  OTHER_LAUNCHER_APP = 15;
-}
-
-// Used to define what type of control a Target would represent.
-enum ControlType {
-  DEFAULT_CONTROLTYPE = 0;
-  ALL_APPS_BUTTON = 1;
-  WIDGETS_BUTTON = 2;
-  WALLPAPER_BUTTON = 3;
-  SETTINGS_BUTTON = 4;
-  REMOVE_TARGET = 5;
-  UNINSTALL_TARGET = 6;
-  APPINFO_TARGET = 7;
-  RESIZE_HANDLE = 8;
-  VERTICAL_SCROLL = 9;
-  HOME_INTENT = 10; // Deprecated, use enum Command instead
-  BACK_BUTTON = 11;
-  QUICK_SCRUB_BUTTON = 12;
-  CLEAR_ALL_BUTTON = 13;
-  CANCEL_TARGET = 14;
-  TASK_PREVIEW = 15;
-  SPLIT_SCREEN_TARGET = 16;
-  REMOTE_ACTION_SHORTCUT = 17;
-  APP_USAGE_SETTINGS = 18;
-  BACK_GESTURE = 19;
-  UNDO = 20;
-  DISMISS_PREDICTION = 21;
-  HYBRID_HOTSEAT_ACCEPTED = 22;
-  HYBRID_HOTSEAT_CANCELED = 23;
-  OVERVIEW_ACTIONS_SHARE_BUTTON = 24;
-  OVERVIEW_ACTIONS_SCREENSHOT_BUTTON = 25;
-  OVERVIEW_ACTIONS_SELECT_BUTTON = 26;
-  SELECT_MODE_CLOSE_BUTTON = 27;
-  SELECT_MODE_ITEM = 28;
-}
-
-enum TipType {
-  DEFAULT_NONE = 0;
-  BOUNCE = 1;
-  SWIPE_UP_TEXT = 2;
-  QUICK_SCRUB_TEXT = 3;
-  PREDICTION_TEXT = 4;
-  DWB_TOAST = 5;
-  HYBRID_HOTSEAT = 6;
-}
-
-// Used to define the action component of the LauncherEvent.
-message Action {
-  enum Type {
-    TOUCH = 0;
-    AUTOMATED = 1;
-    COMMAND = 2;
-    TIP = 3;
-    SOFT_KEYBOARD = 4;
-    // HARD_KEYBOARD, ASSIST
-  }
-
-  enum Touch {
-    TAP = 0;
-    LONGPRESS = 1;
-    DRAGDROP = 2;
-    SWIPE = 3;
-    FLING = 4;
-    PINCH = 5;
-    SWIPE_NOOP = 6;
-  }
-
-  enum Direction {
-    NONE = 0;
-    UP = 1;
-    DOWN = 2;
-    LEFT = 3;
-    RIGHT = 4;
-    UPRIGHT = 5;
-    UPLEFT = 6;
-  }
-  enum Command {
-    HOME_INTENT = 0;
-    BACK = 1;
-    ENTRY = 2;          // Indicates entry to one of Launcher container type target
-                        // not using the HOME_INTENT
-    CANCEL = 3;         // Indicates that a confirmation screen was cancelled
-    CONFIRM = 4;        // Indicates thata confirmation screen was accepted
-    STOP = 5;           // Indicates onStop() was called (screen time out, power off)
-    RECENTS_BUTTON = 6; // Indicates that Recents button was pressed
-    RESUME = 7;         // Indicates onResume() was called
-  }
-
-  optional Type type = 1;
-  optional Touch touch = 2;
-  optional Direction dir = 3;
-  optional Command command = 4;
-  // Log if the action was performed on outside of the container
-  optional bool is_outside = 5;
-  optional bool is_state_change = 6;
-}
-
-//
-// Context free grammar of typical user interaction:
-//         Action (Touch) + Target
-//         Action (Touch) + Target + Target
-//
-message LauncherEvent {
-  required Action action = 1;
-  // List of targets that touch actions can be operated on.
-  repeated Target src_target = 2;
-  repeated Target dest_target = 3;
-
-  optional int64 action_duration_millis = 4;
-  optional int64 elapsed_container_millis = 5;
-  optional int64 elapsed_session_millis = 6;
-
-  optional bool is_in_multi_window_mode = 7 [deprecated = true];
-  optional bool is_in_landscape_mode = 8 [deprecated = true];
-
-  optional LauncherEventExtension extension = 9;
-}
diff --git a/protos/launcher_trace.proto b/protos/launcher_trace.proto
index c6f3543..65fcfe5 100644
--- a/protos/launcher_trace.proto
+++ b/protos/launcher_trace.proto
@@ -28,4 +28,40 @@
 message TouchInteractionServiceProto {
 
     optional bool service_connected = 1;
+    optional OverviewComponentObserverProto overview_component_obvserver = 2;
+    optional InputConsumerProto input_consumer = 3;
+}
+
+message OverviewComponentObserverProto {
+
+    optional bool overview_activity_started = 1;
+    optional bool overview_activity_resumed = 2;
+}
+
+message InputConsumerProto {
+
+    optional string name = 1;
+    optional SwipeHandlerProto swipe_handler = 2;
+}
+
+message SwipeHandlerProto {
+
+    optional GestureStateProto gesture_state = 1;
+    optional bool is_recents_attached_to_app_window = 2;
+    optional int32 scroll_offset = 3;
+    // Swipe up progress from 0 (app) to 1 (overview); can be > 1 if swiping past overview.
+    optional float app_to_overview_progress = 4;
+}
+
+message GestureStateProto {
+
+    optional GestureEndTarget endTarget = 1 [default = UNSET];
+
+    enum GestureEndTarget {
+        UNSET = 0;
+        HOME = 1;
+        RECENTS = 2;
+        NEW_TASK = 3;
+        LAST_TASK = 4;
+    }
 }
diff --git a/protos_overrides/launcher_atom_extension.proto b/protos_overrides/launcher_atom_extension.proto
new file mode 100644
index 0000000..a07daf8
--- /dev/null
+++ b/protos_overrides/launcher_atom_extension.proto
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtomExtensions";
+
+
+// This proto file contains placeholder messages that can be overridden by
+// other Launcher variants.
+// Variants could have its own launcher_atom_extension.proto file(should have
+// same messages declared here but with different implementation); when building
+// variant's apk launcher_atom.proto will reference variant's extension file,
+// essentially overriding this file.
+
+
+// Wrapper message for additional containers used in variants.
+message ExtendedContainers {}
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
new file mode 100644
index 0000000..38c9919
--- /dev/null
+++ b/quickstep/Android.bp
@@ -0,0 +1,28 @@
+// 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 {
+    // 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-quickstep-robolectric-src",
+    path: "robolectric_tests",
+    srcs: ["robolectric_tests/src/**/*.java"],
+}
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 60afddb..6808222 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -49,10 +49,11 @@
             android:stateNotNeeded="true"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
             android:resizeableActivity="true"
             android:resumeWhilePausing="true"
             android:taskAffinity=""
+            android:exported="true"
             android:enabled="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index e49f2ec..b43d8d1 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -17,106 +17,130 @@
 ** limitations under the License.
 */
 -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="com.android.launcher3" >
 
-    <permission
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     xmlns:tools="http://schemas.android.com/tools"
+     package="com.android.launcher3">
+
+     <permission
         android:name="${packageName}.permission.HOTSEAT_EDU"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signatureOrSystem" />
 
-    <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
-    <uses-permission android:name="android.permission.VIBRATE" />
-    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+    <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
+    <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <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.MANAGE_ACTIVITY_TASKS"/>
+    <uses-permission android:name="android.permission.STATUS_BAR"/>
+    <uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
+    <uses-permission android:name="android.permission.SET_ORIENTATION"/>
+    <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
+    <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY"/>
+    <uses-permission android:name="android.permission.MONITOR_INPUT"/>
+
     <uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
+    <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
 
-    <application
-        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
-        android:fullBackupOnly="true"
-        android:fullBackupContent="@xml/backupscheme"
-        android:hardwareAccelerated="true"
-        android:icon="@drawable/ic_launcher_home"
-        android:label="@string/derived_app_name"
-        android:theme="@style/AppTheme"
-        android:largeHeap="@bool/config_largeHeap"
-        android:restoreAnyVersion="true"
-        android:supportsRtl="true" >
+    <application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+         android:fullBackupOnly="true"
+         android:fullBackupContent="@xml/backupscheme"
+         android:hardwareAccelerated="true"
+         android:icon="@drawable/ic_launcher_home"
+         android:label="@string/derived_app_name"
+         android:theme="@style/AppTheme"
+         android:largeHeap="@bool/config_largeHeap"
+         android:restoreAnyVersion="true"
+         android:supportsRtl="true">
 
-        <service
-            android:name="com.android.quickstep.TouchInteractionService"
-            android:permission="android.permission.STATUS_BAR_SERVICE"
-            android:directBootAware="true" >
+        <service android:name="com.android.quickstep.TouchInteractionService"
+             android:permission="android.permission.STATUS_BAR_SERVICE"
+             android:directBootAware="true"
+             android:exported="true">
             <intent-filter>
-                <action android:name="android.intent.action.QUICKSTEP_SERVICE" />
+                <action android:name="android.intent.action.QUICKSTEP_SERVICE"/>
             </intent-filter>
         </service>
 
         <activity android:name="com.android.quickstep.RecentsActivity"
-            android:excludeFromRecents="true"
-            android:launchMode="singleTask"
-            android:clearTaskOnLaunch="true"
-            android:stateNotNeeded="true"
-            android:theme="@style/LauncherTheme"
-            android:screenOrientation="unspecified"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
-            android:resizeableActivity="true"
-            android:resumeWhilePausing="true"
-            android:taskAffinity="" />
+             android:excludeFromRecents="true"
+             android:launchMode="singleTask"
+             android:clearTaskOnLaunch="true"
+             android:stateNotNeeded="true"
+             android:theme="@style/LauncherTheme"
+             android:screenOrientation="unspecified"
+             android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
+             android:resizeableActivity="true"
+             android:resumeWhilePausing="true"
+             android:taskAffinity=""/>
 
         <!-- Content provider to settings search. The autority should be same as the packageName -->
-        <provider
-            android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
-            android:authorities="${packageName}"
-            android:grantUriPermissions="true"
-            android:multiprocess="true"
-            android:permission="android.permission.READ_SEARCH_INDEXABLES"
-            android:exported="true">
+        <provider android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
+             android:authorities="${packageName}"
+             android:grantUriPermissions="true"
+             android:multiprocess="true"
+             android:permission="android.permission.READ_SEARCH_INDEXABLES"
+             android:exported="true">
             <intent-filter>
-                <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" />
+                <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER"/>
             </intent-filter>
         </provider>
 
         <!-- FileProvider used for sharing images. -->
-        <provider
-            android:name="androidx.core.content.FileProvider"
-            android:authorities="${packageName}.overview.fileprovider"
-            android:exported="false"
-            android:grantUriPermissions="true">
-            <meta-data
-                android:name="android.support.FILE_PROVIDER_PATHS"
-                android:resource="@xml/overview_file_provider_paths" />
+        <provider android:name="androidx.core.content.FileProvider"
+             android:authorities="${packageName}.overview.fileprovider"
+             android:exported="false"
+             android:grantUriPermissions="true">
+            <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
+                 android:resource="@xml/overview_file_provider_paths"/>
         </provider>
 
-        <service
-            android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
-            tools:node="remove" />
+        <activity android:name="com.android.launcher3.proxy.ProxyActivityStarter"
+             android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
+             android:launchMode="singleTask"
+             android:clearTaskOnLaunch="true"
+             android:exported="false"/>
 
-        <activity
-            android:name="com.android.launcher3.proxy.ProxyActivityStarter"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
-            android:launchMode="singleTask"
-            android:clearTaskOnLaunch="true"
-            android:exported="false" />
-
-        <activity
-            android:name="com.android.quickstep.interaction.GestureSandboxActivity"
+        <activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
             android:autoRemoveFromRecents="true"
             android:excludeFromRecents="true"
-            android:screenOrientation="portrait">
+            android:screenOrientation="portrait"
+            android:exported="true">
             <intent-filter>
-                <action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
-                <category android:name="android.intent.category.DEFAULT" />
+                <action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
+                <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
+
+        <!--
+        Activity following gesture nav onboarding.
+        It's protected by android.permission.REBOOT to ensure that only system apps can start it
+        (setup wizard already has this permission)
+        -->
+        <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"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.quickstep.action.GESTURE_ONBOARDING_ALL_SET"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
         <activity
             android:name=".hybridhotseat.HotseatEduActivity"
             android:theme="@android:style/Theme.NoDisplay"
             android:noHistory="true"
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
-            android:permission="${packageName}.permission.HOTSEAT_EDU">
+            android:permission="${packageName}.permission.HOTSEAT_EDU"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"/>
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto
new file mode 100644
index 0000000..d2dc0cb
--- /dev/null
+++ b/quickstep/protos_overrides/launcher_atom_extension.proto
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtomExtensions";
+
+
+// Wrapper message for containers used at the quickstep level.
+// Message name should match with launcher_atom_extension.proto message at
+// the AOSP level.
+message ExtendedContainers {
+
+  oneof Container{
+    DeviceSearchResultContainer device_search_result_container = 1;
+    CorrectedDeviceSearchResultContainer corrected_device_search_result_container = 2;
+  }
+}
+
+// Represents on-device search result container.
+message DeviceSearchResultContainer{
+  optional int32 query_length = 1;
+}
+
+// Represents on-device search result container with results from spell-corrected query.
+message CorrectedDeviceSearchResultContainer{
+  optional int32 query_length = 1;
+}
+
diff --git a/quickstep/recents_ui_overrides/res/drawable/chip_scrim_gradient.xml b/quickstep/recents_ui_overrides/res/drawable/chip_scrim_gradient.xml
deleted file mode 100644
index 5a2dfb7..0000000
--- a/quickstep/recents_ui_overrides/res/drawable/chip_scrim_gradient.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <gradient
-        android:angle="90"
-        android:endColor="@android:color/transparent"
-        android:startColor="@color/chip_scrim_start_color"
-        android:type="linear" />
-</shape>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/colors.xml b/quickstep/recents_ui_overrides/res/values/colors.xml
deleted file mode 100644
index f03f118..0000000
--- a/quickstep/recents_ui_overrides/res/values/colors.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
--->
-<resources>
-    <color name="chip_hint_foreground_color">#fff</color>
-    <color name="chip_scrim_start_color">#39000000</color>
-
-    <color name="all_apps_label_text">#61000000</color>
-    <color name="all_apps_label_text_dark">#61FFFFFF</color>
-    <color name="all_apps_prediction_row_separator">#3c000000</color>
-    <color name="all_apps_prediction_row_separator_dark">#3cffffff</color>
-</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
deleted file mode 100644
index 9266b06..0000000
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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.
--->
-<resources>
-    <dimen name="chip_hint_border_width">1dp</dimen>
-    <dimen name="chip_hint_corner_radius">20dp</dimen>
-    <dimen name="chip_hint_outer_padding">20dp</dimen>
-    <dimen name="chip_hint_start_padding">10dp</dimen>
-    <dimen name="chip_hint_end_padding">12dp</dimen>
-    <dimen name="chip_hint_horizontal_margin">20dp</dimen>
-    <dimen name="chip_hint_vertical_offset">16dp</dimen>
-    <dimen name="chip_hint_elevation">2dp</dimen>
-    <dimen name="chip_icon_size">16dp</dimen>
-    <dimen name="chip_text_height">26dp</dimen>
-    <dimen name="chip_text_top_padding">4dp</dimen>
-    <dimen name="chip_text_start_padding">10dp</dimen>
-    <dimen name="chip_text_size">14sp</dimen>
-
-    <dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
-    <dimen name="all_apps_label_top_padding">16dp</dimen>
-    <dimen name="all_apps_label_bottom_padding">8dp</dimen>
-    <dimen name="all_apps_label_text_size">14sp</dimen>
-
-    <!-- Minimum distance to swipe to trigger accessibility gesture -->
-    <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
deleted file mode 100644
index 38adf39..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ /dev/null
@@ -1,134 +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;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
- * {@link RecentsView}.
- */
-public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
-
-    public LauncherAppTransitionManagerImpl(Context context) {
-        super(context);
-    }
-
-    @Override
-    protected boolean isLaunchingFromRecents(@NonNull View v,
-            @Nullable RemoteAnimationTargetCompat[] targets) {
-        return mLauncher.getStateManager().getState().overviewUi
-                && findTaskViewToLaunch(mLauncher, v, targets) != null;
-    }
-
-    @Override
-    protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        boolean skipLauncherChanges = !launcherClosing;
-
-        TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
-        PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
-        createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
-                mLauncher.getDepthController(), pa);
-        anim.play(pa.buildAnim());
-
-        Animator childStateAnimation = null;
-        // Found a visible recents task that matches the opening app, lets launch the app from there
-        Animator launcherAnim;
-        final AnimatorListenerAdapter windowAnimEndListener;
-        if (launcherClosing) {
-            launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
-            launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
-            launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
-
-            // Make sure recents gets fixed up by resetting task alphas and scales, etc.
-            windowAnimEndListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mLauncher.getStateManager().moveToRestState();
-                    mLauncher.getStateManager().reapplyState();
-                }
-            };
-        } else {
-            AnimatorPlaybackController controller =
-                    mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL,
-                            RECENTS_LAUNCH_DURATION);
-            controller.dispatchOnStart();
-            childStateAnimation = controller.getTarget();
-            launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
-            windowAnimEndListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mLauncher.getStateManager().goToState(NORMAL, false);
-                }
-            };
-        }
-        anim.play(launcherAnim);
-
-        // Set the current animation first, before adding windowAnimEndListener. Setting current
-        // animation adds some listeners which need to be called before windowAnimEndListener
-        // (the ordering of listeners matter in this case).
-        mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation);
-        anim.addListener(windowAnimEndListener);
-    }
-
-    @Override
-    protected Runnable composeViewContentAnimator(@NonNull AnimatorSet anim, float[] alphas,
-            float[] trans) {
-        RecentsView overview = mLauncher.getOverviewPanel();
-        ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
-                RecentsView.CONTENT_ALPHA, alphas);
-        alpha.setDuration(CONTENT_ALPHA_DURATION);
-        alpha.setInterpolator(LINEAR);
-        anim.play(alpha);
-        overview.setFreezeViewVisibility(true);
-
-        ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
-        transY.setInterpolator(AGGRESSIVE_EASE);
-        transY.setDuration(CONTENT_TRANSLATION_DURATION);
-        anim.play(transY);
-
-        return () -> {
-            overview.setFreezeViewVisibility(false);
-            overview.setTranslationY(0);
-            mLauncher.getStateManager().reapplyState();
-        };
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
deleted file mode 100644
index 8477b10..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
+++ /dev/null
@@ -1,86 +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.appprediction;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP;
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS;
-
-import android.os.UserManager;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.views.ArrowTipView;
-import com.android.systemui.shared.system.LauncherEventUtil;
-
-/**
- * ArrowTip helper aligned just above prediction apps, shown to users that enter all apps for the
- * first time.
- */
-public class AllAppsTipView {
-
-    private static final String ALL_APPS_TIP_SEEN = "launcher.all_apps_tip_seen";
-
-    private static boolean showAllAppsTipIfNecessary(Launcher launcher) {
-        FloatingHeaderView floatingHeaderView = launcher.getAppsView().getFloatingHeaderView();
-        if (!floatingHeaderView.hasVisibleContent()
-                || AbstractFloatingView.getOpenView(launcher,
-                TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE) != null
-                || !launcher.isInState(ALL_APPS)
-                || hasSeenAllAppsTip(launcher)
-                || launcher.getSystemService(UserManager.class).isDemoUser()
-                || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
-            return false;
-        }
-
-        int[] coords = new int[2];
-        floatingHeaderView.findFixedRowByType(PredictionRowView.class).getLocationOnScreen(coords);
-        ArrowTipView arrowTipView = new ArrowTipView(launcher).setOnClosedCallback(() -> {
-            launcher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
-            launcher.getUserEventDispatcher().logActionTip(LauncherEventUtil.DISMISS,
-                    ALL_APPS_PREDICTION_TIPS);
-        });
-        arrowTipView.show(launcher.getString(R.string.all_apps_prediction_tip), coords[1]);
-
-        return true;
-    }
-
-    private static boolean hasSeenAllAppsTip(Launcher launcher) {
-        return launcher.getSharedPrefs().getBoolean(ALL_APPS_TIP_SEEN, false);
-    }
-
-    public static void scheduleShowIfNeeded(Launcher launcher) {
-        if (!hasSeenAllAppsTip(launcher)) {
-            launcher.getStateManager().addStateListener(new StateListener<LauncherState>() {
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    if (finalState == ALL_APPS) {
-                        if (showAllAppsTipIfNecessary(launcher)) {
-                            launcher.getStateManager().removeStateListener(this);
-                        }
-                    }
-                }
-            });
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
deleted file mode 100644
index d200868..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.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.appprediction;
-
-import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
-
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.util.ComponentKey;
-
-public class ComponentKeyMapper {
-
-    protected final ComponentKey componentKey;
-    private final DynamicItemCache mCache;
-
-    public ComponentKeyMapper(ComponentKey key, DynamicItemCache cache) {
-        componentKey = key;
-        mCache = cache;
-    }
-
-    public String getPackage() {
-        return componentKey.componentName.getPackageName();
-    }
-
-    public String getComponentClass() {
-        return componentKey.componentName.getClassName();
-    }
-
-    public ComponentKey getComponentKey() {
-        return componentKey;
-    }
-
-    @Override
-    public String toString() {
-        return componentKey.toString();
-    }
-
-    public ItemInfoWithIcon getApp(AllAppsStore store) {
-        AppInfo item = store.getApp(componentKey);
-        if (item != null) {
-            return item;
-        } else if (getComponentClass().equals(COMPONENT_CLASS_MARKER)) {
-            return mCache.getInstantApp(componentKey.componentName.getPackageName());
-        } else {
-            return mCache.getShortcutInfo(componentKey);
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
deleted file mode 100644
index ab96b1340..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
+++ /dev/null
@@ -1,268 +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.appprediction;
-
-import static android.content.pm.PackageManager.MATCH_INSTANT;
-
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ShortcutInfo;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.InstantAppResolver;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Utility class which loads and caches predicted items like instant apps and shortcuts, before
- * they can be displayed on the UI
- */
-public class DynamicItemCache {
-
-    private static final String TAG = "DynamicItemCache";
-    private static final boolean DEBUG = false;
-    private static final String DEFAULT_URL = "default-url";
-
-    private static final int BG_MSG_LOAD_SHORTCUTS = 1;
-    private static final int BG_MSG_LOAD_INSTANT_APPS = 2;
-
-    private static final int UI_MSG_UPDATE_SHORTCUTS = 1;
-    private static final int UI_MSG_UPDATE_INSTANT_APPS = 2;
-
-    private final Context mContext;
-    private final Handler mWorker;
-    private final Handler mUiHandler;
-    private final InstantAppResolver mInstantAppResolver;
-    private final Runnable mOnUpdateCallback;
-    private final IconCache mIconCache;
-
-    private final Map<ComponentKey, WorkspaceItemInfo> mShortcuts;
-    private final Map<String, InstantAppItemInfo> mInstantApps;
-
-    public DynamicItemCache(Context context, Runnable onUpdateCallback) {
-        mContext = context;
-        mWorker = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage);
-        mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
-        mInstantAppResolver = InstantAppResolver.newInstance(context);
-        mOnUpdateCallback = onUpdateCallback;
-        mIconCache = LauncherAppState.getInstance(mContext).getIconCache();
-
-        mShortcuts = new HashMap<>();
-        mInstantApps = new HashMap<>();
-    }
-
-    public void cacheItems(List<ShortcutKey> shortcutKeys, List<String> pkgNames) {
-        if (!shortcutKeys.isEmpty()) {
-            mWorker.removeMessages(BG_MSG_LOAD_SHORTCUTS);
-            Message.obtain(mWorker, BG_MSG_LOAD_SHORTCUTS, shortcutKeys).sendToTarget();
-        }
-        if (!pkgNames.isEmpty()) {
-            mWorker.removeMessages(BG_MSG_LOAD_INSTANT_APPS);
-            Message.obtain(mWorker, BG_MSG_LOAD_INSTANT_APPS, pkgNames).sendToTarget();
-        }
-    }
-
-    private boolean handleWorkerMessage(Message msg) {
-        switch (msg.what) {
-            case BG_MSG_LOAD_SHORTCUTS: {
-                List<ShortcutKey> shortcutKeys = msg.obj != null ?
-                        (List<ShortcutKey>) msg.obj : Collections.EMPTY_LIST;
-                Map<ShortcutKey, WorkspaceItemInfo> shortcutKeyAndInfos = new ArrayMap<>();
-                for (ShortcutKey shortcutKey : shortcutKeys) {
-                    WorkspaceItemInfo workspaceItemInfo = loadShortcutWorker(shortcutKey);
-                    if (workspaceItemInfo != null) {
-                        shortcutKeyAndInfos.put(shortcutKey, workspaceItemInfo);
-                    }
-                }
-                Message.obtain(mUiHandler, UI_MSG_UPDATE_SHORTCUTS, shortcutKeyAndInfos)
-                        .sendToTarget();
-                return true;
-            }
-            case BG_MSG_LOAD_INSTANT_APPS: {
-                List<String> pkgNames = msg.obj != null ?
-                        (List<String>) msg.obj : Collections.EMPTY_LIST;
-                List<InstantAppItemInfo> instantAppItemInfos = new ArrayList<>();
-                for (String pkgName : pkgNames) {
-                    InstantAppItemInfo instantAppItemInfo = loadInstantApp(pkgName);
-                    if (instantAppItemInfo != null) {
-                        instantAppItemInfos.add(instantAppItemInfo);
-                    }
-                }
-                Message.obtain(mUiHandler, UI_MSG_UPDATE_INSTANT_APPS, instantAppItemInfos)
-                        .sendToTarget();
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    private boolean handleUiMessage(Message msg) {
-        switch (msg.what) {
-            case UI_MSG_UPDATE_SHORTCUTS: {
-                mShortcuts.clear();
-                mShortcuts.putAll((Map<ShortcutKey, WorkspaceItemInfo>) msg.obj);
-                mOnUpdateCallback.run();
-                return true;
-            }
-            case UI_MSG_UPDATE_INSTANT_APPS: {
-                List<InstantAppItemInfo> instantAppItemInfos = (List<InstantAppItemInfo>) msg.obj;
-                mInstantApps.clear();
-                for (InstantAppItemInfo instantAppItemInfo : instantAppItemInfos) {
-                    mInstantApps.put(instantAppItemInfo.getTargetComponent().getPackageName(),
-                            instantAppItemInfo);
-                }
-                mOnUpdateCallback.run();
-                if (DEBUG) {
-                    Log.d(TAG, String.format("Cache size: %d, Cache: %s",
-                            mInstantApps.size(), mInstantApps.toString()));
-                }
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @WorkerThread
-    private WorkspaceItemInfo loadShortcutWorker(ShortcutKey shortcutKey) {
-        List<ShortcutInfo> details = shortcutKey.buildRequest(mContext).query(ShortcutRequest.ALL);
-        if (!details.isEmpty()) {
-            WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
-            mIconCache.getShortcutIcon(si, details.get(0));
-            return si;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "No shortcut found: " + shortcutKey.toString());
-        }
-        return null;
-    }
-
-    private InstantAppItemInfo loadInstantApp(String pkgName) {
-        PackageManager pm = mContext.getPackageManager();
-
-        try {
-            ApplicationInfo ai = pm.getApplicationInfo(pkgName, 0);
-            if (!mInstantAppResolver.isInstantApp(ai)) {
-                return null;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-
-        String url = retrieveDefaultUrl(pkgName, pm);
-        if (url == null) {
-            Log.w(TAG, "no default-url available for pkg " + pkgName);
-            return null;
-        }
-
-        Intent intent = new Intent(Intent.ACTION_VIEW)
-                .addCategory(Intent.CATEGORY_BROWSABLE)
-                .setData(Uri.parse(url));
-        InstantAppItemInfo info = new InstantAppItemInfo(intent, pkgName);
-        IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
-        iconCache.getTitleAndIcon(info, false);
-        if (info.bitmap.icon == null || iconCache.isDefaultIcon(info.bitmap, info.user)) {
-            return null;
-        }
-        return info;
-    }
-
-    @Nullable
-    public static String retrieveDefaultUrl(String pkgName, PackageManager pm) {
-        Intent mainIntent = new Intent().setAction(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_LAUNCHER).setPackage(pkgName);
-        List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
-                mainIntent, MATCH_INSTANT | PackageManager.GET_META_DATA);
-        String url = null;
-        for (ResolveInfo resolveInfo : resolveInfos) {
-            if (resolveInfo.activityInfo.metaData != null
-                    && resolveInfo.activityInfo.metaData.containsKey(DEFAULT_URL)) {
-                url = resolveInfo.activityInfo.metaData.getString(DEFAULT_URL);
-            }
-        }
-        return url;
-    }
-
-    @UiThread
-    public InstantAppItemInfo getInstantApp(String pkgName) {
-        return mInstantApps.get(pkgName);
-    }
-
-    @MainThread
-    public WorkspaceItemInfo getShortcutInfo(ComponentKey key) {
-        return mShortcuts.get(key);
-    }
-
-    /**
-     * requests and caches icons for app targets
-     */
-    public void updateDependencies(List<ComponentKeyMapper> componentKeyMappers,
-            AllAppsStore appsStore, IconCache.ItemInfoUpdateReceiver callback, int itemCount) {
-        List<String> instantAppsToLoad = new ArrayList<>();
-        List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
-        int total = componentKeyMappers.size();
-        for (int i = 0, count = 0; i < total && count < itemCount; i++) {
-            ComponentKeyMapper mapper = componentKeyMappers.get(i);
-            // Update instant apps
-            if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
-                instantAppsToLoad.add(mapper.getPackage());
-                count++;
-            } else if (mapper.getComponentKey() instanceof ShortcutKey) {
-                shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
-                count++;
-            } else {
-                // Reload high res icon
-                AppInfo info = (AppInfo) mapper.getApp(appsStore);
-                if (info != null) {
-                    if (info.usingLowResIcon()) {
-                        mIconCache.updateIconInBackground(callback, info);
-                    }
-                    count++;
-                }
-            }
-        }
-        cacheItems(shortcutsToLoad, instantAppsToLoad);
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
deleted file mode 100644
index 4c7943b..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
+++ /dev/null
@@ -1,276 +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.appprediction;
-
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.annotation.TargetApi;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetEvent;
-import android.app.prediction.AppTargetId;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UserHandle;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.systemui.plugins.AppLaunchEventsPlugin;
-import com.android.systemui.plugins.PluginListener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Subclass of app tracker which publishes the data to the prediction engine and gets back results.
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public class PredictionAppTracker extends AppLaunchTracker
-        implements PluginListener<AppLaunchEventsPlugin> {
-
-    private static final String TAG = "PredictionAppTracker";
-    private static final boolean DBG = false;
-
-    private static final int MSG_INIT = 0;
-    private static final int MSG_DESTROY = 1;
-    private static final int MSG_LAUNCH = 2;
-    private static final int MSG_PREDICT = 3;
-
-    protected final Context mContext;
-    private final Handler mMessageHandler;
-    private final List<AppLaunchEventsPlugin> mAppLaunchEventsPluginsList;
-
-    // Accessed only on worker thread
-    private AppPredictor mHomeAppPredictor;
-    private AppPredictor mRecentsOverviewPredictor;
-
-    public PredictionAppTracker(Context context) {
-        mContext = context;
-        mMessageHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessage);
-        InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
-
-        mMessageHandler.sendEmptyMessage(MSG_INIT);
-
-        mAppLaunchEventsPluginsList = new ArrayList<>();
-        PluginManagerWrapper.INSTANCE.get(context)
-                .addPluginListener(this, AppLaunchEventsPlugin.class, true);
-    }
-
-    @UiThread
-    private void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
-        if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
-            // Reinitialize everything
-            mMessageHandler.sendEmptyMessage(MSG_INIT);
-        }
-    }
-
-    @WorkerThread
-    private void destroy() {
-        if (mHomeAppPredictor != null) {
-            mHomeAppPredictor.destroy();
-            mHomeAppPredictor = null;
-        }
-        if (mRecentsOverviewPredictor != null) {
-            mRecentsOverviewPredictor.destroy();
-            mRecentsOverviewPredictor = null;
-        }
-    }
-
-    @WorkerThread
-    private AppPredictor createPredictor(Client client, int count) {
-        AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
-
-        if (apm == null) {
-            return null;
-        }
-
-        AppPredictor predictor = apm.createAppPredictionSession(
-                new AppPredictionContext.Builder(mContext)
-                        .setUiSurface(client.id)
-                        .setPredictedTargetCount(count)
-                        .setExtras(getAppPredictionContextExtras(client))
-                        .build());
-        predictor.registerPredictionUpdates(mContext.getMainExecutor(),
-                PredictionUiStateManager.INSTANCE.get(mContext).appPredictorCallback(client));
-        predictor.requestPredictionUpdate();
-        return predictor;
-    }
-
-    /**
-     * Override to add custom extras.
-     */
-    @WorkerThread
-    @Nullable
-    public Bundle getAppPredictionContextExtras(Client client) {
-        return null;
-    }
-
-    @WorkerThread
-    private boolean handleMessage(Message msg) {
-        switch (msg.what) {
-            case MSG_INIT: {
-                // Destroy any existing clients
-                destroy();
-
-                // Initialize the clients
-                int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns;
-                mHomeAppPredictor = createPredictor(Client.HOME, count);
-                mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count);
-                return true;
-            }
-            case MSG_DESTROY: {
-                destroy();
-                return true;
-            }
-            case MSG_LAUNCH: {
-                if (mHomeAppPredictor != null) {
-                    mHomeAppPredictor.notifyAppTargetEvent((AppTargetEvent) msg.obj);
-                }
-                return true;
-            }
-            case MSG_PREDICT: {
-                if (mHomeAppPredictor != null) {
-                    String client = (String) msg.obj;
-                    if (Client.HOME.id.equals(client)) {
-                        mHomeAppPredictor.requestPredictionUpdate();
-                    } else {
-                        mRecentsOverviewPredictor.requestPredictionUpdate();
-                    }
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    @UiThread
-    public void onReturnedToHome() {
-        String client = Client.HOME.id;
-        mMessageHandler.removeMessages(MSG_PREDICT, client);
-        Message.obtain(mMessageHandler, MSG_PREDICT, client).sendToTarget();
-        if (DBG) {
-            Log.d(TAG, String.format("Sent immediate message to update %s", client));
-        }
-
-        // Relay onReturnedToHome to every plugin.
-        mAppLaunchEventsPluginsList.forEach(AppLaunchEventsPlugin::onReturnedToHome);
-    }
-
-    @Override
-    @UiThread
-    public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
-                                String container) {
-        // TODO: Use the full shortcut info
-        AppTarget target = new AppTarget.Builder(
-                new AppTargetId("shortcut:" + shortcutId), packageName, user)
-                .setClassName(shortcutId)
-                .build();
-
-        sendLaunch(target, container);
-
-        // Relay onStartShortcut info to every connected plugin.
-        mAppLaunchEventsPluginsList
-                .forEach(plugin -> plugin.onStartShortcut(
-                        packageName,
-                        shortcutId,
-                        user,
-                        container != null ? container : CONTAINER_DEFAULT)
-        );
-
-    }
-
-    @Override
-    @UiThread
-    public void onStartApp(ComponentName cn, UserHandle user, String container) {
-        if (cn != null) {
-            AppTarget target = new AppTarget.Builder(
-                    new AppTargetId("app:" + cn), cn.getPackageName(), user)
-                    .setClassName(cn.getClassName())
-                    .build();
-            sendLaunch(target, container);
-
-            // Relay onStartApp to every connected plugin.
-            mAppLaunchEventsPluginsList
-                    .forEach(plugin -> plugin.onStartApp(
-                            cn,
-                            user,
-                            container != null ? container : CONTAINER_DEFAULT)
-            );
-        }
-    }
-
-    @Override
-    @UiThread
-    public void onDismissApp(ComponentName cn, UserHandle user, String container) {
-        if (cn == null) return;
-        AppTarget target = new AppTarget.Builder(
-                new AppTargetId("app: " + cn), cn.getPackageName(), user)
-                .setClassName(cn.getClassName())
-                .build();
-        sendDismiss(target, container);
-
-        // Relay onDismissApp to every connected plugin.
-        mAppLaunchEventsPluginsList
-                .forEach(plugin -> plugin.onDismissApp(
-                        cn,
-                        user,
-                        container != null ? container : CONTAINER_DEFAULT)
-        );
-    }
-
-    @UiThread
-    private void sendEvent(AppTarget target, String container, int eventId) {
-        AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
-                .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
-                .build();
-        Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
-    }
-
-    @UiThread
-    private void sendLaunch(AppTarget target, String container) {
-        sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH);
-    }
-
-    @UiThread
-    private void sendDismiss(AppTarget target, String container) {
-        sendEvent(target, container, AppTargetEvent.ACTION_DISMISS);
-    }
-
-    @Override
-    public void onPluginConnected(AppLaunchEventsPlugin appLaunchEventsPlugin, Context context) {
-        mAppLaunchEventsPluginsList.add(appLaunchEventsPlugin);
-    }
-
-    @Override
-    public void onPluginDisconnected(AppLaunchEventsPlugin appLaunchEventsPlugin) {
-        mAppLaunchEventsPluginsList.remove(appLaunchEventsPlugin);
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
deleted file mode 100644
index 44691d3..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.appprediction;
-
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newTarget;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.util.IntProperty;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.Interpolator;
-import android.widget.LinearLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.allapps.FloatingHeaderRow;
-import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.anim.AlphaUpdateListener;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.keyboard.FocusIndicatorHelper;
-import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.AppLaunchTracker;
-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.WorkspaceItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.Themes;
-import com.android.quickstep.AnimatedFloat;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-@TargetApi(Build.VERSION_CODES.P)
-public class PredictionRowView extends LinearLayout implements
-        LogContainerProvider, OnDeviceProfileChangeListener, FloatingHeaderRow {
-
-    private static final String TAG = "PredictionRowView";
-
-    private static final IntProperty<PredictionRowView> TEXT_ALPHA =
-            new IntProperty<PredictionRowView>("textAlpha") {
-                @Override
-                public void setValue(PredictionRowView view, int alpha) {
-                    view.setTextAlpha(alpha);
-                }
-
-                @Override
-                public Integer get(PredictionRowView view) {
-                    return view.mIconLastSetTextAlpha;
-                }
-            };
-
-    private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
-            (t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;
-
-    private static final OnClickListener PREDICTION_CLICK_LISTENER =
-            ItemClickHandler.getInstance(AppLaunchTracker.CONTAINER_PREDICTIONS);
-
-    private final Launcher mLauncher;
-    private final PredictionUiStateManager mPredictionUiStateManager;
-    private int mNumPredictedAppsPerRow;
-
-    // The set of predicted app component names
-    private final List<ComponentKeyMapper> mPredictedAppComponents = new ArrayList<>();
-    // The set of predicted apps resolved from the component names and the current set of apps
-    private final ArrayList<ItemInfoWithIcon> mPredictedApps = new ArrayList<>();
-    // Helper to drawing the focus indicator.
-    private final FocusIndicatorHelper mFocusHelper;
-
-    private final int mIconTextColor;
-    private final int mIconFullTextAlpha;
-    private int mIconLastSetTextAlpha;
-    // Might use mIconFullTextAlpha instead of mIconLastSetTextAlpha if we are translucent.
-    private int mIconCurrentTextAlpha;
-
-    private FloatingHeaderView mParent;
-    private boolean mScrolledOut;
-
-    private float mScrollTranslation = 0;
-    private final AnimatedFloat mContentAlphaFactor =
-            new AnimatedFloat(this::updateTranslationAndAlpha);
-    private final AnimatedFloat mOverviewScrollFactor =
-            new AnimatedFloat(this::updateTranslationAndAlpha);
-
-    private boolean mPredictionsEnabled = false;
-
-    public PredictionRowView(@NonNull Context context) {
-        this(context, null);
-    }
-
-    public PredictionRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        setOrientation(LinearLayout.HORIZONTAL);
-
-        mFocusHelper = new SimpleFocusIndicatorHelper(this);
-
-        mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns;
-        mLauncher = Launcher.getLauncher(context);
-        mLauncher.addOnDeviceProfileChangeListener(this);
-
-        mPredictionUiStateManager = PredictionUiStateManager.INSTANCE.get(context);
-
-        mIconTextColor = Themes.getAttrColor(context, android.R.attr.textColorSecondary);
-        mIconFullTextAlpha = Color.alpha(mIconTextColor);
-        mIconCurrentTextAlpha = mIconFullTextAlpha;
-
-        updateVisibility();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        mPredictionUiStateManager.setTargetAppsView(mLauncher.getAppsView());
-        getAppsStore().registerIconContainer(this);
-        AllAppsTipView.scheduleShowIfNeeded(mLauncher);
-    }
-
-    private AllAppsStore getAppsStore() {
-        return mLauncher.getAppsView().getAppsStore();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        mPredictionUiStateManager.setTargetAppsView(null);
-        getAppsStore().unregisterIconContainer(this);
-    }
-
-    public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
-        mParent = parent;
-    }
-
-    private void updateVisibility() {
-        setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getExpectedHeight(),
-                MeasureSpec.EXACTLY));
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        mFocusHelper.draw(canvas);
-        super.dispatchDraw(canvas);
-    }
-
-    @Override
-    public int getExpectedHeight() {
-        return getVisibility() == GONE ? 0 :
-                Launcher.getLauncher(getContext()).getDeviceProfile().allAppsCellHeightPx
-                        + getPaddingTop() + getPaddingBottom();
-    }
-
-    @Override
-    public boolean shouldDraw() {
-        return getVisibility() != GONE;
-    }
-
-    @Override
-    public boolean hasVisibleContent() {
-        return mPredictionsEnabled;
-    }
-
-    /**
-     * Returns the predicted apps.
-     */
-    public List<ItemInfoWithIcon> getPredictedApps() {
-        return mPredictedApps;
-    }
-
-    /**
-     * Sets the current set of predicted apps.
-     *
-     * This can be called before we get the full set of applications, we should merge the results
-     * only in onPredictionsUpdated() which is idempotent.
-     *
-     * If the number of predicted apps is the same as the previous list of predicted apps,
-     * we can optimize by swapping them in place.
-     */
-    public void setPredictedApps(List<ComponentKeyMapper> apps) {
-        mPredictedAppComponents.clear();
-        mPredictedAppComponents.addAll(apps);
-
-        mPredictedApps.clear();
-        mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
-        applyPredictionApps();
-    }
-
-    @Override
-    public void onDeviceProfileChanged(DeviceProfile dp) {
-        mNumPredictedAppsPerRow = dp.inv.numAllAppsColumns;
-        removeAllViews();
-        applyPredictionApps();
-    }
-
-    private void applyPredictionApps() {
-        if (getChildCount() != mNumPredictedAppsPerRow) {
-            while (getChildCount() > mNumPredictedAppsPerRow) {
-                removeViewAt(0);
-            }
-            LayoutInflater inflater = mLauncher.getAppsView().getLayoutInflater();
-            while (getChildCount() < mNumPredictedAppsPerRow) {
-                BubbleTextView icon = (BubbleTextView) inflater.inflate(
-                        R.layout.all_apps_icon, this, false);
-                icon.setOnClickListener(PREDICTION_CLICK_LISTENER);
-                icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
-                icon.setLongPressTimeoutFactor(1f);
-                icon.setOnFocusChangeListener(mFocusHelper);
-
-                LayoutParams lp = (LayoutParams) icon.getLayoutParams();
-                // Ensure the all apps icon height matches the workspace icons in portrait mode.
-                lp.height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
-                lp.width = 0;
-                lp.weight = 1;
-                addView(icon);
-            }
-        }
-
-        int predictionCount = mPredictedApps.size();
-        int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
-
-        for (int i = 0; i < getChildCount(); i++) {
-            BubbleTextView icon = (BubbleTextView) getChildAt(i);
-            icon.reset();
-            if (predictionCount > i) {
-                icon.setVisibility(View.VISIBLE);
-                if (mPredictedApps.get(i) instanceof AppInfo) {
-                    icon.applyFromApplicationInfo((AppInfo) mPredictedApps.get(i));
-                } else if (mPredictedApps.get(i) instanceof WorkspaceItemInfo) {
-                    icon.applyFromWorkspaceItem((WorkspaceItemInfo) mPredictedApps.get(i));
-                }
-                icon.setTextColor(iconColor);
-            } else {
-                icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
-            }
-        }
-
-        boolean predictionsEnabled = predictionCount > 0;
-        if (predictionsEnabled != mPredictionsEnabled) {
-            mPredictionsEnabled = predictionsEnabled;
-            mLauncher.reapplyUi(false /* cancelCurrentAnimation */);
-            updateVisibility();
-        }
-        mParent.onHeightUpdated();
-    }
-
-    private List<ItemInfoWithIcon> processPredictedAppComponents(
-            List<ComponentKeyMapper> components) {
-        if (getAppsStore().getApps().length == 0) {
-            // Apps have not been bound yet.
-            return Collections.emptyList();
-        }
-
-        List<ItemInfoWithIcon> predictedApps = new ArrayList<>();
-        for (ComponentKeyMapper mapper : components) {
-            ItemInfoWithIcon info = mapper.getApp(getAppsStore());
-            if (info != null) {
-                ItemInfoWithIcon predictedApp = info.clone();
-                predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
-                predictedApps.add(predictedApp);
-            } else {
-                if (FeatureFlags.IS_STUDIO_BUILD) {
-                    Log.e(TAG, "Predicted app not found: " + mapper);
-                }
-            }
-            // Stop at the number of predicted apps
-            if (predictedApps.size() == mNumPredictedAppsPerRow) {
-                break;
-            }
-        }
-        return predictedApps;
-    }
-
-    @Override
-    public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
-            ArrayList<LauncherLogProto.Target> parents) {
-        for (int i = 0; i < mPredictedApps.size(); i++) {
-            ItemInfoWithIcon appInfo = mPredictedApps.get(i);
-            if (appInfo == childInfo) {
-                child.predictedRank = i;
-                break;
-            }
-        }
-        parents.add(newContainerTarget(LauncherLogProto.ContainerType.PREDICTION));
-
-        // include where the prediction is coming this used to be Launcher#modifyUserEvent
-        LauncherLogProto.Target parent = newTarget(LauncherLogProto.Target.Type.CONTAINER);
-        LauncherState state = mLauncher.getStateManager().getState();
-        if (state == LauncherState.ALL_APPS) {
-            parent.containerType = LauncherLogProto.ContainerType.ALLAPPS;
-        } else if (state == OVERVIEW) {
-            parent.containerType = LauncherLogProto.ContainerType.TASKSWITCHER;
-        }
-        parents.add(parent);
-    }
-
-    public void setTextAlpha(int textAlpha) {
-        mIconLastSetTextAlpha = textAlpha;
-        if (getAlpha() < 1 && textAlpha > 0) {
-            // If the entire header is translucent, make sure the text is at full opacity so it's
-            // not double-translucent. However, we support keeping the text invisible (alpha == 0).
-            textAlpha = mIconFullTextAlpha;
-        }
-        mIconCurrentTextAlpha = textAlpha;
-        int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
-        for (int i = 0; i < getChildCount(); i++) {
-            ((BubbleTextView) getChildAt(i)).setTextColor(iconColor);
-        }
-    }
-
-    @Override
-    public void setAlpha(float alpha) {
-        super.setAlpha(alpha);
-        // Reapply text alpha so that we update it to be full alpha if the row is now translucent.
-        setTextAlpha(mIconLastSetTextAlpha);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-
-    @Override
-    public void setVerticalScroll(int scroll, boolean isScrolledOut) {
-        mScrolledOut = isScrolledOut;
-        updateTranslationAndAlpha();
-        if (!isScrolledOut) {
-            mScrollTranslation = scroll;
-            updateTranslationAndAlpha();
-        }
-    }
-
-    private void updateTranslationAndAlpha() {
-        if (mPredictionsEnabled) {
-            setTranslationY((1 - mOverviewScrollFactor.value) * mScrollTranslation);
-
-            float factor = ALPHA_FACTOR_INTERPOLATOR.getInterpolation(mOverviewScrollFactor.value);
-            float endAlpha = factor + (1 - factor) * (mScrolledOut ? 0 : 1);
-            setAlpha(mContentAlphaFactor.value * endAlpha);
-            AlphaUpdateListener.updateVisibility(this);
-        }
-    }
-
-    @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
-        // Text follows all apps visibility
-        int textAlpha = hasHeaderExtra && hasAllAppsContent ? mIconFullTextAlpha : 0;
-        setter.setInt(this, TEXT_ALPHA, textAlpha, allAppsFade);
-        setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE,
-                (hasHeaderExtra && !hasAllAppsContent) ? 1 : 0, LINEAR);
-        setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0,
-                headerFade);
-    }
-
-    @Override
-    public void setInsets(Rect insets, DeviceProfile grid) {
-        int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
-                + grid.cellLayoutPaddingLeftRightPx;
-        setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
-    }
-
-    @Override
-    public Class<PredictionRowView> getTypeClass() {
-        return PredictionRowView.class;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
deleted file mode 100644
index a0f6b04..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ /dev/null
@@ -1,370 +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.appprediction;
-
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.content.ComponentName;
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
-import com.android.launcher3.hybridhotseat.HotseatPredictionController;
-import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.MainThreadInitializedObject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.IntStream;
-
-/**
- * Handler responsible to updating the UI due to predicted apps changes. Operations:
- * 1) Pushes the predicted apps to all-apps. If all-apps is visible, waits until it becomes
- * invisible again before applying the changes. This ensures that the UI does not change abruptly
- * in front of the user, even if an app launched and user pressed back button to return to the
- * all-apps UI again.
- * 2) Prefetch high-res icons for predicted apps. This ensures that we have the icons in memory
- * even if all-apps is not opened as they are shown in search UI as well
- * 3) Load instant app if it is not already in memory. As predictions are persisted on disk,
- * instant app will not be in memory when launcher starts.
- * 4) Maintains the current active client id (for the predictions) and all updates are performed on
- * that client id.
- */
-public class PredictionUiStateManager implements StateListener<LauncherState>,
-        ItemInfoUpdateReceiver, OnIDPChangeListener, OnUpdateListener {
-
-    public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
-
-    // TODO (b/129421797): Update the client constants
-    public enum Client {
-        HOME("home"),
-        OVERVIEW("overview");
-
-        public final String id;
-
-        Client(String id) {
-            this.id = id;
-        }
-    }
-
-    public static final MainThreadInitializedObject<PredictionUiStateManager> INSTANCE =
-            new MainThreadInitializedObject<>(PredictionUiStateManager::new);
-
-    private final Context mContext;
-
-    private final DynamicItemCache mDynamicItemCache;
-    private final List[] mPredictionServicePredictions;
-
-    private int mMaxIconsPerRow;
-    private Client mActiveClient;
-
-    private AllAppsContainerView mAppsView;
-
-    private PredictionState mPendingState;
-    private PredictionState mCurrentState;
-
-    private boolean mGettingValidPredictionResults;
-
-    private PredictionUiStateManager(Context context) {
-        mContext = context;
-
-        mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);
-
-        mActiveClient = Client.HOME;
-
-        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        mMaxIconsPerRow = idp.numColumns;
-
-        idp.addOnChangeListener(this);
-        mPredictionServicePredictions = new List[Client.values().length];
-        for (int i = 0; i < mPredictionServicePredictions.length; i++) {
-            mPredictionServicePredictions[i] = Collections.emptyList();
-        }
-        mGettingValidPredictionResults = Utilities.getDevicePrefs(context)
-                .getBoolean(LAST_PREDICTION_ENABLED_STATE, true);
-
-        // Call this last
-        mCurrentState = parseLastState();
-    }
-
-    @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
-        mMaxIconsPerRow = profile.numColumns;
-    }
-
-    public Client getClient() {
-        return mActiveClient;
-    }
-
-    public void switchClient(Client client) {
-        if (client == mActiveClient) {
-            return;
-        }
-        mActiveClient = client;
-        dispatchOnChange(true);
-    }
-
-    public void setTargetAppsView(AllAppsContainerView appsView) {
-        if (mAppsView != null) {
-            mAppsView.getAppsStore().removeUpdateListener(this);
-        }
-        mAppsView = appsView;
-        if (mAppsView != null) {
-            mAppsView.getAppsStore().addUpdateListener(this);
-        }
-        if (mPendingState != null) {
-            applyState(mPendingState);
-            mPendingState = null;
-        } else {
-            applyState(mCurrentState);
-        }
-        updateDependencies(mCurrentState);
-    }
-
-    @Override
-    public void reapplyItemInfo(ItemInfoWithIcon info) { }
-
-    @Override
-    public void onStateTransitionComplete(LauncherState state) {
-        if (mAppsView == null) {
-            return;
-        }
-        if (mPendingState != null && canApplyPredictions(mPendingState)) {
-            applyState(mPendingState);
-            mPendingState = null;
-        }
-        if (mPendingState == null) {
-            Launcher.getLauncher(mAppsView.getContext()).getStateManager()
-                    .removeStateListener(this);
-        }
-    }
-
-    private void scheduleApplyPredictedApps(PredictionState state) {
-        boolean registerListener = mPendingState == null;
-        mPendingState = state;
-        if (registerListener) {
-            // Add a listener and wait until appsView is invisible again.
-            Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this);
-        }
-    }
-
-    private void applyState(PredictionState state) {
-        mCurrentState = state;
-        if (mAppsView != null) {
-            mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
-                    .setPredictedApps(mCurrentState.apps);
-        }
-    }
-
-    private void updatePredictionStateAfterCallback() {
-        boolean validResults = false;
-        for (List l : mPredictionServicePredictions) {
-            validResults |= l != null && !l.isEmpty();
-        }
-        if (validResults != mGettingValidPredictionResults) {
-            mGettingValidPredictionResults = validResults;
-            Utilities.getDevicePrefs(mContext).edit()
-                    .putBoolean(LAST_PREDICTION_ENABLED_STATE, true)
-                    .apply();
-        }
-        dispatchOnChange(true);
-    }
-
-    public AppPredictor.Callback appPredictorCallback(Client client) {
-        return targets -> {
-            mPredictionServicePredictions[client.ordinal()] = targets;
-            updatePredictionStateAfterCallback();
-        };
-    }
-
-    private void dispatchOnChange(boolean changed) {
-        PredictionState newState = changed
-                ? parseLastState()
-                : mPendingState != null && canApplyPredictions(mPendingState)
-                        ? mPendingState
-                        : mCurrentState;
-        if (changed && mAppsView != null && !canApplyPredictions(newState)) {
-            scheduleApplyPredictedApps(newState);
-        } else {
-            applyState(newState);
-        }
-    }
-
-    private PredictionState parseLastState() {
-        PredictionState state = new PredictionState();
-        state.isEnabled = mGettingValidPredictionResults;
-        if (!state.isEnabled) {
-            state.apps = Collections.EMPTY_LIST;
-            return state;
-        }
-
-        state.apps = new ArrayList<>();
-
-        List<AppTarget> appTargets = mPredictionServicePredictions[mActiveClient.ordinal()];
-        if (!appTargets.isEmpty()) {
-            for (AppTarget appTarget : appTargets) {
-                ComponentKey key;
-                if (appTarget.getShortcutInfo() != null) {
-                    key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
-                } else {
-                    key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
-                            appTarget.getClassName()), appTarget.getUser());
-                }
-                state.apps.add(new ComponentKeyMapper(key, mDynamicItemCache));
-            }
-        }
-        updateDependencies(state);
-        return state;
-    }
-
-    private void updateDependencies(PredictionState state) {
-        if (!state.isEnabled || mAppsView == null) {
-            return;
-        }
-        mDynamicItemCache.updateDependencies(state.apps, mAppsView.getAppsStore(), this,
-                mMaxIconsPerRow);
-    }
-
-    @Override
-    public void onAppsUpdated() {
-        dispatchOnChange(false);
-    }
-
-    private boolean canApplyPredictions(PredictionState newState) {
-        if (mAppsView == null) {
-            // If there is no apps view, no need to schedule.
-            return true;
-        }
-        Launcher launcher = Launcher.getLauncher(mAppsView.getContext());
-        PredictionRowView predictionRow = mAppsView.getFloatingHeaderView().
-                findFixedRowByType(PredictionRowView.class);
-        if (!predictionRow.isShown() || predictionRow.getAlpha() == 0 ||
-                launcher.isForceInvisible()) {
-            return true;
-        }
-
-        if (mCurrentState.isEnabled != newState.isEnabled
-                || mCurrentState.apps.isEmpty() != newState.apps.isEmpty()) {
-            // If the visibility of the prediction row is changing, apply immediately.
-            return true;
-        }
-
-        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            // If we are here & mAppsView.isShown() = true, we are probably in all-apps or mid way
-            return false;
-        }
-        if (!launcher.isInState(OVERVIEW) && !launcher.isInState(BACKGROUND_APP)) {
-            // Just a fallback as we dont need to apply instantly, if we are not in the swipe-up UI
-            return false;
-        }
-
-        // Instead of checking against 1, we should check against (1 + delta), where delta accounts
-        // for the nav-bar height (as app icon can still be visible under the nav-bar). Checking
-        // against 1, keeps the logic simple :)
-        return launcher.getAllAppsController().getProgress() > 1;
-    }
-
-    public PredictionState getCurrentState() {
-        return mCurrentState;
-    }
-
-    /**
-     * Returns ranking info for the app within all apps prediction.
-     * Only applicable when {@link ItemInfo#itemType} is one of the followings:
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
-     */
-    public OptionalInt getAllAppsRank(@Nullable ItemInfo itemInfo) {
-        if (itemInfo == null || itemInfo.getTargetComponent() == null || itemInfo.user == null) {
-            return OptionalInt.empty();
-        }
-
-        if (itemInfo.itemType == ITEM_TYPE_APPLICATION
-                || itemInfo.itemType == ITEM_TYPE_SHORTCUT
-                || itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
-            ComponentKey key = new ComponentKey(itemInfo.getTargetComponent(),
-                    itemInfo.user);
-            final List<ComponentKeyMapper> apps = getCurrentState().apps;
-            return IntStream.range(0, apps.size())
-                    .filter(index -> key.equals(apps.get(index).getComponentKey()))
-                    .findFirst();
-        }
-
-        return OptionalInt.empty();
-    }
-
-    /**
-     * Fill in predicted_rank field based on app prediction.
-     * Only applicable when {@link ItemInfo#itemType} is one of the followings:
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
-     * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
-     */
-    public static void fillInPredictedRank(
-            @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
-
-        final PredictionUiStateManager manager = PredictionUiStateManager.INSTANCE.getNoCreate();
-        if (manager == null || itemInfo.getTargetComponent() == null || itemInfo.user == null
-                || (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
-                && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
-            return;
-        }
-        if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) {
-            HotseatPredictionController.encodeHotseatLayoutIntoPredictionRank(itemInfo, target);
-            return;
-        }
-
-        final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
-        final List<ComponentKeyMapper> predictedApps = manager.getCurrentState().apps;
-        IntStream.range(0, predictedApps.size())
-                .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
-                .findFirst()
-                .ifPresent((rank) -> target.predictedRank = 0 - rank);
-    }
-
-    public static class PredictionState {
-
-        public boolean isEnabled;
-        public List<ComponentKeyMapper> apps;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
deleted file mode 100644
index 20e1edc..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
+++ /dev/null
@@ -1,128 +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.hybridhotseat;
-
-import android.content.Context;
-import android.os.Handler;
-import android.util.Log;
-
-import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.MainThreadInitializedObject;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.PrintWriter;
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.Date;
-
-/**
- * Helper class to allow hot seat file logging
- */
-public class HotseatFileLog {
-
-    public static final int LOG_DAYS = 10;
-    private static final String FILE_NAME_PREFIX = "hotseat-log-";
-    private static final DateFormat DATE_FORMAT =
-            DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
-    public static final MainThreadInitializedObject<HotseatFileLog> INSTANCE =
-            new MainThreadInitializedObject<>(HotseatFileLog::new);
-
-
-    private final Handler mHandler = new Handler(
-            Executors.createAndStartNewLooper("hotseat-logger"));
-    private final File mLogsDir;
-    private PrintWriter mCurrentWriter;
-    private String mFileName;
-
-    private HotseatFileLog(Context context) {
-        mLogsDir = context.getFilesDir();
-    }
-
-    /**
-     * Prints log values to disk
-     */
-    public void log(String tag, String msg) {
-        String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
-
-        mHandler.post(() -> {
-            synchronized (this) {
-                PrintWriter writer = getWriter();
-                if (writer != null) {
-                    writer.println(out);
-                }
-            }
-        });
-    }
-
-    private PrintWriter getWriter() {
-        Calendar cal = Calendar.getInstance();
-        String fName = FILE_NAME_PREFIX + (cal.get(Calendar.DAY_OF_YEAR) % 10);
-        if (fName.equals(mFileName)) return mCurrentWriter;
-
-        boolean append = false;
-        File logFile = new File(mLogsDir, fName);
-        if (logFile.exists()) {
-            Calendar modifiedTime = Calendar.getInstance();
-            modifiedTime.setTimeInMillis(logFile.lastModified());
-
-            // If the file was modified more that 36 hours ago, purge the file.
-            // We use instead of 24 to account for day-365 followed by day-1
-            modifiedTime.add(Calendar.HOUR, 36);
-            append = cal.before(modifiedTime);
-        }
-
-
-        if (mCurrentWriter != null) {
-            mCurrentWriter.close();
-        }
-        try {
-            mCurrentWriter = new PrintWriter(new FileWriter(logFile, append));
-            mFileName = fName;
-        } catch (Exception ex) {
-            Log.e("HotseatLogs", "Error writing logs to file", ex);
-            closeWriter();
-        }
-        return mCurrentWriter;
-    }
-
-
-    private synchronized void closeWriter() {
-        mFileName = null;
-        if (mCurrentWriter != null) {
-            mCurrentWriter.close();
-        }
-        mCurrentWriter = null;
-    }
-
-
-    /**
-     * Returns a list of all log files
-     */
-    public synchronized File[] getLogFiles() {
-        File[] files = new File[LOG_DAYS + FileLog.LOG_DAYS];
-        //include file log files here
-        System.arraycopy(FileLog.getLogFiles(), 0, files, 0, FileLog.LOG_DAYS);
-
-        closeWriter();
-        for (int i = 0; i < LOG_DAYS; i++) {
-            files[FileLog.LOG_DAYS + i] = new File(mLogsDir, FILE_NAME_PREFIX + i);
-        }
-        return files;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
deleted file mode 100644
index b94e633..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ /dev/null
@@ -1,723 +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.hybridhotseat;
-
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetEvent;
-import android.content.ComponentName;
-import android.os.Process;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.appprediction.ComponentKeyMapper;
-import com.android.launcher3.appprediction.DynamicItemCache;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
-import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer;
-import com.android.launcher3.logging.InstanceId;
-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.ItemInfoWithIcon;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.uioverrides.PredictedAppIcon;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.views.ArrowTipView;
-import com.android.launcher3.views.Snackbar;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.IntStream;
-
-/**
- * Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows
- * pinning of predicted apps and manages replacement of predicted apps with user drag.
- */
-public class HotseatPredictionController implements DragController.DragListener,
-        View.OnAttachStateChangeListener, SystemShortcut.Factory<QuickstepLauncher>,
-        InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener,
-        IconCache.ItemInfoUpdateReceiver, DragSource {
-
-    private static final String TAG = "PredictiveHotseat";
-    private static final boolean DEBUG = false;
-
-    private static final String PREDICTION_CLIENT = "hotseat";
-    private DropTarget.DragObject mDragObject;
-    private int mHotSeatItemsCount;
-    private int mPredictedSpotsCount = 0;
-
-    private Launcher mLauncher;
-    private final Hotseat mHotseat;
-
-    private final HotseatRestoreHelper mRestoreHelper;
-
-    private List<ComponentKeyMapper> mComponentKeyMappers = new ArrayList<>();
-
-    private DynamicItemCache mDynamicItemCache;
-
-    private final HotseatPredictionModel mPredictionModel;
-    private AppPredictor mAppPredictor;
-    private AllAppsStore mAllAppsStore;
-    private AnimatorSet mIconRemoveAnimators;
-    private boolean mUIUpdatePaused = false;
-    private boolean mIsDestroyed = false;
-
-
-    private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
-
-    private final View.OnLongClickListener mPredictionLongClickListener = v -> {
-        if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
-        if (mLauncher.getWorkspace().isSwitchingState()) return false;
-        if (!mLauncher.getOnboardingPrefs().getBoolean(
-                OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN)) {
-            Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
-                    R.string.hotseat_prediction_settings, null,
-                    () -> mLauncher.startActivity(getSettingsIntent()));
-            mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN);
-            mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-            return true;
-        }
-        // Start the drag
-        mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
-        return true;
-    };
-
-    public HotseatPredictionController(Launcher launcher) {
-        mLauncher = launcher;
-        mHotseat = launcher.getHotseat();
-        mAllAppsStore = mLauncher.getAppsView().getAppsStore();
-        LauncherAppState appState = LauncherAppState.getInstance(launcher);
-        mPredictionModel = (HotseatPredictionModel) appState.getPredictionModel();
-        mAllAppsStore.addUpdateListener(this);
-        mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
-        mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
-        launcher.getDeviceProfile().inv.addOnChangeListener(this);
-        mHotseat.addOnAttachStateChangeListener(this);
-        mRestoreHelper = new HotseatRestoreHelper(mLauncher);
-        if (mHotseat.isAttachedToWindow()) {
-            onViewAttachedToWindow(mHotseat);
-        }
-    }
-
-    /**
-     * Shows appropriate hotseat education based on prediction enabled and migration states.
-     */
-    public void showEdu() {
-        mLauncher.getStateManager().goToState(NORMAL, true, () -> {
-            if (mComponentKeyMappers.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,
-                        mRestoreHelper,
-                        this::createPredictor);
-                eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
-                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() {
-        return !mComponentKeyMappers.isEmpty();
-    }
-
-    @Override
-    public void onViewAttachedToWindow(View view) {
-        mLauncher.getDragController().addDragListener(this);
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(View view) {
-        mLauncher.getDragController().removeDragListener(this);
-    }
-
-    private void fillGapsWithPrediction() {
-        fillGapsWithPrediction(false, null);
-    }
-
-    private void fillGapsWithPrediction(boolean animate, Runnable callback) {
-        if (mUIUpdatePaused || mDragObject != null) {
-            return;
-        }
-        List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
-        if (mComponentKeyMappers.isEmpty() != predictedApps.isEmpty()) {
-            // Safely ignore update as AppsList is not ready yet. This will called again once
-            // apps are ready (HotseatPredictionController#onAppsUpdated)
-            return;
-        }
-        int predictionIndex = 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()) {
-            mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    fillGapsWithPrediction(animate, callback);
-                    mIconRemoveAnimators.removeListener(this);
-                }
-            });
-            return;
-        }
-        for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
-            View child = mHotseat.getChildAt(
-                    mHotseat.getCellXFromOrder(rank),
-                    mHotseat.getCellYFromOrder(rank));
-
-            if (child != null && !isPredictedIcon(child)) {
-                continue;
-            }
-            if (predictedApps.size() <= predictionIndex) {
-                // Remove predicted apps from the past
-                if (isPredictedIcon(child)) {
-                    mHotseat.removeView(child);
-                }
-                continue;
-            }
-            WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++);
-            if (isPredictedIcon(child) && child.isEnabled()) {
-                PredictedAppIcon icon = (PredictedAppIcon) child;
-                icon.applyFromWorkspaceItem(predictedItem);
-                icon.finishBinding(mPredictionLongClickListener);
-            } else {
-                newItems.add(predictedItem);
-            }
-            preparePredictionInfo(predictedItem, rank);
-        }
-        mPredictedSpotsCount = predictionIndex;
-        bindItems(newItems, animate, callback);
-    }
-
-    private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate, Runnable callback) {
-        AnimatorSet animationSet = new AnimatorSet();
-        for (WorkspaceItemInfo item : itemsToAdd) {
-            PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
-            mLauncher.getWorkspace().addInScreenFromBind(icon, item);
-            icon.finishBinding(mPredictionLongClickListener);
-            if (animate) {
-                animationSet.play(ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0.2f, 1));
-            }
-        }
-        if (animate) {
-            if (callback != null) {
-                animationSet.addListener(AnimationSuccessListener.forRunnable(callback));
-            }
-            animationSet.start();
-        } else {
-            if (callback != null) callback.run();
-        }
-    }
-
-    /**
-     * Unregisters callbacks and frees resources
-     */
-    public void destroy() {
-        mIsDestroyed = true;
-        mAllAppsStore.removeUpdateListener(this);
-        mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
-        mHotseat.removeOnAttachStateChangeListener(this);
-        if (mAppPredictor != null) {
-            mAppPredictor.destroy();
-        }
-    }
-
-    /**
-     * start and pauses predicted apps update on the hotseat
-     */
-    public void setPauseUIUpdate(boolean paused) {
-        mUIUpdatePaused = paused;
-        if (!paused) {
-            fillGapsWithPrediction();
-        }
-    }
-
-    /**
-     * Creates App Predictor with all the current apps pinned on the hotseat
-     */
-    public void createPredictor() {
-        AppPredictionManager apm = mLauncher.getSystemService(AppPredictionManager.class);
-        if (apm == null) {
-            return;
-        }
-        if (mAppPredictor != null) {
-            mAppPredictor.destroy();
-            mAppPredictor = null;
-        }
-        WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this);
-
-
-        mPredictionModel.createBundle(bundle -> {
-            if (mIsDestroyed) return;
-            mAppPredictor = apm.createAppPredictionSession(
-                    new AppPredictionContext.Builder(mLauncher)
-                            .setUiSurface(PREDICTION_CLIENT)
-                            .setPredictedTargetCount(mHotSeatItemsCount)
-                            .setExtras(bundle)
-                            .build());
-            mAppPredictor.registerPredictionUpdates(
-                    mLauncher.getApplicationContext().getMainExecutor(),
-                    list -> {
-                        if (controllerRef.get() != null) {
-                            controllerRef.get().setPredictedApps(list);
-                        }
-                    });
-            mAppPredictor.requestPredictionUpdate();
-        });
-        setPauseUIUpdate(false);
-    }
-
-    /**
-     * Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
-     */
-    public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
-        if (hasPredictions() && mAppPredictor != null) {
-            mAppPredictor.requestPredictionUpdate();
-            fillGapsWithPrediction();
-            return;
-        }
-        int count = Math.min(ranks.size(), apps.size());
-        List<WorkspaceItemInfo> items = new ArrayList<>(count);
-        for (int i = 0; i < count; i++) {
-            WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
-            ComponentKey componentKey = new ComponentKey(item.getTargetComponent(), item.user);
-            preparePredictionInfo(item, ranks.get(i));
-            items.add(item);
-
-            mComponentKeyMappers.add(new ComponentKeyMapper(componentKey, mDynamicItemCache));
-        }
-        updateDependencies();
-        bindItems(items, false, null);
-    }
-
-    private void setPredictedApps(List<AppTarget> appTargets) {
-        mComponentKeyMappers.clear();
-        if (appTargets.isEmpty()) {
-            mRestoreHelper.restoreBackup();
-        }
-        StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
-        ArrayList<ComponentKey> componentKeys = new ArrayList<>();
-        for (AppTarget appTarget : appTargets) {
-            ComponentKey key;
-            if (appTarget.getShortcutInfo() != null) {
-                key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
-            } else {
-                key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
-                        appTarget.getClassName()), appTarget.getUser());
-            }
-            componentKeys.add(key);
-            predictionLog.append(key.toString());
-            predictionLog.append(",rank:");
-            predictionLog.append(appTarget.getRank());
-            predictionLog.append("\n");
-            mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
-        }
-        predictionLog.append("]");
-        if (Utilities.IS_DEBUG_DEVICE) {
-            HotseatFileLog.INSTANCE.get(mLauncher).log(TAG, predictionLog.toString());
-        }
-        updateDependencies();
-        fillGapsWithPrediction();
-        mPredictionModel.cachePredictionComponentKeys(componentKeys);
-    }
-
-    private void updateDependencies() {
-        mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this,
-                mHotSeatItemsCount);
-    }
-
-    /**
-     * Pins a predicted app icon into place.
-     */
-    public void pinPrediction(ItemInfo info) {
-        PredictedAppIcon icon = (PredictedAppIcon) mHotseat.getChildAt(
-                mHotseat.getCellXFromOrder(info.rank),
-                mHotseat.getCellYFromOrder(info.rank));
-        if (icon == null) {
-            return;
-        }
-        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo((WorkspaceItemInfo) info);
-        mLauncher.getModelWriter().addItemToDatabase(workspaceItemInfo,
-                LauncherSettings.Favorites.CONTAINER_HOTSEAT, workspaceItemInfo.screenId,
-                workspaceItemInfo.cellX, workspaceItemInfo.cellY);
-        ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
-        icon.pin(workspaceItemInfo);
-        AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(workspaceItemInfo);
-        if (appTarget != null) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
-                    AppTargetEvent.ACTION_PIN, workspaceItemInfo));
-        }
-    }
-
-    private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
-            List<ComponentKeyMapper> components) {
-        AllAppsStore allAppsStore = mLauncher.getAppsView().getAppsStore();
-        if (allAppsStore.getApps().length == 0) {
-            return Collections.emptyList();
-        }
-
-        List<WorkspaceItemInfo> predictedApps = new ArrayList<>();
-        for (ComponentKeyMapper mapper : components) {
-            ItemInfoWithIcon info = mapper.getApp(allAppsStore);
-            if (info instanceof AppInfo) {
-                WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((AppInfo) info);
-                predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
-                predictedApps.add(predictedApp);
-            } else if (info instanceof WorkspaceItemInfo) {
-                WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((WorkspaceItemInfo) info);
-                predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
-                predictedApps.add(predictedApp);
-            } else {
-                if (DEBUG) {
-                    Log.e(TAG, "Predicted app not found: " + mapper);
-                }
-            }
-            // Stop at the number of hotseat items
-            if (predictedApps.size() == mHotSeatItemsCount) {
-                break;
-            }
-        }
-        return predictedApps;
-    }
-
-    private List<PredictedAppIcon> getPredictedIcons() {
-        List<PredictedAppIcon> icons = new ArrayList<>();
-        ViewGroup vg = mHotseat.getShortcutsAndWidgets();
-        for (int i = 0; i < vg.getChildCount(); i++) {
-            View child = vg.getChildAt(i);
-            if (isPredictedIcon(child)) {
-                icons.add((PredictedAppIcon) child);
-            }
-        }
-        return icons;
-    }
-
-    private void removePredictedApps(List<PredictedAppIcon.PredictedIconOutlineDrawing> outlines,
-            ItemInfo draggedInfo) {
-        if (mIconRemoveAnimators != null) {
-            mIconRemoveAnimators.end();
-        }
-        mIconRemoveAnimators = new AnimatorSet();
-        removeOutlineDrawings();
-        for (PredictedAppIcon icon : getPredictedIcons()) {
-            if (!icon.isEnabled()) {
-                continue;
-            }
-            if (icon.getTag().equals(draggedInfo)) {
-                mHotseat.removeView(icon);
-                continue;
-            }
-            int rank = ((WorkspaceItemInfo) icon.getTag()).rank;
-            outlines.add(new PredictedAppIcon.PredictedIconOutlineDrawing(
-                    mHotseat.getCellXFromOrder(rank), mHotseat.getCellYFromOrder(rank), icon));
-            icon.setEnabled(false);
-            ObjectAnimator animator = ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0);
-            animator.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (icon.getParent() != null) {
-                        mHotseat.removeView(icon);
-                    }
-                }
-            });
-            mIconRemoveAnimators.play(animator);
-        }
-        mIconRemoveAnimators.start();
-    }
-
-    private void notifyItemAction(AppTargetEvent event) {
-        if (mAppPredictor != null) {
-            mAppPredictor.notifyAppTargetEvent(event);
-        }
-    }
-
-    @Override
-    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
-        removePredictedApps(mOutlineDrawings, dragObject.dragInfo);
-        mDragObject = dragObject;
-        if (mOutlineDrawings.isEmpty()) return;
-        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
-            mHotseat.addDelegatedCellDrawing(outlineDrawing);
-        }
-        mHotseat.invalidate();
-    }
-
-    /**
-     * Unpins pinned app when it's converted into a folder
-     */
-    public void folderCreatedFromWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
-        AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
-        AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
-        if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
-                    AppTargetEvent.ACTION_PIN, folderInfo));
-        }
-        // using folder info with isTrackedForPrediction as itemInfo.container is already changed
-        // to folder by this point
-        if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
-                    AppTargetEvent.ACTION_UNPIN, folderInfo
-            ));
-        }
-    }
-
-    /**
-     * Pins workspace item created when all folder items are removed but one
-     */
-    public void folderConvertedToWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
-        AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
-        AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
-        if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
-                    AppTargetEvent.ACTION_UNPIN, folderInfo));
-        }
-        if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(itemInfo)) {
-            notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
-                    AppTargetEvent.ACTION_PIN, itemInfo));
-        }
-    }
-
-    @Override
-    public void onDragEnd() {
-        if (mDragObject == null) {
-            return;
-        }
-
-        ItemInfo dragInfo = mDragObject.dragInfo;
-        if (mDragObject.isMoved()) {
-            AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(dragInfo);
-            //always send pin event first to prevent AiAi from predicting an item moved within
-            // the same page
-            if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(dragInfo)) {
-                notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
-                        AppTargetEvent.ACTION_PIN, dragInfo));
-            }
-            if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(
-                    mDragObject.originalDragInfo)) {
-                notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
-                        AppTargetEvent.ACTION_UNPIN, mDragObject.originalDragInfo));
-            }
-        }
-        mDragObject = null;
-        fillGapsWithPrediction(true, this::removeOutlineDrawings);
-    }
-
-
-    @Nullable
-    @Override
-    public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
-            ItemInfo itemInfo) {
-        if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
-            return null;
-        }
-        return new PinPrediction(activity, itemInfo);
-    }
-
-    private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
-        itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
-        itemInfo.rank = rank;
-        itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
-        itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
-        itemInfo.screenId = rank;
-    }
-
-    private void removeOutlineDrawings() {
-        if (mOutlineDrawings.isEmpty()) return;
-        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
-            mHotseat.removeDelegatedCellDrawing(outlineDrawing);
-        }
-        mHotseat.invalidate();
-        mOutlineDrawings.clear();
-    }
-
-    @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
-        if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
-            this.mHotSeatItemsCount = profile.numHotseatIcons;
-            createPredictor();
-        }
-    }
-
-    @Override
-    public void onAppsUpdated() {
-        fillGapsWithPrediction();
-    }
-
-    @Override
-    public void reapplyItemInfo(ItemInfoWithIcon info) {
-    }
-
-    @Override
-    public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
-        //Does nothing
-    }
-
-    @Override
-    public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
-            ArrayList<LauncherLogProto.Target> parents) {
-        mHotseat.fillInLogContainerData(childInfo, child, parents);
-    }
-
-    /**
-     * Logs rank info based on current list of predicted items
-     */
-    public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
-        if (Utilities.IS_DEBUG_DEVICE) {
-            final String pkg = itemInfo.getTargetComponent() != null
-                    ? itemInfo.getTargetComponent().getPackageName() : "unknown";
-            HotseatFileLog.INSTANCE.get(mLauncher).log("UserEvent",
-                    "appLaunch: packageName:" + pkg + ",isWorkApp:" + (itemInfo.user != null
-                            && !Process.myUserHandle().equals(itemInfo.user))
-                            + ",launchLocation:" + itemInfo.container);
-        }
-
-        if (itemInfo.getTargetComponent() == null || itemInfo.user == null) {
-            return;
-        }
-
-        final ComponentKey key = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
-
-        final List<ComponentKeyMapper> predictedApps = new ArrayList<>(mComponentKeyMappers);
-        OptionalInt rank = IntStream.range(0, predictedApps.size())
-                .filter(index -> key.equals(predictedApps.get(index).getComponentKey()))
-                .findFirst();
-        if (!rank.isPresent()) {
-            return;
-        }
-
-        int cardinality = 0;
-        for (PredictedAppIcon icon : getPredictedIcons()) {
-            ItemInfo info = (ItemInfo) icon.getTag();
-            cardinality |= 1 << info.screenId;
-        }
-
-        PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
-        containerBuilder.setCardinality(cardinality);
-        if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
-            containerBuilder.setIndex(rank.getAsInt());
-        }
-        mLauncher.getStatsLogManager().logger()
-                .withInstanceId(instanceId)
-                .withRank(rank.getAsInt())
-                .withContainerInfo(ContainerInfo.newBuilder()
-                        .setPredictedHotseatContainer(containerBuilder)
-                        .build())
-                .log(LAUNCHER_HOTSEAT_RANKED);
-    }
-
-    private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
-
-        private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
-            super(R.drawable.ic_pin, R.string.pin_prediction, target,
-                    itemInfo);
-        }
-
-        @Override
-        public void onClick(View view) {
-            dismissTaskMenuView(mTarget);
-            pinPrediction(mItemInfo);
-        }
-    }
-
-    /**
-     * Fill in predicted_rank field based on app prediction.
-     * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT
-     */
-    public static void encodeHotseatLayoutIntoPredictionRank(
-            @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
-        QuickstepLauncher launcher = QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
-        if (launcher == null || launcher.getHotseatPredictionController() == null
-                || itemInfo.getTargetComponent() == null) {
-            return;
-        }
-        HotseatPredictionController controller = launcher.getHotseatPredictionController();
-
-        final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
-
-        final List<ComponentKeyMapper> predictedApps = controller.mComponentKeyMappers;
-        OptionalInt rank = IntStream.range(0, predictedApps.size())
-                .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
-                .findFirst();
-
-        target.predictedRank = 10000 + (controller.mPredictedSpotsCount * 100)
-                + (rank.isPresent() ? rank.getAsInt() + 1 : 0);
-    }
-
-    private static boolean isPredictedIcon(View view) {
-        return view instanceof PredictedAppIcon && view.getTag() instanceof WorkspaceItemInfo
-                && ((WorkspaceItemInfo) view.getTag()).container
-                == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
deleted file mode 100644
index 9a03d92..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ /dev/null
@@ -1,343 +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.uioverrides;
-
-import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
-import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.hybridhotseat.HotseatPredictionController;
-import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.logging.StatsLogManager.StatsLogger;
-import com.android.launcher3.model.data.AppInfo;
-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.testing.TestProtocol;
-import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
-import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
-import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.Stream;
-
-public class QuickstepLauncher extends BaseQuickstepLauncher {
-
-    public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
-    /**
-     * Reusable command for applying the shelf height on the background thread.
-     */
-    public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
-            SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.createPredictor();
-        }
-    }
-
-    @Override
-    protected void setupViews() {
-        super.setupViews();
-        if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
-            mHotseatPredictionController = new HotseatPredictionController(this);
-        }
-    }
-
-    @Override
-    protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
-        StatsLogger logger = getStatsLogManager()
-                .logger().withItemInfo(info).withInstanceId(instanceId);
-        OptionalInt allAppsRank = PredictionUiStateManager.INSTANCE.get(this).getAllAppsRank(info);
-        allAppsRank.ifPresent(logger::withRank);
-        logger.log(LAUNCHER_APP_LAUNCH_TAP);
-
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
-        }
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        onStateOrResumeChanging(false /* inTransition */);
-    }
-
-    @Override
-    public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
-            @Nullable String sourceContainer) {
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.setPauseUIUpdate(true);
-        }
-        return super.startActivitySafely(v, intent, item, sourceContainer);
-    }
-
-    @Override
-    protected void onActivityFlagsChanged(int changeBits) {
-        super.onActivityFlagsChanged(changeBits);
-        if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
-                | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
-            onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
-        }
-
-        if (mHotseatPredictionController != null && ((changeBits & ACTIVITY_STATE_STARTED) != 0
-                || (changeBits & getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
-            mHotseatPredictionController.setPauseUIUpdate(false);
-        }
-    }
-
-    @Override
-    public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo) {
-        super.folderCreatedFromItem(folder, itemInfo);
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.folderCreatedFromWorkspaceItem(itemInfo, folder.getInfo());
-        }
-    }
-
-    @Override
-    public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {
-        super.folderConvertedToItem(folder, itemInfo);
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.folderConvertedToWorkspaceItem(itemInfo, folder.getInfo());
-        }
-    }
-
-    @Override
-    public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
-        if (mHotseatPredictionController != null) {
-            return Stream.concat(super.getSupportedShortcuts(),
-                    Stream.of(mHotseatPredictionController));
-        } else {
-            return super.getSupportedShortcuts();
-        }
-    }
-
-    /**
-     * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
-     */
-    private void onStateOrResumeChanging(boolean inTransition) {
-        LauncherState state = getStateManager().getState();
-        DeviceProfile profile = getDeviceProfile();
-        boolean willUserBeActive = (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
-        boolean visible = (state == NORMAL || state == OVERVIEW)
-                && (willUserBeActive || isUserActive())
-                && !profile.isVerticalBarLayout()
-                && profile.isPhone && !profile.isLandscape;
-        UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
-                profile.hotseatBarSizePx);
-        if (state == NORMAL && !inTransition) {
-            ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
-        }
-    }
-
-    @Override
-    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) {
-        super.bindPredictedItems(appInfos, ranks);
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.showCachedItems(appInfos, ranks);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.destroy();
-            mHotseatPredictionController = null;
-        }
-    }
-
-    @Override
-    public void onStateSetEnd(LauncherState state) {
-        super.onStateSetEnd(state);
-
-        switch (state.ordinal) {
-            case HINT_STATE_ORDINAL: {
-                Workspace workspace = getWorkspace();
-                boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE;
-                getStateManager().goToState(NORMAL, true,
-                        willMoveScreens ? null : getScrimView()::startDragHandleEducationAnim);
-                if (willMoveScreens) {
-                    workspace.post(workspace::moveToDefaultScreen);
-                }
-                break;
-            }
-            case OVERVIEW_STATE_ORDINAL: {
-                RecentsView recentsView = getOverviewPanel();
-                DiscoveryBounce.showForOverviewIfNeeded(this,
-                        recentsView.getPagedOrientationHandler());
-                RecentsView rv = getOverviewPanel();
-                sendCustomAccessibilityEvent(
-                        rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
-                break;
-            }
-            case QUICK_SWITCH_STATE_ORDINAL: {
-                RecentsView rv = getOverviewPanel();
-                TaskView tasktolaunch = rv.getTaskViewAt(0);
-                if (tasktolaunch != null) {
-                    tasktolaunch.launchTask(false, success -> {
-                        if (!success) {
-                            getStateManager().goToState(OVERVIEW);
-                            tasktolaunch.notifyTaskLaunchFailed(TAG);
-                        } else {
-                            getStateManager().moveToRestState();
-                        }
-                    }, MAIN_EXECUTOR.getHandler());
-                } else {
-                    getStateManager().goToState(NORMAL);
-                }
-                break;
-            }
-
-        }
-    }
-
-    @Override
-    public TouchController[] createTouchControllers() {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.1");
-        }
-        Mode mode = SysUINavigationMode.getMode(this);
-
-        ArrayList<TouchController> list = new ArrayList<>();
-        list.add(getDragController());
-        if (mode == NO_BUTTON) {
-            list.add(new NoButtonQuickSwitchTouchController(this));
-            list.add(new NavBarToHomeTouchController(this));
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.2");
-            }
-            if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
-                if (TestProtocol.sDebugTracing) {
-                    Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.3");
-                }
-                list.add(new NoButtonNavbarToOverviewTouchController(this));
-            } else {
-                list.add(new FlingAndHoldTouchController(this));
-            }
-        } else {
-            if (getDeviceProfile().isVerticalBarLayout()) {
-                list.add(new OverviewToAllAppsTouchController(this));
-                list.add(new LandscapeEdgeSwipeController(this));
-                if (mode.hasGestures) {
-                    list.add(new TransposedQuickSwitchTouchController(this));
-                }
-            } else {
-                list.add(new PortraitStatesTouchController(this,
-                        mode.hasGestures /* allowDragToOverview */));
-                if (mode.hasGestures) {
-                    list.add(new QuickSwitchTouchController(this));
-                }
-            }
-        }
-
-        if (!getDeviceProfile().isMultiWindowMode) {
-            list.add(new StatusBarTouchController(this));
-        }
-
-        list.add(new LauncherTaskViewController(this));
-        return list.toArray(new TouchController[list.size()]);
-    }
-
-    @Override
-    public AtomicAnimationFactory createAtomicAnimationFactory() {
-        return new QuickstepAtomicAnimationFactory(this);
-    }
-
-    private static final class LauncherTaskViewController extends
-            TaskViewTouchController<Launcher> {
-
-        LauncherTaskViewController(Launcher activity) {
-            super(activity);
-        }
-
-        @Override
-        protected boolean isRecentsInteractive() {
-            return mActivity.isInState(OVERVIEW) || mActivity.isInState(OVERVIEW_MODAL_TASK);
-        }
-
-        @Override
-        protected boolean isRecentsModal() {
-            return mActivity.isInState(OVERVIEW_MODAL_TASK);
-        }
-
-        @Override
-        protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
-            mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
-        }
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        super.dump(prefix, fd, writer, args);
-        RecentsView recentsView = getOverviewPanel();
-        writer.println("\nQuickstepLauncher:");
-        writer.println(prefix + "\tmOrientationState: " + (recentsView == null ? "recentsNull" :
-                recentsView.getPagedViewOrientedState()));
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
deleted file mode 100644
index fc9a11b..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
+++ /dev/null
@@ -1,31 +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.uioverrides.states;
-
-import com.android.launcher3.Launcher;
-
-public class OverviewPeekState extends OverviewState {
-    private static final float OVERVIEW_OFFSET = 0.7f;
-
-    public OverviewPeekState(int id) {
-        super(id);
-    }
-
-    @Override
-    public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        return new float[] {NO_SCALE, OVERVIEW_OFFSET};
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
deleted file mode 100644
index d174bfd..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides.states;
-
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-
-import android.content.Context;
-import android.graphics.Rect;
-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.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-
-/**
- * Definition for overview state
- */
-public class OverviewState extends LauncherState {
-
-    protected static final Rect sTempRect = new Rect();
-
-    private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
-            | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE
-            | FLAG_CLOSE_POPUPS;
-
-    public OverviewState(int id) {
-        this(id, STATE_FLAGS);
-    }
-
-    protected OverviewState(int id, int stateFlags) {
-        this(id, ContainerType.TASKSWITCHER, stateFlags);
-    }
-
-    protected OverviewState(int id, int logContainer, int stateFlags) {
-        super(id, logContainer, stateFlags);
-    }
-
-    @Override
-    public int getTransitionDuration(Context context) {
-        // In no-button mode, overview comes in all the way from the left, so give it more time.
-        boolean isNoButtonMode = SysUINavigationMode.INSTANCE.get(context).getMode() == NO_BUTTON;
-        return isNoButtonMode && ENABLE_OVERVIEW_ACTIONS.get() ? 380 : 250;
-    }
-
-    @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;
-        recentsView.getTaskSize(sTempRect);
-        float scale = (float) sTempRect.width() / workspacePageWidth;
-        float parallaxFactor = 0.5f;
-        return new ScaleAndTranslation(scale, 0, -getDefaultSwipeHeight(launcher) * parallaxFactor);
-    }
-
-    @Override
-    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
-        if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
-            DeviceProfile dp = launcher.getDeviceProfile();
-            if (dp.allAppsIconSizePx >= dp.iconSizePx) {
-                return new ScaleAndTranslation(1, 0, 0);
-            } else {
-                float scale = ((float) dp.allAppsIconSizePx) / dp.iconSizePx;
-                // Distance between the screen center (which is the pivotY for hotseat) and the
-                // bottom of the hotseat (which we want to preserve)
-                float distanceFromBottom = dp.heightPx / 2 - dp.hotseatBarBottomPaddingPx;
-                // On scaling, the bottom edge is moved closer to the pivotY. We move the
-                // hotseat back down so that the bottom edge's position is preserved.
-                float translationY = distanceFromBottom * (1 - scale);
-                return new ScaleAndTranslation(scale, 0, translationY);
-            }
-        }
-        return getWorkspaceScaleAndTranslation(launcher);
-    }
-
-    @Override
-    public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        return new float[] {NO_SCALE, NO_OFFSET};
-    }
-
-    @Override
-    public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
-        if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()
-                && removeShelfFromOverview(launcher)) {
-            // Treat the QSB as part of the hotseat so they move together.
-            return getHotseatScaleAndTranslation(launcher);
-        }
-        return super.getQsbScaleAndTranslation(launcher);
-    }
-
-    @Override
-    public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
-        return new PageAlphaProvider(DEACCEL_2) {
-            @Override
-            public float getPageAlpha(int pageIndex) {
-                return 0;
-            }
-        };
-    }
-
-    @Override
-    public int getVisibleElements(Launcher launcher) {
-        RecentsView recentsView = launcher.getOverviewPanel();
-        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ||
-                hideShelfInTwoButtonLandscape(launcher, recentsView.getPagedOrientationHandler())) {
-            return OVERVIEW_BUTTONS;
-        } else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
-        } else {
-            boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null
-                    && launcher.getAppsView().getFloatingHeaderView().hasVisibleContent();
-            return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS
-                    | (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
-        }
-    }
-
-    @Override
-    public float getOverviewScrimAlpha(Launcher launcher) {
-        return 0.5f;
-    }
-
-    @Override
-    public float getVerticalProgress(Launcher launcher) {
-        if ((getVisibleElements(launcher) & ALL_APPS_HEADER_EXTRA) == 0) {
-            // We have no all apps content, so we're still at the fully down progress.
-            return super.getVerticalProgress(launcher);
-        }
-        return getDefaultVerticalProgress(launcher);
-    }
-
-    public static float getDefaultVerticalProgress(Launcher launcher) {
-        return 1 - (getDefaultSwipeHeight(launcher)
-                / launcher.getAllAppsController().getShiftRange());
-    }
-
-    @Override
-    public String getDescription(Launcher launcher) {
-        return launcher.getString(R.string.accessibility_recent_apps);
-    }
-
-    public static float getDefaultSwipeHeight(Launcher launcher) {
-        return LayoutUtils.getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
-    }
-
-    @Override
-    protected float getDepthUnchecked(Context context) {
-        return 1f;
-    }
-
-    @Override
-    public void onBackPressed(Launcher launcher) {
-        TaskView taskView = launcher.<RecentsView>getOverviewPanel().getRunningTaskView();
-        if (taskView != null) {
-            launcher.getUserEventDispatcher().logActionCommand(Action.Command.BACK,
-                    newContainerTarget(ContainerType.OVERVIEW));
-            taskView.launchTask(true);
-        } else {
-            super.onBackPressed(launcher);
-        }
-    }
-
-    public static OverviewState newBackgroundState(int id) {
-        return new BackgroundAppState(id);
-    }
-
-    public static OverviewState newPeekState(int id) {
-        return new OverviewPeekState(id);
-    }
-
-    public static OverviewState newSwitchState(int id) {
-        return new QuickSwitchState(id);
-    }
-
-    /**
-     *  New Overview substate that represents the overview in modal mode (one task shown on its own)
-     */
-    public static OverviewState newModalTaskState(int id) {
-        return new OverviewModalTaskState(id);
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
deleted file mode 100644
index daa1aad..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ /dev/null
@@ -1,246 +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.uioverrides.states;
-
-import static android.view.View.VISIBLE;
-
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.HINT_STATE;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
-import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-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.INSTANT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
-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.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.CellLayout;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.util.RecentsAtomicAnimationFactory;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Animation factory for quickstep specific transitions
- */
-public class QuickstepAtomicAnimationFactory extends
-        RecentsAtomicAnimationFactory<Launcher, LauncherState> {
-
-    // Scale recents takes before animating in
-    private static final float RECENTS_PREPARE_SCALE = 1.33f;
-
-    public static final int INDEX_SHELF_ANIM = RecentsAtomicAnimationFactory.NEXT_INDEX + 0;
-    public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM =
-            RecentsAtomicAnimationFactory.NEXT_INDEX + 1;
-
-    private static final int MY_ANIM_COUNT = 2;
-    protected static final int NEXT_INDEX = RecentsAtomicAnimationFactory.NEXT_INDEX
-            + MY_ANIM_COUNT;
-
-    // Due to use of physics, duration may differ between devices so we need to calculate and
-    // cache the value.
-    private int mHintToNormalDuration = -1;
-
-    public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
-
-    public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) {
-        super(activity, MY_ANIM_COUNT);
-    }
-
-    @Override
-    public Animator createStateElementAnimation(int index, float... values) {
-        switch (index) {
-            case INDEX_SHELF_ANIM: {
-                AllAppsTransitionController aatc = mActivity.getAllAppsController();
-                Animator springAnim = aatc.createSpringAnimation(values);
-
-                if ((OVERVIEW.getVisibleElements(mActivity) & HOTSEAT_ICONS) != 0) {
-                    // Translate hotseat with the shelf until reaching overview.
-                    float overviewProgress = OVERVIEW.getVerticalProgress(mActivity);
-                    ScaleAndTranslation sat = OVERVIEW.getHotseatScaleAndTranslation(mActivity);
-                    float shiftRange = aatc.getShiftRange();
-                    if (values.length == 1) {
-                        values = new float[] {aatc.getProgress(), values[0]};
-                    }
-                    ValueAnimator hotseatAnim = ValueAnimator.ofFloat(values);
-                    hotseatAnim.addUpdateListener(anim -> {
-                        float progress = (Float) anim.getAnimatedValue();
-                        if (progress >= overviewProgress || mActivity.isInState(BACKGROUND_APP)) {
-                            float hotseatShift = (progress - overviewProgress) * shiftRange;
-                            mActivity.getHotseat().setTranslationY(hotseatShift + sat.translationY);
-                        }
-                    });
-                    hotseatAnim.setInterpolator(LINEAR);
-                    hotseatAnim.setDuration(springAnim.getDuration());
-
-                    AnimatorSet anim = new AnimatorSet();
-                    anim.play(hotseatAnim);
-                    anim.play(springAnim);
-                    return anim;
-                }
-
-                return springAnim;
-            }
-            case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
-                StateAnimationConfig config = new StateAnimationConfig();
-                config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
-
-                config.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
-                config.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
-                if ((OVERVIEW.getVisibleElements(mActivity) & HOTSEAT_ICONS) != 0) {
-                    config.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
-                    config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
-                }
-
-                StateManager<LauncherState> stateManager = mActivity.getStateManager();
-                return stateManager.createAtomicAnimation(
-                        stateManager.getCurrentStableState(), OVERVIEW, config);
-            }
-            default:
-                return super.createStateElementAnimation(index, values);
-        }
-    }
-
-    @Override
-    public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
-            StateAnimationConfig config) {
-        if (toState == NORMAL && fromState == OVERVIEW) {
-            config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
-            config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
-            config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
-            config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
-
-            if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
-                config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
-            } else {
-                config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
-            }
-
-            Workspace workspace = mActivity.getWorkspace();
-            // Start from a higher workspace scale, but only if we're invisible so we don't jump.
-            boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
-            if (isWorkspaceVisible) {
-                CellLayout currentChild = (CellLayout) workspace.getChildAt(
-                        workspace.getCurrentPage());
-                isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
-                        && currentChild.getShortcutsAndWidgets().getAlpha() > 0;
-            }
-            if (!isWorkspaceVisible) {
-                workspace.setScaleX(0.92f);
-                workspace.setScaleY(0.92f);
-            }
-            Hotseat hotseat = mActivity.getHotseat();
-            boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
-            if (!isHotseatVisible) {
-                hotseat.setScaleX(0.92f);
-                hotseat.setScaleY(0.92f);
-                if (ENABLE_OVERVIEW_ACTIONS.get()) {
-                    AllAppsContainerView qsbContainer = mActivity.getAppsView();
-                    View qsb = qsbContainer.getSearchView();
-                    boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
-                    if (!qsbVisible) {
-                        qsbContainer.setScaleX(0.92f);
-                        qsbContainer.setScaleY(0.92f);
-                    }
-                }
-            }
-        } else if (toState == NORMAL && fromState == OVERVIEW_PEEK) {
-            // Keep fully visible until the very end (when overview is offscreen) to make invisible.
-            config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
-        } else if (toState == OVERVIEW_PEEK && fromState == NORMAL) {
-            config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
-            config.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
-        } else if ((fromState == NORMAL || fromState == HINT_STATE) && toState == OVERVIEW) {
-            if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
-                config.setInterpolator(ANIM_WORKSPACE_SCALE,
-                        fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
-                config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
-                config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
-            } else {
-                config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
-                config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
-
-                // Scale up the recents, if it is not coming from the side
-                RecentsView overview = mActivity.getOverviewPanel();
-                if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
-                    RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
-                }
-            }
-            config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
-            config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
-            config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
-            config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
-            Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
-                    && removeShelfFromOverview(mActivity)
-                    ? OVERSHOOT_1_2
-                    : OVERSHOOT_1_7;
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
-        } else if (fromState == HINT_STATE && toState == NORMAL) {
-            config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
-            if (mHintToNormalDuration == -1) {
-                ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(),
-                        toState.getWorkspaceScaleAndTranslation(mActivity).scale);
-                mHintToNormalDuration = (int) va.getDuration();
-            }
-            config.duration = Math.max(config.duration, mHintToNormalDuration);
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
deleted file mode 100644
index fac478e..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ /dev/null
@@ -1,279 +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.uioverrides.touchcontrollers;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
-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.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_PAUSE_TO_OVERVIEW_ANIM;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.VibratorWrapper;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Touch controller which handles swipe and hold to go to Overview
- */
-public class FlingAndHoldTouchController extends PortraitStatesTouchController {
-
-    private static final long PEEK_IN_ANIM_DURATION = 240;
-    private static final long PEEK_OUT_ANIM_DURATION = 100;
-    private static final float MAX_DISPLACEMENT_PERCENT = 0.75f;
-
-    protected final MotionPauseDetector mMotionPauseDetector;
-    private final float mMotionPauseMinDisplacement;
-    private final float mMotionPauseMaxDisplacement;
-
-    private AnimatorSet mPeekAnim;
-
-    public FlingAndHoldTouchController(Launcher l) {
-        super(l, false /* allowDragToOverview */);
-        mMotionPauseDetector = new MotionPauseDetector(l);
-        mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
-        mMotionPauseMaxDisplacement = getMotionPauseMaxDisplacement();
-    }
-
-    protected float getMotionPauseMaxDisplacement() {
-        return getShiftRange() * MAX_DISPLACEMENT_PERCENT;
-    }
-
-    @Override
-    protected long getAtomicDuration() {
-        return QuickstepAtomicAnimationFactory.ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
-    }
-
-    @Override
-    public void onDragStart(boolean start, float startDisplacement) {
-        mMotionPauseDetector.clear();
-
-        super.onDragStart(start, startDisplacement);
-
-        if (handlingOverviewAnim()) {
-            mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
-        }
-
-        if (mAtomicAnim != null) {
-            mAtomicAnim.cancel();
-        }
-    }
-
-    protected void onMotionPauseChanged(boolean isPaused) {
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        recentsView.setOverviewStateEnabled(isPaused);
-        if (mPeekAnim != null) {
-            mPeekAnim.cancel();
-        }
-        LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
-        LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
-        long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION;
-
-        StateAnimationConfig config = new StateAnimationConfig();
-        config.duration = peekDuration;
-        config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
-        mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(
-                fromState, toState, config);
-        mPeekAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mPeekAnim = null;
-            }
-        });
-        mPeekAnim.start();
-        VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
-
-        mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(isPaused ? 0 : 1)
-                .setDuration(peekDuration).start();
-    }
-
-    /**
-     * @return Whether we are handling the overview animation, rather than
-     * having it as part of the existing animation to the target state.
-     */
-    protected boolean handlingOverviewAnim() {
-        int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
-        return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
-    }
-
-    @Override
-    protected StateAnimationConfig getConfigForStates(
-            LauncherState fromState, LauncherState toState) {
-        if (fromState == NORMAL && toState == ALL_APPS) {
-            StateAnimationConfig builder = new StateAnimationConfig();
-            // Fade in prediction icons quickly, then rest of all apps after reaching overview.
-            float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
-                    - OVERVIEW.getVerticalProgress(mLauncher);
-            builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
-                    ACCEL,
-                    0,
-                    ALL_APPS_CONTENT_FADE_THRESHOLD));
-            builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
-                    ACCEL,
-                    progressToReachOverview,
-                    progressToReachOverview + ALL_APPS_CONTENT_FADE_THRESHOLD));
-
-            // Get workspace out of the way quickly, to prepare for potential pause.
-            builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
-            builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3);
-            builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3);
-            return builder;
-        } else if (fromState == ALL_APPS && toState == NORMAL) {
-            StateAnimationConfig builder = new StateAnimationConfig();
-            // Keep all apps/predictions opaque until the very end of the transition.
-            float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher);
-            builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
-                    DEACCEL,
-                    progressToReachOverview - ALL_APPS_CONTENT_FADE_THRESHOLD,
-                    progressToReachOverview));
-            builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
-                    DEACCEL,
-                    1 - ALL_APPS_CONTENT_FADE_THRESHOLD,
-                    1));
-            return builder;
-        }
-        return super.getConfigForStates(fromState, toState);
-    }
-
-    @Override
-    public boolean onDrag(float displacement, MotionEvent event) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "FlingAndHoldTouchController");
-        }
-        float upDisplacement = -displacement;
-        mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
-                || upDisplacement < mMotionPauseMinDisplacement
-                || upDisplacement > mMotionPauseMaxDisplacement);
-        mMotionPauseDetector.addPosition(event);
-        return super.onDrag(displacement, event);
-    }
-
-    @Override
-    public void onDragEnd(float velocity) {
-        if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
-            goToOverviewOnDragEnd(velocity);
-        } else {
-            super.onDragEnd(velocity);
-        }
-
-        View searchView = mLauncher.getAppsView().getSearchView();
-        if (searchView instanceof FeedbackHandler) {
-            ((FeedbackHandler) searchView).resetFeedback();
-        }
-        mMotionPauseDetector.clear();
-    }
-
-    protected void goToOverviewOnDragEnd(float velocity) {
-        if (mPeekAnim != null) {
-            mPeekAnim.cancel();
-        }
-
-        Animator overviewAnim = mLauncher.createAtomicAnimationFactory()
-                .createStateElementAnimation(INDEX_PAUSE_TO_OVERVIEW_ANIM);
-        mAtomicAnim = new AnimatorSet();
-        mAtomicAnim.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                if (mCancelled) {
-                    StateAnimationConfig config = new StateAnimationConfig();
-                    config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
-                    config.duration = PEEK_OUT_ANIM_DURATION;
-                    mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(
-                            mFromState, mToState, config);
-                    mPeekAnim.start();
-                }
-                mAtomicAnim = null;
-            }
-        });
-        mAtomicAnim.play(overviewAnim);
-        mAtomicAnim.start();
-    }
-
-    @Override
-    protected void goToTargetState(LauncherState targetState, int logAction) {
-        if (mPeekAnim != null && mPeekAnim.isStarted()) {
-            // Don't jump to the target state until overview is no longer peeking.
-            mPeekAnim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    FlingAndHoldTouchController.super.goToTargetState(targetState, logAction);
-                }
-            });
-        } else {
-            super.goToTargetState(targetState, logAction);
-        }
-    }
-
-    @Override
-    @AnimationFlags
-    protected int updateAnimComponentsOnReinit(@AnimationFlags int animComponents) {
-        if (handlingOverviewAnim()) {
-            // We don't want the state transition to all apps to animate overview,
-            // as that will cause a jump after our atomic animation.
-            return animComponents | SKIP_OVERVIEW;
-        } else {
-            return animComponents;
-        }
-    }
-
-    /**
-     * Interface for views with feedback animation requiring reset
-     */
-    public interface FeedbackHandler {
-
-        /**
-         * reset searchWidget feedback
-         */
-        void resetFeedback();
-    }
-
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
deleted file mode 100644
index 9091168..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides.touchcontrollers;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.TouchInteractionService;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Touch controller from going from OVERVIEW to ALL_APPS.
- *
- * This is used in landscape mode. It is also used in portrait mode for the fallback recents.
- */
-public class OverviewToAllAppsTouchController extends PortraitStatesTouchController {
-
-    public OverviewToAllAppsTouchController(Launcher l) {
-        super(l, true /* allowDragToOverview */);
-    }
-
-    @Override
-    protected boolean canInterceptTouch(MotionEvent ev) {
-        if (mCurrentAnimation != null) {
-            // If we are already animating from a previous state, we can intercept.
-            return true;
-        }
-        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
-            return false;
-        }
-        if (mLauncher.isInState(ALL_APPS)) {
-            // In all-apps only listen if the container cannot scroll itself
-            return mLauncher.getAppsView().shouldContainerScroll(ev);
-        } else if (mLauncher.isInState(NORMAL)) {
-            return (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0;
-        } else if (mLauncher.isInState(OVERVIEW)) {
-            RecentsView rv = mLauncher.getOverviewPanel();
-            return ev.getY() > (rv.getBottom() - rv.getPaddingBottom());
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
-        if (fromState == ALL_APPS && !isDragTowardPositive) {
-            // Should swipe down go to OVERVIEW instead?
-            return TouchInteractionService.isConnected() ?
-                    mLauncher.getStateManager().getLastState() : NORMAL;
-        } else if (isDragTowardPositive) {
-            return ALL_APPS;
-        }
-        return fromState;
-    }
-
-    @Override
-    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
-        return LauncherLogProto.ContainerType.WORKSPACE;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
deleted file mode 100644
index 55f5424..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ /dev/null
@@ -1,175 +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;
-
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.util.Log;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TaskViewSimulator;
-import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
- *
- * @param <T> activity that contains the overview
- */
-final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extends
-        RemoteAnimationProvider {
-
-    private static final long RECENTS_LAUNCH_DURATION = 250;
-    private static final String TAG = "AppToOverviewAnimationProvider";
-
-    private final BaseActivityInterface<?, T> mActivityInterface;
-    // The id of the currently running task that is transitioning to overview.
-    private final RunningTaskInfo mTargetTask;
-    private final RecentsAnimationDeviceState mDeviceState;
-
-    private T mActivity;
-    private RecentsView mRecentsView;
-
-    AppToOverviewAnimationProvider(
-            BaseActivityInterface<?, T> activityInterface, RunningTaskInfo targetTask,
-            RecentsAnimationDeviceState deviceState) {
-        mActivityInterface = activityInterface;
-        mTargetTask = targetTask;
-        mDeviceState = deviceState;
-    }
-
-    /**
-     * Callback for when the activity is ready/initialized.
-     *
-     * @param activity the activity that is ready
-     * @param wasVisible true if it was visible before
-     */
-    boolean onActivityReady(T activity, Boolean wasVisible) {
-        activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTask);
-        AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
-        BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
-                mDeviceState,
-                wasVisible, (controller) -> {
-                    controller.getNormalController().dispatchOnStart();
-                    controller.getNormalController().getAnimationPlayer().end();
-                });
-        factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
-        factory.setRecentsAttachedToAppWindow(true, false);
-        mActivity = activity;
-        mRecentsView = mActivity.getOverviewPanel();
-        return false;
-    }
-
-    /**
-     * Create remote window animation from the currently running app to the overview panel.
-     *
-     * @param appTargets the target apps
-     * @return animation from app to overview
-     */
-    @Override
-    public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
-        PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
-        if (mActivity == null) {
-            Log.e(TAG, "Animation created, before activity");
-            return pa.buildAnim();
-        }
-
-        mRecentsView.setRunningTaskIconScaledDown(true);
-        pa.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                mActivityInterface.onSwipeUpToRecentsComplete();
-                mRecentsView.animateUpRunningTaskIconScale();
-            }
-        });
-
-        DepthController depthController = mActivityInterface.getDepthController();
-        if (depthController != null) {
-            pa.addFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(mActivity),
-                    OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
-        }
-
-        RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
-                wallpaperTargets, MODE_CLOSING);
-
-        // Use the top closing app to determine the insets for the animation
-        RemoteAnimationTargetCompat runningTaskTarget = mTargetTask == null ? null
-                : targets.findTask(mTargetTask.taskId);
-        if (runningTaskTarget == null) {
-            Log.e(TAG, "No closing app");
-            return pa.buildAnim();
-        }
-
-        TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
-        tsv.setDp(mActivity.getDeviceProfile());
-        tsv.setPreview(runningTaskTarget);
-        tsv.setLayoutRotation(mRecentsView.getPagedViewOrientedState().getTouchRotation(),
-                mRecentsView.getPagedViewOrientedState().getDisplayRotation());
-
-        TransformParams params = new TransformParams()
-                .setTargetSet(targets)
-                .setSyncTransactionApplier(new SurfaceTransactionApplier(mActivity.getRootView()));
-
-        AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
-        params.setBaseBuilderProxy((builder, app, p)
-                -> builder.withAlpha(recentsAlpha.value));
-
-        Interpolator taskInterpolator;
-        if (targets.isAnimatingHome()) {
-            params.setHomeBuilderProxy((builder, app, p) -> builder.withAlpha(1 - p.getProgress()));
-
-            taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
-            pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
-        } else {
-            // When animation from app to recents, the recents layer is drawn on top of the app. To
-            // prevent the overlap, we animate the task first and then quickly fade in the recents.
-            taskInterpolator = clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0, 0.8f);
-            pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1,
-                    clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0.8f, 1));
-        }
-
-        pa.addFloat(params, TransformParams.PROGRESS, 0, 1, taskInterpolator);
-        tsv.addAppToOverviewAnim(pa, taskInterpolator);
-        pa.addOnFrameCallback(() -> tsv.apply(params));
-        return pa.buildAnim();
-    }
-
-    /**
-     * Get duration of animation from app to overview.
-     *
-     * @return duration of animation
-     */
-    long getRecentsLaunchDuration() {
-        return RECENTS_LAUNCH_DURATION;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
deleted file mode 100644
index c90f701..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ /dev/null
@@ -1,394 +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;
-
-import static android.widget.Toast.LENGTH_SHORT;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import androidx.annotation.CallSuper;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.VibratorWrapper;
-import com.android.launcher3.util.WindowBounds;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.InputConsumerProxy;
-import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TransformParams;
-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.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Base class for swipe up handler with some utility methods
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
-        extends SwipeUpAnimationLogic implements RecentsAnimationListener {
-
-    private static final String TAG = "BaseSwipeUpHandler";
-
-    protected final BaseActivityInterface<?, T> mActivityInterface;
-    protected final InputConsumerProxy mInputConsumerProxy;
-
-    protected final ActivityInitListener mActivityInitListener;
-
-    protected RecentsAnimationController mRecentsAnimationController;
-    protected RecentsAnimationTargets mRecentsAnimationTargets;
-
-    // Callbacks to be made once the recents animation starts
-    private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
-
-    protected T mActivity;
-    protected Q mRecentsView;
-
-    protected Runnable mGestureEndCallback;
-
-    protected MultiStateCallback mStateCallback;
-
-    protected boolean mCanceled;
-
-    private boolean mRecentsViewScrollLinked = false;
-
-    protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
-            GestureState gestureState, InputConsumerController inputConsumer) {
-        super(context, deviceState, gestureState, new TransformParams());
-        mActivityInterface = gestureState.getActivityInterface();
-        mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
-        mInputConsumerProxy =
-                new InputConsumerProxy(inputConsumer, this::createNewInputProxyHandler);
-    }
-
-    /**
-     * To be called at the end of constructor of subclasses. This calls various methods which can
-     * depend on proper class initialization.
-     */
-    protected void initAfterSubclassConstructor() {
-        initTransitionEndpoints(
-                mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
-    }
-
-    protected void performHapticFeedback() {
-        VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
-    }
-
-    public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
-        return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
-    }
-
-    public void setGestureEndCallback(Runnable gestureEndCallback) {
-        mGestureEndCallback = gestureEndCallback;
-    }
-
-    public abstract Intent getLaunchIntent();
-
-    protected void linkRecentsViewScroll() {
-        SurfaceTransactionApplier.create(mRecentsView, applier -> {
-            mTransformParams.setSyncTransactionApplier(applier);
-            runOnRecentsAnimationStart(() ->
-                    mRecentsAnimationTargets.addReleaseCheck(applier));
-        });
-
-        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
-            if (moveWindowWithRecentsScroll()) {
-                updateFinalShift();
-            }
-        });
-        runOnRecentsAnimationStart(() ->
-                mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
-                        mRecentsAnimationTargets));
-        mRecentsViewScrollLinked = true;
-    }
-
-    protected void startNewTask(Consumer<Boolean> resultCallback) {
-        // Launch the task user scrolled to (mRecentsView.getNextPage()).
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            // We finish recents animation inside launchTask() when live tile is enabled.
-            mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
-                    true /* freezeTaskList */);
-        } else {
-            if (!mCanceled) {
-                TaskView nextTask = mRecentsView.getNextPageTaskView();
-                if (nextTask != null) {
-                    int taskId = nextTask.getTask().key.id;
-                    mGestureState.updateLastStartedTaskId(taskId);
-                    boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
-                            .contains(taskId);
-                    nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
-                            success -> {
-                                resultCallback.accept(success);
-                                if (success) {
-                                    if (hasTaskPreviouslyAppeared) {
-                                        onRestartPreviouslyAppearedTask();
-                                    }
-                                } else {
-                                    mActivityInterface.onLaunchTaskFailed();
-                                    nextTask.notifyTaskLaunchFailed(TAG);
-                                    mRecentsAnimationController.finish(true /* toRecents */, null);
-                                }
-                            }, MAIN_EXECUTOR.getHandler());
-                } else {
-                    mActivityInterface.onLaunchTaskFailed();
-                    Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
-                    mRecentsAnimationController.finish(true /* toRecents */, null);
-                }
-            }
-            mCanceled = false;
-        }
-    }
-
-    /**
-     * Called when we successfully startNewTask() on the task that was previously running. Normally
-     * we call resumeLastTask() when returning to the previously running task, but this handles a
-     * specific edge case: if we switch from A to B, and back to A before B appears, we need to
-     * start A again to ensure it stays on top.
-     */
-    @CallSuper
-    protected void onRestartPreviouslyAppearedTask() {
-        // Finish the controller here, since we won't get onTaskAppeared() for a task that already
-        // appeared.
-        if (mRecentsAnimationController != null) {
-            mRecentsAnimationController.finish(false, null);
-        }
-    }
-
-    /**
-     * Runs the given {@param action} if the recents animation has already started, or queues it to
-     * be run when it is next started.
-     */
-    protected void runOnRecentsAnimationStart(Runnable action) {
-        if (mRecentsAnimationTargets == null) {
-            mRecentsAnimationStartCallbacks.add(action);
-        } else {
-            action.run();
-        }
-    }
-
-    /**
-     * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
-     * @return whether the recents animation has started and there are valid app targets.
-     */
-    protected boolean hasTargets() {
-        return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
-    }
-
-    @Override
-    public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController,
-            RecentsAnimationTargets targets) {
-        mRecentsAnimationController = recentsAnimationController;
-        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) {
-                Rect overviewStackBounds = mActivityInterface
-                        .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
-                dp = dp.getMultiWindowProfile(mContext,
-                        new WindowBounds(overviewStackBounds, targets.homeContentInsets));
-            } else {
-                // If we are not in multi-window mode, home insets should be same as system insets.
-                dp = dp.copy(mContext);
-            }
-            dp.updateInsets(targets.homeContentInsets);
-            dp.updateIsSeascape(mContext);
-            initTransitionEndpoints(dp);
-        }
-
-        // Notify when the animation starts
-        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
-            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
-                action.run();
-            }
-            mRecentsAnimationStartCallbacks.clear();
-        }
-    }
-
-    @Override
-    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        mRecentsAnimationController = null;
-        mRecentsAnimationTargets = null;
-        if (mRecentsView != null) {
-            mRecentsView.setRecentsAnimationTargets(null, null);
-        }
-    }
-
-    @Override
-    public void onRecentsAnimationFinished(RecentsAnimationController controller) {
-        mRecentsAnimationController = null;
-        mRecentsAnimationTargets = null;
-        if (mRecentsView != null) {
-            mRecentsView.setRecentsAnimationTargets(null, null);
-        }
-    }
-
-    @Override
-    public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
-        if (mRecentsAnimationController != null) {
-            if (handleTaskAppeared(appearedTaskTarget)) {
-                mRecentsAnimationController.finish(false /* toRecents */,
-                        null /* onFinishComplete */);
-                mActivityInterface.onLaunchTaskSuccess();
-                ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
-            }
-        }
-    }
-
-    /** @return Whether this was the task we were waiting to appear, and thus handled it. */
-    protected abstract boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget);
-
-    /**
-     * @return The index of the TaskView in RecentsView whose taskId matches the task that will
-     * resume if we finish the controller.
-     */
-    protected int getLastAppearedTaskIndex() {
-        return mGestureState.getLastAppearedTaskId() != -1
-                ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
-                : mRecentsView.getRunningTaskIndex();
-    }
-
-    /**
-     * @return Whether we are continuing a gesture that already landed on a new task,
-     * but before that task appeared.
-     */
-    protected boolean hasStartedNewTask() {
-        return mGestureState.getLastStartedTaskId() != -1;
-    }
-
-    /**
-     * Return true if the window should be translated horizontally if the recents view scrolls
-     */
-    protected abstract boolean moveWindowWithRecentsScroll();
-
-    protected boolean onActivityInit(Boolean alreadyOnHome) {
-        T createdActivity = mActivityInterface.getCreatedActivity();
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
-        }
-        if (createdActivity != null) {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
-            }
-            initTransitionEndpoints(createdActivity.getDeviceProfile());
-        }
-        return true;
-    }
-
-    /**
-     * Called to create a input proxy for the running task
-     */
-    @UiThread
-    protected abstract InputConsumer createNewInputProxyHandler();
-
-    /**
-     * Called when the value of {@link #mCurrentShift} changes
-     */
-    @UiThread
-    public abstract void updateFinalShift();
-
-    /**
-     * Called when motion pause is detected
-     */
-    public abstract void onMotionPauseChanged(boolean isPaused);
-
-    @UiThread
-    public void onGestureStarted(boolean isLikelyToStartNewTask) { }
-
-    @UiThread
-    public abstract void onGestureCancelled();
-
-    @UiThread
-    public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos);
-
-    public abstract void onConsumerAboutToBeSwitched();
-
-    public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { }
-
-    /**
-     * Registers a callback to run when the activity is ready.
-     * @param intent The intent that will be used to start the activity if it doesn't exist already.
-     */
-    public void initWhenReady(Intent intent) {
-        // Preload the plan
-        RecentsModel.INSTANCE.get(mContext).getTasks(null);
-
-        mActivityInitListener.register(intent);
-    }
-
-    /**
-     * Applies the transform on the recents animation
-     */
-    protected void applyWindowTransform() {
-        if (mWindowTransitionController != null) {
-            mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
-        }
-        if (mRecentsAnimationTargets != null) {
-            if (mRecentsViewScrollLinked) {
-                mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
-            }
-            mTaskViewSimulator.apply(mTransformParams);
-        }
-    }
-
-    @Override
-    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
-            HomeAnimationFactory homeAnimationFactory) {
-        RectFSpringAnim anim =
-                super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
-        if (mRecentsAnimationTargets != null) {
-            mRecentsAnimationTargets.addReleaseCheck(anim);
-        }
-        return anim;
-    }
-
-    public interface Factory {
-
-        BaseSwipeUpHandler newHandler(
-                GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
deleted file mode 100644
index f468788..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ /dev/null
@@ -1,1334 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
-import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
-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.util.DefaultDisplay.getSingleFrameMs;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
-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;
-import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
-import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
-import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
-import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
-import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
-import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
-
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.os.Build;
-import android.os.SystemClock;
-import android.view.View;
-import android.view.View.OnApplyWindowInsetsListener;
-import android.view.ViewTreeObserver.OnDrawListener;
-import android.view.WindowInsets;
-import android.view.animation.Interpolator;
-
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.BaseActivityInterface.AnimationFactory;
-import com.android.quickstep.GestureState.GestureEndTarget;
-import com.android.quickstep.inputconsumers.OverviewInputConsumer;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.AnimatorControllerWithResistance;
-import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.ShelfPeekAnim;
-import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.views.LiveTileOverlay;
-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.ActivityManagerWrapper;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TaskInfoCompat;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-
-/**
- * Handles the navigation gestures when Launcher is the default home activity.
- * TODO: Merge this with BaseSwipeUpHandler
- */
-@TargetApi(Build.VERSION_CODES.O)
-public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q extends RecentsView>
-        extends BaseSwipeUpHandler<T, Q> implements OnApplyWindowInsetsListener {
-    private static final String TAG = BaseSwipeUpHandlerV2.class.getSimpleName();
-
-    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
-
-    private static int getFlagForIndex(int index, String name) {
-        if (DEBUG_STATES) {
-            STATE_NAMES[index] = name;
-        }
-        return 1 << index;
-    }
-
-    // Launcher UI related states
-    protected static final int STATE_LAUNCHER_PRESENT =
-            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");
-
-    // Internal initialization states
-    private static final int STATE_APP_CONTROLLER_RECEIVED =
-            getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED");
-
-    // Interaction finish states
-    private static final int STATE_SCALED_CONTROLLER_HOME =
-            getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME");
-    private static final int STATE_SCALED_CONTROLLER_RECENTS =
-            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS");
-
-    protected static final int STATE_HANDLER_INVALIDATED =
-            getFlagForIndex(6, "STATE_HANDLER_INVALIDATED");
-    private static final int STATE_GESTURE_STARTED =
-            getFlagForIndex(7, "STATE_GESTURE_STARTED");
-    private static final int STATE_GESTURE_CANCELLED =
-            getFlagForIndex(8, "STATE_GESTURE_CANCELLED");
-    private static final int STATE_GESTURE_COMPLETED =
-            getFlagForIndex(9, "STATE_GESTURE_COMPLETED");
-
-    private static final int STATE_CAPTURE_SCREENSHOT =
-            getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT");
-    protected static final int STATE_SCREENSHOT_CAPTURED =
-            getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED");
-    private static final int STATE_SCREENSHOT_VIEW_SHOWN =
-            getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN");
-
-    private static final int STATE_RESUME_LAST_TASK =
-            getFlagForIndex(13, "STATE_RESUME_LAST_TASK");
-    private static final int STATE_START_NEW_TASK =
-            getFlagForIndex(14, "STATE_START_NEW_TASK");
-    private static final int STATE_CURRENT_TASK_FINISHED =
-            getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED");
-
-    private static final int LAUNCHER_UI_STATES =
-            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
-
-    public static final long MAX_SWIPE_DURATION = 350;
-    public static final long MIN_OVERSHOOT_DURATION = 120;
-
-    public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
-    private static final float SWIPE_DURATION_MULTIPLIER =
-            Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
-    private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured";
-
-    public static final long RECENTS_ATTACH_DURATION = 300;
-
-    /**
-     * Used as the page index for logging when we return to the last task at the end of the gesture.
-     */
-    private static final int LOG_NO_OP_PAGE_INDEX = -1;
-
-    protected final TaskAnimationManager mTaskAnimationManager;
-
-    // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
-    private RunningWindowAnim mRunningWindowAnim;
-    private boolean mIsShelfPeeking;
-
-    private boolean mContinuingLastGesture;
-
-    private ThumbnailData mTaskSnapshot;
-
-    // Used to control launcher components throughout the swipe gesture.
-    private AnimatorControllerWithResistance mLauncherTransitionController;
-
-    private AnimationFactory mAnimationFactory = (t) -> { };
-
-    private boolean mWasLauncherAlreadyVisible;
-
-    private boolean mPassedOverviewThreshold;
-    private boolean mGestureStarted;
-    private int mLogAction = Touch.SWIPE;
-    private int mLogDirection = Direction.UP;
-    private PointF mDownPos;
-    private boolean mIsLikelyToStartNewTask;
-
-    private final long mTouchTimeMs;
-    private long mLauncherFrameDrawnTime;
-
-    private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
-
-    public BaseSwipeUpHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
-            TaskAnimationManager taskAnimationManager, GestureState gestureState,
-            long touchTimeMs, boolean continuingLastGesture,
-            InputConsumerController inputConsumer) {
-        super(context, deviceState, gestureState, inputConsumer);
-        mTaskAnimationManager = taskAnimationManager;
-        mTouchTimeMs = touchTimeMs;
-        mContinuingLastGesture = continuingLastGesture;
-
-        initAfterSubclassConstructor();
-        initStateCallbacks();
-    }
-
-    private void initStateCallbacks() {
-        mStateCallback = new MultiStateCallback(STATE_NAMES);
-
-        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
-                this::onLauncherPresentAndGestureStarted);
-
-        mStateCallback.runOnceAtState(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
-                this::initializeLauncherAnimationController);
-
-        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
-                this::launcherFrameDrawn);
-
-        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
-                        | STATE_GESTURE_CANCELLED,
-                this::resetStateForAnimationCancel);
-
-        mStateCallback.runOnceAtState(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
-                this::resumeLastTask);
-        mStateCallback.runOnceAtState(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
-                this::startNewTask);
-
-        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_LAUNCHER_DRAWN | STATE_CAPTURE_SCREENSHOT,
-                this::switchToScreenshot);
-
-        mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
-                        | STATE_SCALED_CONTROLLER_RECENTS,
-                this::finishCurrentTransitionToRecents);
-
-        mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
-                        | STATE_SCALED_CONTROLLER_HOME,
-                this::finishCurrentTransitionToHome);
-        mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
-                this::reset);
-
-        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
-                        | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
-                        | STATE_GESTURE_STARTED,
-                this::setupLauncherUiAfterSwipeUpToRecentsAnimation);
-
-        mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED,
-                this::continueComputingRecentsScrollIfNecessary);
-        mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED
-                        | STATE_RECENTS_SCROLLING_FINISHED,
-                this::onSettledOnEndTarget);
-
-        mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
-        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
-                this::invalidateHandlerWithLauncher);
-        mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
-                this::notifyTransitionCancelled);
-
-        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
-                            | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
-                    (b) -> mRecentsView.setRunningTaskHidden(!b));
-        }
-    }
-
-    @Override
-    protected boolean onActivityInit(Boolean alreadyOnHome) {
-        super.onActivityInit(alreadyOnHome);
-        final T activity = mActivityInterface.getCreatedActivity();
-        if (mActivity == activity) {
-            return true;
-        }
-
-        if (mActivity != null) {
-            // The launcher may have been recreated as a result of device rotation.
-            int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
-            initStateCallbacks();
-            mStateCallback.setState(oldState);
-        }
-        mWasLauncherAlreadyVisible = alreadyOnHome;
-        mActivity = activity;
-        // Override the visibility of the activity until the gesture actually starts and we swipe
-        // up, or until we transition home and the home animation is composed
-        if (alreadyOnHome) {
-            mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
-        } else {
-            mActivity.addForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
-        }
-
-        mRecentsView = activity.getOverviewPanel();
-        mRecentsView.setOnPageTransitionEndCallback(null);
-        addLiveTileOverlay();
-
-        mStateCallback.setState(STATE_LAUNCHER_PRESENT);
-        if (alreadyOnHome) {
-            onLauncherStart();
-        } else {
-            activity.runOnceOnStart(this::onLauncherStart);
-        }
-
-        setupRecentsViewUi();
-
-        if (mDeviceState.getNavMode() == TWO_BUTTONS) {
-            // If the device is in two button mode, swiping up will show overview with predictions
-            // so we need to kick off switching to the overview predictions as soon as possible
-            mActivityInterface.updateOverviewPredictionState();
-        }
-        linkRecentsViewScroll();
-
-        return true;
-    }
-
-    @Override
-    protected boolean moveWindowWithRecentsScroll() {
-        return mGestureState.getEndTarget() != HOME;
-    }
-
-    private void onLauncherStart() {
-        final T activity = mActivityInterface.getCreatedActivity();
-        if (mActivity != activity) {
-            return;
-        }
-        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
-            return;
-        }
-        mTaskViewSimulator.setRecentsRotation(mActivity.getDisplay().getRotation());
-
-        // 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.
-        if (mGestureState.getEndTarget() != HOME) {
-            Runnable initAnimFactory = () -> {
-                mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState,
-                        mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
-                maybeUpdateRecentsAttachedState(false /* animate */);
-            };
-            if (mWasLauncherAlreadyVisible) {
-                // Launcher is visible, but might be about to stop. Thus, if we prepare recents
-                // now, it might get overridden by moveToRestState() in onStop(). To avoid this,
-                // wait until the next gesture (and possibly launcher) starts.
-                mStateCallback.runOnceAtState(STATE_GESTURE_STARTED, initAnimFactory);
-            } else {
-                initAnimFactory.run();
-            }
-        }
-        AbstractFloatingView.closeAllOpenViewsExcept(activity, mWasLauncherAlreadyVisible,
-                AbstractFloatingView.TYPE_LISTENER);
-
-        if (mWasLauncherAlreadyVisible) {
-            mStateCallback.setState(STATE_LAUNCHER_DRAWN);
-        } else {
-            Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init");
-            View dragLayer = activity.getDragLayer();
-            dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
-                boolean mHandled = false;
-
-                @Override
-                public void onDraw() {
-                    if (mHandled) {
-                        return;
-                    }
-                    mHandled = true;
-
-                    TraceHelper.INSTANCE.endSection(traceToken);
-                    dragLayer.post(() ->
-                            dragLayer.getViewTreeObserver().removeOnDrawListener(this));
-                    if (activity != mActivity) {
-                        return;
-                    }
-
-                    mStateCallback.setState(STATE_LAUNCHER_DRAWN);
-                }
-            });
-        }
-
-        activity.getRootView().setOnApplyWindowInsetsListener(this);
-        mStateCallback.setState(STATE_LAUNCHER_STARTED);
-    }
-
-    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.
-        setupRecentsViewUi();
-
-        // For the duration of the gesture, in cases where an activity is launched while the
-        // activity is not yet resumed, finish the animation to ensure we get resumed
-        mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
-                mOnDeferredActivityLaunch);
-
-        mGestureState.runOnceAtState(STATE_END_TARGET_SET,
-                () -> mDeviceState.getRotationTouchHelper().
-                        onEndTargetCalculated(mGestureState.getEndTarget(),
-                        mActivityInterface));
-
-        notifyGestureStartedAsync();
-    }
-
-    private void onDeferredActivityLaunch() {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mActivityInterface.switchRunningTaskViewToScreenshot(
-                    null, () -> {
-                        mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
-                    });
-        } else {
-            mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
-        }
-    }
-
-    private void setupRecentsViewUi() {
-        if (mContinuingLastGesture) {
-            updateSysUiFlags(mCurrentShift.value);
-            return;
-        }
-        notifyGestureAnimationStartToRecents();
-    }
-
-    protected void notifyGestureAnimationStartToRecents() {
-        mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
-    }
-
-    private void launcherFrameDrawn() {
-        mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
-    }
-
-    private void initializeLauncherAnimationController() {
-        buildAnimationController();
-
-        Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
-                TraceHelper.FLAG_IGNORE_BINDERS);
-        // Only used in debug builds
-        if (LatencyTrackerCompat.isEnabled(mContext)) {
-            LatencyTrackerCompat.logToggleRecents(
-                    (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
-        }
-        TraceHelper.INSTANCE.endSection(traceToken);
-
-        // This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
-        // high-res thumbnail loader here once we are sure that we will end up in an overview state
-        RecentsModel.INSTANCE.get(mContext).getThumbnailCache()
-                .getHighResLoadingState().setVisible(true);
-    }
-
-    @Override
-    public void onMotionPauseChanged(boolean isPaused) {
-        setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
-
-        if (mDeviceState.isFullyGesturalNavMode() && isPaused) {
-            // In fully gestural nav mode, switch to overview predictions once the user has paused
-            // (this is a no-op if the predictions are already in that state)
-            mActivityInterface.updateOverviewPredictionState();
-        }
-    }
-
-    public void maybeUpdateRecentsAttachedState() {
-        maybeUpdateRecentsAttachedState(true /* animate */);
-    }
-
-    /**
-     * 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.
-     */
-    private void maybeUpdateRecentsAttachedState(boolean animate) {
-        if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
-            return;
-        }
-        RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
-                ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
-                : null;
-        final boolean recentsAttachedToAppWindow;
-        if (mGestureState.getEndTarget() != null) {
-            recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
-        } else if (mContinuingLastGesture
-                && mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
-            recentsAttachedToAppWindow = true;
-        } else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) {
-            // The window is going away so make sure recents is always visible in this case.
-            recentsAttachedToAppWindow = true;
-        } else {
-            recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask;
-        }
-        mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
-
-        // Reapply window transform throughout the attach animation, as the animation affects how
-        // much the window is bound by overscroll (vs moving freely).
-        if (animate) {
-            ValueAnimator reapplyWindowTransformAnim = ValueAnimator.ofFloat(0, 1);
-            reapplyWindowTransformAnim.addUpdateListener(anim -> {
-                if (mRunningWindowAnim == null) {
-                    applyWindowTransform();
-                }
-            });
-            reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
-        } else {
-            applyWindowTransform();
-        }
-    }
-
-    @Override
-    public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
-        setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
-    }
-
-    private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) {
-        if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
-            mIsLikelyToStartNewTask = isLikelyToStartNewTask;
-            maybeUpdateRecentsAttachedState(animate);
-        }
-    }
-
-    @UiThread
-    public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
-        mAnimationFactory.setShelfState(shelfState, interpolator, duration);
-        boolean wasShelfPeeking = mIsShelfPeeking;
-        mIsShelfPeeking = shelfState == PEEK;
-        if (mIsShelfPeeking != wasShelfPeeking) {
-            maybeUpdateRecentsAttachedState();
-        }
-        if (shelfState.shouldPreformHaptic) {
-            performHapticFeedback();
-        }
-    }
-
-    private void buildAnimationController() {
-        if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
-            return;
-        }
-        initTransitionEndpoints(mActivity.getDeviceProfile());
-        mAnimationFactory.createActivityInterface(mTransitionDragLength);
-    }
-
-    /**
-     * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
-     * (it has its own animation).
-     * @return Whether we can create the launcher controller or update its progress.
-     */
-    private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
-        return mGestureState.getEndTarget() != HOME;
-    }
-
-    @Override
-    public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
-        WindowInsets result = view.onApplyWindowInsets(windowInsets);
-        buildAnimationController();
-        return result;
-    }
-
-    private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) {
-        mLauncherTransitionController = anim;
-        mLauncherTransitionController.getNormalController().dispatchOnStart();
-        updateLauncherTransitionProgress();
-    }
-
-    @Override
-    public Intent getLaunchIntent() {
-        return mGestureState.getOverviewIntent();
-    }
-
-    @Override
-    public void updateFinalShift() {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (mRecentsAnimationTargets != null) {
-                LiveTileOverlay.INSTANCE.update(
-                        mTaskViewSimulator.getCurrentCropRect(),
-                        mTaskViewSimulator.getCurrentCornerRadius());
-            }
-        }
-
-        final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
-        if (passed != mPassedOverviewThreshold) {
-            mPassedOverviewThreshold = passed;
-            if (!mDeviceState.isFullyGesturalNavMode()) {
-                performHapticFeedback();
-            }
-        }
-
-        updateSysUiFlags(mCurrentShift.value);
-        applyWindowTransform();
-        updateLauncherTransitionProgress();
-    }
-
-    private void updateLauncherTransitionProgress() {
-        if (mLauncherTransitionController == null
-                || !canCreateNewOrUpdateExistingLauncherTransitionController()) {
-            return;
-        }
-        mLauncherTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
-    }
-
-    /**
-     * @param windowProgress 0 == app, 1 == overview
-     */
-    private void updateSysUiFlags(float windowProgress) {
-        if (mRecentsAnimationController != null && mRecentsView != null) {
-            TaskView runningTask = mRecentsView.getRunningTaskView();
-            TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen();
-            int centermostTaskFlags = centermostTask == null ? 0
-                    : centermostTask.getThumbnail().getSysUiStatusNavFlags();
-            boolean swipeUpThresholdPassed = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
-            boolean quickswitchThresholdPassed = centermostTask != runningTask;
-
-            // We will handle the sysui flags based on the centermost task view.
-            mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
-                    ||  (quickswitchThresholdPassed && centermostTaskFlags != 0));
-            mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
-
-            int sysuiFlags = swipeUpThresholdPassed ? 0 : centermostTaskFlags;
-            mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
-        }
-    }
-
-    @Override
-    public void onRecentsAnimationStart(RecentsAnimationController controller,
-            RecentsAnimationTargets targets) {
-        ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
-        super.onRecentsAnimationStart(controller, targets);
-
-        // Only add the callback to enable the input consumer after we actually have the controller
-        mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
-                mRecentsAnimationController::enableInputConsumer);
-        mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
-
-        mPassedOverviewThreshold = false;
-    }
-
-    @Override
-    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-        ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
-        mActivityInitListener.unregister();
-        mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
-
-        // Defer clearing the controller and the targets until after we've updated the state
-        super.onRecentsAnimationCanceled(thumbnailData);
-    }
-
-    @Override
-    public void onGestureStarted(boolean isLikelyToStartNewTask) {
-        notifyGestureStartedAsync();
-        setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
-        mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
-        mGestureStarted = true;
-    }
-
-    /**
-     * Notifies the launcher that the swipe gesture has started. This can be called multiple times.
-     */
-    @UiThread
-    private void notifyGestureStartedAsync() {
-        final T curActivity = mActivity;
-        if (curActivity != null) {
-            // Once the gesture starts, we can no longer transition home through the button, so
-            // reset the force override of the activity visibility
-            mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
-        }
-    }
-
-    /**
-     * Called as a result on ACTION_CANCEL to return the UI to the start state.
-     */
-    @Override
-    public void onGestureCancelled() {
-        updateDisplacement(0);
-        mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
-        mLogAction = Touch.SWIPE_NOOP;
-        handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
-    }
-
-    /**
-     * @param endVelocity The velocity in the direction of the nav bar to the middle of the screen.
-     * @param velocity The x and y components of the velocity when the gesture ends.
-     * @param downPos The x and y value of where the gesture started.
-     */
-    @Override
-    public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
-        float flingThreshold = mContext.getResources()
-                .getDimension(R.dimen.quickstep_fling_threshold_velocity);
-        boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
-        mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
-
-        mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
-        boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x);
-        if (isVelocityVertical) {
-            mLogDirection = velocity.y < 0 ? Direction.UP : Direction.DOWN;
-        } else {
-            mLogDirection = velocity.x < 0 ? Direction.LEFT : Direction.RIGHT;
-        }
-        mDownPos = downPos;
-        handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
-    }
-
-    @Override
-    protected InputConsumer createNewInputProxyHandler() {
-        endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
-        endLauncherTransitionController();
-
-        StatefulActivity activity = mActivityInterface.getCreatedActivity();
-        return activity == null ? InputConsumer.NO_OP
-                : new OverviewInputConsumer(mGestureState, activity, null, true);
-    }
-
-    private void endRunningWindowAnim(boolean cancel) {
-        if (mRunningWindowAnim != null) {
-            if (cancel) {
-                mRunningWindowAnim.cancel();
-            } else {
-                mRunningWindowAnim.end();
-            }
-        }
-    }
-
-    private void onSettledOnEndTarget() {
-        // Fast-finish the attaching animation if it's still running.
-        maybeUpdateRecentsAttachedState(false);
-        switch (mGestureState.getEndTarget()) {
-            case HOME:
-                mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
-                // Notify swipe-to-home (recents animation) is finished
-                SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished();
-                break;
-            case RECENTS:
-                mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
-                        | STATE_SCREENSHOT_VIEW_SHOWN);
-                break;
-            case NEW_TASK:
-                mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT);
-                break;
-            case LAST_TASK:
-                mStateCallback.setState(STATE_RESUME_LAST_TASK);
-                break;
-        }
-        ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget());
-    }
-
-    @Override
-    protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
-        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
-            return false;
-        }
-        if (mStateCallback.hasStates(STATE_START_NEW_TASK)
-                && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
-            reset();
-            return true;
-        }
-        return false;
-    }
-
-    private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
-            boolean isCancel) {
-        final GestureEndTarget endTarget;
-        final boolean goingToNewTask;
-        if (mRecentsView != null) {
-            if (!hasTargets()) {
-                // If there are no running tasks, then we can assume that this is a continuation of
-                // the last gesture, but after the recents animation has finished
-                goingToNewTask = true;
-            } else {
-                final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
-                final int taskToLaunch = mRecentsView.getNextPage();
-                goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
-            }
-        } else {
-            goingToNewTask = false;
-        }
-        final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
-        if (!isFling) {
-            if (isCancel) {
-                endTarget = LAST_TASK;
-            } else if (mDeviceState.isFullyGesturalNavMode()) {
-                if (mIsShelfPeeking) {
-                    endTarget = RECENTS;
-                } else if (goingToNewTask) {
-                    endTarget = NEW_TASK;
-                } else {
-                    endTarget = !reachedOverviewThreshold ? LAST_TASK : HOME;
-                }
-            } else {
-                endTarget = reachedOverviewThreshold && mGestureStarted
-                        ? RECENTS
-                        : goingToNewTask
-                                ? NEW_TASK
-                                : LAST_TASK;
-            }
-        } else {
-            // If swiping at a diagonal, base end target on the faster velocity.
-            boolean isSwipeUp = endVelocity < 0;
-            boolean willGoToNewTaskOnSwipeUp =
-                    goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
-
-            if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
-                endTarget = HOME;
-            } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsShelfPeeking) {
-                // If swiping at a diagonal, base end target on the faster velocity.
-                endTarget = NEW_TASK;
-            } else if (isSwipeUp) {
-                endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp
-                        ? NEW_TASK : RECENTS;
-            } else {
-                endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
-            }
-        }
-
-        if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) {
-            return LAST_TASK;
-        }
-        return endTarget;
-    }
-
-    @UiThread
-    private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
-            boolean isCancel) {
-        PointF velocityPxPerMs = new PointF(velocity.x / 1000, velocity.y / 1000);
-        long duration = MAX_SWIPE_DURATION;
-        float currentShift = mCurrentShift.value;
-        final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
-                isFling, isCancel);
-        float endShift = endTarget.isLauncher ? 1 : 0;
-        final float startShift;
-        Interpolator interpolator = DEACCEL;
-        if (!isFling) {
-            long expectedDuration = Math.abs(Math.round((endShift - currentShift)
-                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
-            duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
-            startShift = currentShift;
-            interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
-        } else {
-            startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
-                    * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
-            float minFlingVelocity = mContext.getResources()
-                    .getDimension(R.dimen.quickstep_fling_min_velocity);
-            if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
-                if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) {
-                    Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
-                            startShift, endShift, endShift, endVelocity / 1000,
-                            mTransitionDragLength, mContext);
-                    endShift = overshoot.end;
-                    interpolator = overshoot.interpolator;
-                    duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION,
-                            MAX_SWIPE_DURATION);
-                } else {
-                    float distanceToTravel = (endShift - currentShift) * mTransitionDragLength;
-
-                    // we want the page's snap velocity to approximately match the velocity at
-                    // which the user flings, so we scale the duration by a value near to the
-                    // derivative of the scroll interpolator at zero, ie. 2.
-                    long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y));
-                    duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
-
-                    if (endTarget == RECENTS) {
-                        interpolator = OVERSHOOT_1_2;
-                    }
-                }
-            }
-        }
-
-        if (endTarget.isLauncher) {
-            mInputConsumerProxy.enable();
-        }
-        if (endTarget == HOME) {
-            setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
-            duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
-        } else if (endTarget == RECENTS) {
-            LiveTileOverlay.INSTANCE.startIconAnimation();
-            if (mRecentsView != null) {
-                int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
-                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.
-                    mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration));
-                }
-                if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) {
-                    mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION);
-                }
-                duration = Math.max(duration, mRecentsView.getScroller().getDuration());
-            }
-            if (mDeviceState.isFullyGesturalNavMode()) {
-                setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
-            }
-        }
-
-        // Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
-        // or resumeLastTask().
-        if (mRecentsView != null) {
-            mRecentsView.setOnPageTransitionEndCallback(
-                    () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED));
-        } else {
-            mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
-        }
-
-        animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
-    }
-
-    private void doLogGesture(GestureEndTarget endTarget) {
-        DeviceProfile dp = mDp;
-        if (dp == null || mDownPos == null) {
-            // We probably never received an animation controller, skip logging.
-            return;
-        }
-
-        int pageIndex = endTarget == LAST_TASK
-                ? LOG_NO_OP_PAGE_INDEX
-                : mRecentsView.getNextPage();
-        UserEventDispatcher.newInstance(mContext).logStateChangeAction(
-                mLogAction, mLogDirection,
-                (int) mDownPos.x, (int) mDownPos.y,
-                ContainerType.NAVBAR, ContainerType.APP,
-                endTarget.containerType,
-                pageIndex);
-        StatsLogManager.EventEnum event;
-        switch (endTarget) {
-            case HOME:
-                event = LAUNCHER_HOME_GESTURE;
-                break;
-            case RECENTS:
-                event = LAUNCHER_OVERVIEW_GESTURE;
-                break;
-            case LAST_TASK:
-            case NEW_TASK:
-                event = (mLogDirection == Direction.LEFT)
-                        ? LAUNCHER_QUICKSWITCH_LEFT
-                        : LAUNCHER_QUICKSWITCH_RIGHT;
-                break;
-            default:
-                event = IGNORE;
-        }
-        ComponentName componentName = mGestureState.getRunningTask().baseActivity;
-        StatsLogManager.newInstance(mContext).logger()
-                .withSrcState(LAUNCHER_STATE_BACKGROUND)
-                .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType))
-                .withItemInfo(getItemInfo(componentName))
-                .log(event);
-    }
-
-    /**
-     * Builds proto for logging
-     */
-    public WorkspaceItemInfo getItemInfo(ComponentName componentName) {
-        WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
-        placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
-        placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_TASKFOREGROUND;
-        placeholderInfo.intent = new Intent().setComponent(componentName);
-        return placeholderInfo;
-    }
-
-    /** Animates to the given progress, where 0 is the current app and 1 is overview. */
-    @UiThread
-    private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
-            GestureEndTarget target, PointF velocityPxPerMs) {
-        runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration,
-                interpolator, target, velocityPxPerMs));
-    }
-
-    protected abstract HomeAnimationFactory createHomeAnimationFactory(long duration);
-
-    private TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
-        @Override
-        public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
-                boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
-            if (task.taskId == mGestureState.getRunningTaskId()
-                    && TaskInfoCompat.getActivityType(task) != ACTIVITY_TYPE_HOME) {
-                // Since this is an edge case, just cancel and relaunch with default activity
-                // options (since we don't know if there's an associated app icon to launch from)
-                endRunningWindowAnim(true /* cancel */);
-                ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
-                        mActivityRestartListener);
-                ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId, null);
-            }
-        }
-    };
-
-    @UiThread
-    private void animateToProgressInternal(float start, float end, long duration,
-            Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
-        // Set the state, but don't notify until the animation completes
-        mGestureState.setEndTarget(target, false /* isAtomic */);
-        maybeUpdateRecentsAttachedState();
-
-        // If we are transitioning to launcher, then listen for the activity to be restarted while
-        // the transition is in progress
-        if (mGestureState.getEndTarget().isLauncher) {
-            ActivityManagerWrapper.getInstance().registerTaskStackListener(
-                    mActivityRestartListener);
-        }
-
-        if (mGestureState.getEndTarget() == HOME) {
-            HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
-            RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
-            windowAnim.addAnimatorListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (mRecentsAnimationController == null) {
-                        // If the recents animation is interrupted, we still end the running
-                        // animation (not canceled) so this is still called. In that case, we can
-                        // skip doing any future work here for the current gesture.
-                        return;
-                    }
-                    // Finalize the state and notify of the change
-                    mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
-                }
-            });
-            getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
-            windowAnim.start(mContext, velocityPxPerMs);
-            homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
-            mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
-            mLauncherTransitionController = null;
-        } else {
-            ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
-            windowAnim.setDuration(duration).setInterpolator(interpolator);
-            windowAnim.addUpdateListener(valueAnimator -> {
-                computeRecentsScrollIfInvisible();
-            });
-            windowAnim.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (mRecentsAnimationController == null) {
-                        // If the recents animation is interrupted, we still end the running
-                        // animation (not canceled) so this is still called. In that case, we can
-                        // skip doing any future work here for the current gesture.
-                        return;
-                    }
-                    if (mRecentsView != null) {
-                        int taskToLaunch = mRecentsView.getNextPage();
-                        int runningTask = getLastAppearedTaskIndex();
-                        boolean hasStartedNewTask = hasStartedNewTask();
-                        if (target == NEW_TASK && taskToLaunch == runningTask
-                                && !hasStartedNewTask) {
-                            // We are about to launch the current running task, so use LAST_TASK
-                            // state instead of NEW_TASK. This could happen, for example, if our
-                            // scroll is aborted after we determined the target to be NEW_TASK.
-                            mGestureState.setEndTarget(LAST_TASK);
-                        } else if (target == LAST_TASK && hasStartedNewTask) {
-                            // We are about to re-launch the previously running task, but we can't
-                            // just finish the controller like we normally would because that would
-                            // instead resume the last task that appeared, and not ensure that this
-                            // task is restored to the top. To address this, re-launch the task as
-                            // if it were a new task.
-                            mGestureState.setEndTarget(NEW_TASK);
-                        }
-                    }
-                    mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
-                }
-            });
-            windowAnim.start();
-            mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
-        }
-    }
-
-    private void computeRecentsScrollIfInvisible() {
-        if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
-            // Views typically don't compute scroll when invisible as an optimization,
-            // but in our case we need to since the window offset depends on the scroll.
-            mRecentsView.computeScroll();
-        }
-    }
-
-    private void continueComputingRecentsScrollIfNecessary() {
-        if (!mGestureState.hasState(STATE_RECENTS_SCROLLING_FINISHED)
-                && !mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)
-                && !mCanceled) {
-            computeRecentsScrollIfInvisible();
-            mRecentsView.postOnAnimation(this::continueComputingRecentsScrollIfNecessary);
-        }
-    }
-
-    /**
-     * Creates an animation that transforms the current app window into the home app.
-     * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
-     * @param homeAnimationFactory The home animation factory.
-     */
-    @Override
-    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
-            HomeAnimationFactory homeAnimationFactory) {
-        RectFSpringAnim anim =
-                super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
-        anim.addOnUpdateListener((r, p) -> {
-            updateSysUiFlags(Math.max(p, mCurrentShift.value));
-        });
-        anim.addAnimatorListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                if (mActivity != null) {
-                    removeLiveTileOverlay();
-                }
-            }
-
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                if (mRecentsView != null) {
-                    mRecentsView.post(mRecentsView::resetTaskVisuals);
-                }
-                // Make sure recents is in its final state
-                maybeUpdateRecentsAttachedState(false);
-                mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
-            }
-        });
-        return anim;
-    }
-
-    @Override
-    public void onConsumerAboutToBeSwitched() {
-        if (mActivity != null) {
-            // In the off chance that the gesture ends before Launcher is started, we should clear
-            // the callback here so that it doesn't update with the wrong state
-            mActivity.clearRunOnceOnStartCallback();
-            resetLauncherListenersAndOverlays();
-        }
-        if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
-            cancelCurrentAnimation();
-        } else {
-            reset();
-        }
-    }
-
-    public boolean isCanceled() {
-        return mCanceled;
-    }
-
-    @UiThread
-    private void resumeLastTask() {
-        mRecentsAnimationController.finish(false /* toRecents */, null);
-        ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
-        doLogGesture(LAST_TASK);
-        reset();
-    }
-
-    @UiThread
-    private void startNewTask() {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mRecentsAnimationController.finish(true /* toRecents */, this::startNewTaskInternal);
-        } else {
-            startNewTaskInternal();
-        }
-    }
-
-    @UiThread
-    private void startNewTaskInternal() {
-        startNewTask(success -> {
-            if (!success) {
-                reset();
-                // We couldn't launch the task, so take user to overview so they can
-                // decide what to do instead of staying in this broken state.
-                endLauncherTransitionController();
-                updateSysUiFlags(1 /* windowProgress == overview */);
-            }
-            doLogGesture(NEW_TASK);
-        });
-    }
-
-    @Override
-    protected void onRestartPreviouslyAppearedTask() {
-        super.onRestartPreviouslyAppearedTask();
-        reset();
-    }
-
-    private void reset() {
-        mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
-    }
-
-    /**
-     * Cancels any running animation so that the active target can be overriden by a new swipe
-     * handle (in case of quick switch).
-     */
-    private void cancelCurrentAnimation() {
-        mCanceled = true;
-        mCurrentShift.cancelAnimation();
-    }
-
-    private void invalidateHandler() {
-        mInputConsumerProxy.destroy();
-        endRunningWindowAnim(false /* cancel */);
-
-        if (mGestureEndCallback != null) {
-            mGestureEndCallback.run();
-        }
-
-        mActivityInitListener.unregister();
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
-        mTaskSnapshot = null;
-    }
-
-    private void invalidateHandlerWithLauncher() {
-        endLauncherTransitionController();
-
-        mRecentsView.onGestureAnimationEnd();
-        resetLauncherListenersAndOverlays();
-    }
-
-    private void endLauncherTransitionController() {
-        setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
-        if (mLauncherTransitionController != null) {
-            // End the animation, but stay at the same visual progress.
-            mLauncherTransitionController.getNormalController().dispatchSetInterpolator(
-                    t -> Utilities.boundToRange(mCurrentShift.value, 0, 1));
-            mLauncherTransitionController.getNormalController().getAnimationPlayer().end();
-            mLauncherTransitionController = null;
-        }
-    }
-
-    private void resetLauncherListenersAndOverlays() {
-        // Reset the callback for deferred activity launches
-        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mActivityInterface.setOnDeferredActivityLaunchCallback(null);
-        }
-        mActivity.getRootView().setOnApplyWindowInsetsListener(null);
-        removeLiveTileOverlay();
-    }
-
-    private void notifyTransitionCancelled() {
-        mAnimationFactory.onTransitionCancelled();
-    }
-
-    private void resetStateForAnimationCancel() {
-        boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
-        mActivityInterface.onTransitionCancelled(wasVisible);
-
-        // Leave the pending invisible flag, as it may be used by wallpaper open animation.
-        mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
-    }
-
-    protected void switchToScreenshot() {
-        final int runningTaskId = mGestureState.getRunningTaskId();
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (mRecentsAnimationController != null) {
-                mRecentsAnimationController.getController().setWillFinishToHome(true);
-                // Update the screenshot of the task
-                if (mTaskSnapshot == null) {
-                    mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
-                }
-                mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot, false /* refreshNow */);
-            }
-            mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
-        } else if (!hasTargets()) {
-            // If there are no targets, then we don't need to capture anything
-            mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
-        } else {
-            boolean finishTransitionPosted = false;
-            if (mRecentsAnimationController != null) {
-                // Update the screenshot of the task
-                if (mTaskSnapshot == null) {
-                    mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
-                }
-                final TaskView taskView;
-                if (mGestureState.getEndTarget() == HOME) {
-                    // Capture the screenshot before finishing the transition to home to ensure it's
-                    // taken in the correct orientation, but no need to update the thumbnail.
-                    taskView = null;
-                } else {
-                    taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot);
-                }
-                if (taskView != null && !mCanceled) {
-                    // Defer finishing the animation until the next launcher frame with the
-                    // new thumbnail
-                    finishTransitionPosted = ViewUtils.postDraw(taskView,
-                            () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
-                                    this::isCanceled);
-                }
-            }
-            if (!finishTransitionPosted) {
-                // If we haven't posted a draw callback, set the state immediately.
-                Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
-                        TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
-                mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
-                TraceHelper.INSTANCE.endSection(traceToken);
-            }
-        }
-    }
-
-    private void finishCurrentTransitionToRecents() {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
-        } else if (!hasTargets() || mRecentsAnimationController == null) {
-            // If there are no targets or the animation not started, then there is nothing to finish
-            mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
-        } else {
-            mRecentsAnimationController.finish(true /* toRecents */,
-                    () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
-        }
-        ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
-    }
-
-    private void finishCurrentTransitionToHome() {
-        if (!hasTargets() || mRecentsAnimationController == null) {
-            // If there are no targets or the animation not started, then there is nothing to finish
-            mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
-        } else {
-            finishRecentsControllerToHome(
-                    () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
-        }
-        ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
-        doLogGesture(HOME);
-    }
-
-    protected abstract void finishRecentsControllerToHome(Runnable callback);
-
-    private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
-        endLauncherTransitionController();
-        mActivityInterface.onSwipeUpToRecentsComplete();
-        if (mRecentsAnimationController != null) {
-            mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
-                    true /* screenshot */);
-        }
-        mRecentsView.onSwipeUpAnimationSuccess();
-
-        SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
-        doLogGesture(RECENTS);
-        reset();
-    }
-
-    private void addLiveTileOverlay() {
-        if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
-            mRecentsView.setLiveTileOverlayAttached(true);
-        }
-    }
-
-    private void removeLiveTileOverlay() {
-        LiveTileOverlay.INSTANCE.detach(mActivity.getRootView().getOverlay());
-        mRecentsView.setLiveTileOverlayAttached(false);
-    }
-
-    private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
-        return app.isNotInRecents
-                || app.activityType == ACTIVITY_TYPE_HOME;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
deleted file mode 100644
index 052d0a6..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ /dev/null
@@ -1,144 +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.LauncherState.NORMAL;
-import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
-
-import android.animation.AnimatorSet;
-import android.content.Context;
-import android.graphics.RectF;
-import android.os.UserHandle;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.views.FloatingIconView;
-import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.InputConsumerController;
-
-/**
- * Temporary class to allow easier refactoring
- */
-public class LauncherSwipeHandlerV2 extends
-        BaseSwipeUpHandlerV2<BaseQuickstepLauncher, RecentsView> {
-
-    public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
-            TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
-            boolean continuingLastGesture, InputConsumerController inputConsumer) {
-        super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
-                continuingLastGesture, inputConsumer);
-    }
-
-
-    @Override
-    protected HomeAnimationFactory createHomeAnimationFactory(long duration) {
-        HomeAnimationFactory homeAnimFactory;
-        if (mActivity != null) {
-            final TaskView runningTaskView = mRecentsView.getRunningTaskView();
-            final View workspaceView;
-            if (runningTaskView != null
-                    && runningTaskView.getTask().key.getComponent() != null) {
-                workspaceView = mActivity.getWorkspace().getFirstMatchForAppClose(
-                        runningTaskView.getTask().key.getComponent().getPackageName(),
-                        UserHandle.of(runningTaskView.getTask().key.userId));
-            } else {
-                workspaceView = null;
-            }
-            final RectF iconLocation = new RectF();
-            boolean canUseWorkspaceView =
-                    workspaceView != null && workspaceView.isAttachedToWindow();
-            FloatingIconView floatingIconView = canUseWorkspaceView
-                    ? FloatingIconView.getFloatingIconView(mActivity, workspaceView,
-                    true /* hideOriginal */, iconLocation, false /* isOpening */)
-                    : null;
-
-            mActivity.getRootView().setForceHideBackArrow(true);
-            mActivity.setHintUserWillBeActive();
-
-            if (canUseWorkspaceView) {
-                // 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.
-                float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
-                homeAnimFactory = new LauncherHomeAnimationFactory() {
-                    @Override
-                    public RectF getWindowTargetRect() {
-                        return iconLocation;
-                    }
-
-                    @Override
-                    public void setAnimation(RectFSpringAnim anim) {
-                        anim.addAnimatorListener(floatingIconView);
-                        floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
-                        floatingIconView.setFastFinishRunnable(anim::end);
-                    }
-
-                    @Override
-                    public void update(RectF currentRect, float progress, float radius) {
-                        floatingIconView.update(currentRect, 1f, progress, windowAlphaThreshold,
-                                radius, false);
-                    }
-
-                    @Override
-                    public void onCancel() {
-                        floatingIconView.fastFinish();
-                    }
-                };
-            } else {
-                homeAnimFactory = new LauncherHomeAnimationFactory();
-            }
-        } else {
-            homeAnimFactory = new HomeAnimationFactory() {
-                @Override
-                public AnimatorPlaybackController createActivityAnimationToHome() {
-                    return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
-                }
-            };
-            mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
-                    isPresent -> mRecentsView.startHome());
-        }
-        return homeAnimFactory;
-    }
-
-    @Override
-    protected void finishRecentsControllerToHome(Runnable callback) {
-        mRecentsAnimationController.finish(
-                true /* toRecents */, callback, true /* sendUserLeaveHint */);
-    }
-
-    private class LauncherHomeAnimationFactory extends HomeAnimationFactory {
-        @NonNull
-        @Override
-        public AnimatorPlaybackController createActivityAnimationToHome() {
-            // Return an empty APC here since we have an non-user controlled animation
-            // to home.
-            long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
-            return mActivity.getStateManager().createAnimationToNewWorkspace(
-                    NORMAL, accuracy, 0 /* animComponents */);
-        }
-
-        @Override
-        public void playAtomicAnimation(float velocity) {
-            new StaggeredWorkspaceAnim(mActivity, velocity,
-                    true /* animateOverviewScrim */).start();
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
deleted file mode 100644
index 4879db7..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.os.SystemClock;
-import android.view.ViewConfiguration;
-
-import androidx.annotation.BinderThread;
-
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Helper class to handle various atomic commands for switching between Overview.
- */
-@TargetApi(Build.VERSION_CODES.P)
-public class OverviewCommandHelper {
-
-    private final Context mContext;
-    private final RecentsAnimationDeviceState mDeviceState;
-    private final RecentsModel mRecentsModel;
-    private final OverviewComponentObserver mOverviewComponentObserver;
-
-    private long mLastToggleTime;
-
-    public OverviewCommandHelper(Context context, RecentsAnimationDeviceState deviceState,
-            OverviewComponentObserver observer) {
-        mContext = context;
-        mDeviceState = deviceState;
-        mRecentsModel = RecentsModel.INSTANCE.get(mContext);
-        mOverviewComponentObserver = observer;
-    }
-
-    @BinderThread
-    public void onOverviewToggle() {
-        // If currently screen pinning, do not enter overview
-        if (mDeviceState.isScreenPinningActive()) {
-            return;
-        }
-
-        ActivityManagerWrapper.getInstance()
-                .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-        MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
-    }
-
-    @BinderThread
-    public void onOverviewShown(boolean triggeredFromAltTab) {
-        if (triggeredFromAltTab) {
-            ActivityManagerWrapper.getInstance()
-                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-        }
-        MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab));
-    }
-
-    @BinderThread
-    public void onOverviewHidden() {
-        MAIN_EXECUTOR.execute(new HideRecentsCommand());
-    }
-
-    @BinderThread
-    public void onTip(int actionType, int viewType) {
-        MAIN_EXECUTOR.execute(() ->
-                UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
-    }
-
-    private class ShowRecentsCommand extends RecentsActivityCommand {
-
-        private final boolean mTriggeredFromAltTab;
-
-        ShowRecentsCommand(boolean triggeredFromAltTab) {
-            mTriggeredFromAltTab = triggeredFromAltTab;
-        }
-
-        @Override
-        protected boolean handleCommand(long elapsedTime) {
-            // TODO: Go to the next page if started from alt-tab.
-            return mActivityInterface.getVisibleRecentsView() != null;
-        }
-
-        @Override
-        protected void onTransitionComplete() {
-            // TODO(b/138729100) This doesn't execute first time launcher is run
-            if (mTriggeredFromAltTab) {
-                RecentsView rv =  mActivityInterface.getVisibleRecentsView();
-                if (rv == null) {
-                    return;
-                }
-
-                // 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.requestFocus();
-                    } else {
-                        rv.requestFocus();
-                    }
-                } else {
-                    taskView.requestFocus();
-                }
-            }
-        }
-    }
-
-    private class HideRecentsCommand extends RecentsActivityCommand {
-
-        @Override
-        protected boolean handleCommand(long elapsedTime) {
-            RecentsView recents = mActivityInterface.getVisibleRecentsView();
-            if (recents == null) {
-                return false;
-            }
-            int currentPage = recents.getNextPage();
-            if (currentPage >= 0 && currentPage < recents.getTaskViewCount()) {
-                ((TaskView) recents.getPageAt(currentPage)).launchTask(true);
-            } else {
-                recents.startHome();
-            }
-            return true;
-        }
-    }
-
-    private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
-
-        protected final BaseActivityInterface<?, T> mActivityInterface;
-        private final long mCreateTime;
-        private final AppToOverviewAnimationProvider<T> mAnimationProvider;
-
-        private final long mToggleClickedTime = SystemClock.uptimeMillis();
-        private boolean mUserEventLogged;
-        private ActivityInitListener mListener;
-
-        public RecentsActivityCommand() {
-            mActivityInterface = mOverviewComponentObserver.getActivityInterface();
-            mCreateTime = SystemClock.elapsedRealtime();
-            mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
-                    ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
-
-            // Preload the plan
-            mRecentsModel.getTasks(null);
-        }
-
-        @Override
-        public void run() {
-            long elapsedTime = mCreateTime - mLastToggleTime;
-            mLastToggleTime = mCreateTime;
-
-            if (handleCommand(elapsedTime)) {
-                // Command already handled.
-                return;
-            }
-
-            if (mActivityInterface.switchToRecentsIfVisible(this::onTransitionComplete)) {
-                // If successfully switched, then return
-                return;
-            }
-
-            // Otherwise, start overview.
-            mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
-            mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
-                    new RemoteAnimationProvider() {
-                        @Override
-                        public AnimatorSet createWindowAnimation(
-                                RemoteAnimationTargetCompat[] appTargets,
-                                RemoteAnimationTargetCompat[] wallpaperTargets) {
-                            return RecentsActivityCommand.this.createWindowAnimation(appTargets,
-                                    wallpaperTargets);
-                        }
-                    }, mContext, MAIN_EXECUTOR.getHandler(),
-                    mAnimationProvider.getRecentsLaunchDuration());
-        }
-
-        protected boolean handleCommand(long elapsedTime) {
-            // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
-            //       the menu activity which takes window focus, preventing the right condition from
-            //       being run below
-            RecentsView recents = mActivityInterface.getVisibleRecentsView();
-            if (recents != null) {
-                // Launch the next task
-                recents.showNextTask();
-                return true;
-            } else if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
-                // The user tried to launch back into overview too quickly, either after
-                // launching an app, or before overview has actually shown, just ignore for now
-                return true;
-            }
-            return false;
-        }
-
-        private boolean onActivityReady(Boolean wasVisible) {
-            final T activity = mActivityInterface.getCreatedActivity();
-            if (!mUserEventLogged) {
-                activity.getUserEventDispatcher().logActionCommand(
-                        LauncherLogProto.Action.Command.RECENTS_BUTTON,
-                        mActivityInterface.getContainerType(),
-                        LauncherLogProto.ContainerType.TASKSWITCHER);
-                mUserEventLogged = true;
-            }
-
-            // Switch prediction client to overview
-            PredictionUiStateManager.INSTANCE.get(activity).switchClient(
-                    PredictionUiStateManager.Client.OVERVIEW);
-            return mAnimationProvider.onActivityReady(activity, wasVisible);
-        }
-
-        private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets) {
-            if (LatencyTrackerCompat.isEnabled(mContext)) {
-                LatencyTrackerCompat.logToggleRecents(
-                        (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
-            }
-
-            mListener.unregister();
-
-            AnimatorSet animatorSet = mAnimationProvider.createWindowAnimation(appTargets,
-                    wallpaperTargets);
-            animatorSet.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    onTransitionComplete();
-                }
-            });
-            return animatorSet;
-        }
-
-        protected void onTransitionComplete() { }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
deleted file mode 100644
index e2e25f3..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ /dev/null
@@ -1,238 +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;
-
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-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.clampToProgress;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Matrix;
-import android.graphics.Matrix.ScaleToFit;
-import android.graphics.RectF;
-import android.os.Build;
-import android.view.View;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TaskViewSimulator;
-import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
- */
-@TargetApi(Build.VERSION_CODES.R)
-public final class TaskViewUtils {
-
-    private TaskViewUtils() {}
-
-    /**
-     * Try to find a TaskView that corresponds with the component of the launched view.
-     *
-     * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
-     * Otherwise, we will assume we are using a normal app transition, but it's possible that the
-     * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
-     */
-    public static TaskView findTaskViewToLaunch(
-            BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
-        RecentsView recentsView = activity.getOverviewPanel();
-        if (v instanceof TaskView) {
-            TaskView taskView = (TaskView) v;
-            return recentsView.isTaskViewVisible(taskView) ? taskView : null;
-        }
-
-        // It's possible that the launched view can still be resolved to a visible task view, check
-        // the task id of the opening task and see if we can find a match.
-        if (v.getTag() instanceof ItemInfo) {
-            ItemInfo itemInfo = (ItemInfo) v.getTag();
-            ComponentName componentName = itemInfo.getTargetComponent();
-            int userId = itemInfo.user.getIdentifier();
-            if (componentName != null) {
-                for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
-                    TaskView taskView = recentsView.getTaskViewAt(i);
-                    if (recentsView.isTaskViewVisible(taskView)) {
-                        Task.TaskKey key = taskView.getTask().key;
-                        if (componentName.equals(key.getComponent()) && userId == key.userId) {
-                            return taskView;
-                        }
-                    }
-                }
-            }
-        }
-
-        if (targets == null) {
-            return null;
-        }
-        // Resolve the opening task id
-        int openingTaskId = -1;
-        for (RemoteAnimationTargetCompat target : targets) {
-            if (target.mode == MODE_OPENING) {
-                openingTaskId = target.taskId;
-                break;
-            }
-        }
-
-        // If there is no opening task id, fall back to the normal app icon launch animation
-        if (openingTaskId == -1) {
-            return null;
-        }
-
-        // 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);
-        if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
-            return null;
-        }
-        return taskView;
-    }
-
-    /**
-     * 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, DepthController depthController,
-            PendingAnimation out) {
-
-        SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
-        final RemoteAnimationTargets targets =
-                new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING);
-        targets.addReleaseCheck(applier);
-
-        TransformParams params = new TransformParams()
-                    .setSyncTransactionApplier(applier)
-                    .setTargetSet(targets);
-
-        final RecentsView recentsView = v.getRecentsView();
-        int taskIndex = recentsView.indexOfChild(v);
-        boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage();
-        int startScroll = recentsView.getScrollOffset(taskIndex);
-
-        Context context = v.getContext();
-        DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
-        // RecentsView never updates the display rotation until swipe-up so the value may be stale.
-        // Use the display value instead.
-        int displayRotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
-
-        TaskViewSimulator topMostSimulator = null;
-        if (targets.apps.length > 0) {
-            TaskViewSimulator tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
-            tsv.setDp(dp);
-            tsv.setLayoutRotation(displayRotation, displayRotation);
-            tsv.setPreview(targets.apps[targets.apps.length - 1]);
-            tsv.fullScreenProgress.value = 0;
-            tsv.recentsViewScale.value = 1;
-            tsv.setScroll(startScroll);
-
-            out.setFloat(tsv.fullScreenProgress,
-                    AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
-            out.setFloat(tsv.recentsViewScale,
-                    AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
-            out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
-
-            out.addOnFrameCallback(() -> tsv.apply(params));
-            topMostSimulator = tsv;
-        }
-
-        // 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 (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
-            out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
-
-            TaskViewSimulator simulatorToCopy = topMostSimulator;
-            simulatorToCopy.apply(params);
-
-            // Mt represents the overall transformation on the thumbnailView relative to the
-            // Launcher's rootView
-            // K(t) represents transformation on the running window by the taskViewSimulator at
-            // any time t.
-            // at t = 0, we know that the simulator matches the thumbnailView. So if we apply K(0)`
-            // on the Launcher's rootView, the thumbnailView would match the full running task
-            // window. If we apply "K(0)` K(t)" thumbnailView will match the final transformed
-            // window at any time t. This gives the overall matrix on thumbnailView to be:
-            //    Mt K(0)` K(t)
-            // 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]);
-
-            Matrix mt = new Matrix();
-            mt.setRectToRect(tvBounds, tvBoundsInRoot, ScaleToFit.FILL);
-
-            Matrix mti = new Matrix();
-            mt.invert(mti);
-
-            Matrix k0i = new Matrix();
-            simulatorToCopy.getCurrentMatrix().invert(k0i);
-
-            Matrix animationMatrix = new Matrix();
-            out.addOnFrameCallback(() -> {
-                animationMatrix.set(mt);
-                animationMatrix.postConcat(k0i);
-                animationMatrix.postConcat(simulatorToCopy.getCurrentMatrix());
-                animationMatrix.postConcat(mti);
-                ttv.setAnimationMatrix(animationMatrix);
-            });
-
-            out.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    ttv.setAnimationMatrix(null);
-                }
-            });
-        }
-
-        out.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                targets.release();
-            }
-        });
-
-        if (depthController != null) {
-            out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(context),
-                    TOUCH_RESPONSE_INTERPOLATOR);
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java
deleted file mode 100644
index cbb6ad4..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java
+++ /dev/null
@@ -1,74 +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;
-
-import android.graphics.Canvas;
-import android.view.View;
-
-import com.android.systemui.shared.system.WindowCallbacksCompat;
-
-import java.util.function.BooleanSupplier;
-
-/**
- * Utility class for helpful methods related to {@link View} objects.
- */
-public class ViewUtils {
-
-    /** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */
-    public static boolean postDraw(View view, Runnable onFinishRunnable) {
-        return postDraw(view, onFinishRunnable, () -> false);
-    }
-
-    /**
-     * Inject some addition logic in order to make sure that the view is updated smoothly post
-     * draw, and allow addition task to be run after view update.
-     *
-     * @param onFinishRunnable runnable to be run right after the view finishes drawing.
-     */
-    public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
-        // Defer finishing the animation until the next launcher frame with the
-        // new thumbnail
-        return new WindowCallbacksCompat(view) {
-            // The number of frames to defer until we actually finish the animation
-            private int mDeferFrameCount = 2;
-
-            @Override
-            public void onPostDraw(Canvas canvas) {
-                // If we were cancelled after this was attached, do not update
-                // the state.
-                if (canceled.getAsBoolean()) {
-                    detach();
-                    return;
-                }
-
-                if (mDeferFrameCount > 0) {
-                    mDeferFrameCount--;
-                    // Workaround, detach and reattach to invalidate the root node for
-                    // another draw
-                    detach();
-                    attach();
-                    view.invalidate();
-                    return;
-                }
-
-                if (onFinishRunnable != null) {
-                    onFinishRunnable.run();
-                }
-                detach();
-            }
-        }.attach();
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java
deleted file mode 100644
index f15a9de..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java
+++ /dev/null
@@ -1,115 +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.fallback;
-
-import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
-import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState;
-
-import android.content.Context;
-
-import com.android.launcher3.statemanager.BaseState;
-import com.android.quickstep.RecentsActivity;
-
-/**
- * State definition for Fallback recents
- */
-public class RecentsState implements BaseState<RecentsState> {
-
-    private static final int FLAG_MODAL = BaseState.getFlag(0);
-    private static final int FLAG_HAS_BUTTONS = BaseState.getFlag(1);
-    private static final int FLAG_FULL_SCREEN = BaseState.getFlag(2);
-
-    public static final RecentsState DEFAULT = new RecentsState(0, FLAG_HAS_BUTTONS);
-    public static final RecentsState MODAL_TASK = new ModalState(1,
-            FLAG_DISABLE_RESTORE | FLAG_HAS_BUTTONS | FLAG_MODAL);
-    public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
-            FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN);
-
-    public final int ordinal;
-    private final int mFlags;
-
-    private static final float NO_OFFSET = 0;
-    private static final float NO_SCALE = 1;
-
-    public RecentsState(int id, int flags) {
-        this.ordinal = id;
-        this.mFlags = flags;
-    }
-
-
-    @Override
-    public String toString() {
-        return "Ordinal-" + ordinal;
-    }
-
-    @Override
-    public final boolean hasFlag(int mask) {
-        return (mFlags & mask) != 0;
-    }
-
-    @Override
-    public int getTransitionDuration(Context context) {
-        return 250;
-    }
-
-    @Override
-    public RecentsState getHistoryForState(RecentsState previousState) {
-        return DEFAULT;
-    }
-
-    /**
-     * For this state, how modal should over view been shown. 0 modalness means all tasks drawn,
-     * 1 modalness means the current task is show on its own.
-     */
-    public float getOverviewModalness() {
-        return hasFlag(FLAG_MODAL) ? 1 : 0;
-    }
-
-    public boolean isFullScreen() {
-        return hasFlag(FLAG_FULL_SCREEN);
-    }
-
-    public boolean hasButtons() {
-        return hasFlag(FLAG_HAS_BUTTONS);
-    }
-
-    public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
-        return new float[] { NO_SCALE, NO_OFFSET };
-    }
-
-    private static class ModalState extends RecentsState {
-
-        public ModalState(int id, int flags) {
-            super(id, flags);
-        }
-
-        @Override
-        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
-            return getOverviewScaleAndOffsetForModalState(activity);
-        }
-    }
-
-    private static class BackgroundAppState extends RecentsState {
-        public BackgroundAppState(int id, int flags) {
-            super(id, flags);
-        }
-
-        @Override
-        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
-            return getOverviewScaleAndOffsetForBackgroundState(activity);
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
deleted file mode 100644
index e000803..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.logging;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-
-import java.util.ArrayList;
-
-/**
- * This class handles AOSP MetricsLogger function calls and logging around
- * quickstep interactions and app launches.
- */
-@SuppressWarnings("unused")
-public class UserEventDispatcherAppPredictionExtension extends UserEventDispatcherExtension {
-
-    public static final int ALL_APPS_PREDICTION_TIPS = 2;
-
-    private static final String TAG = "UserEventDispatcher";
-
-    public UserEventDispatcherAppPredictionExtension(Context context) {
-        super(context);
-    }
-
-    @Override
-    protected void onFillInLogContainerData(
-            @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target,
-            @NonNull ArrayList<LauncherLogProto.Target> targets) {
-        PredictionUiStateManager.fillInPredictedRank(itemInfo, target);
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/OverviewToHomeAnim.java
deleted file mode 100644
index 1031c5b..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/OverviewToHomeAnim.java
+++ /dev/null
@@ -1,155 +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 com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.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.INSTANT;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.util.Log;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Runs an animation from overview to home. Currently, this animation is just a wrapper around the
- * normal state transition, in order to keep RecentsView at the same scale and translationY that
- * it started out at as it translates offscreen. It also scrolls RecentsView to page 0 and may play
- * a {@link StaggeredWorkspaceAnim} if we're starting from an upward fling.
- */
-public class OverviewToHomeAnim {
-
-    private static final String TAG = "OverviewToHomeAnim";
-
-    // Constants to specify how to scroll RecentsView to the default page if it's not already there.
-    private static final int DEFAULT_PAGE = 0;
-    private static final int PER_PAGE_SCROLL_DURATION = 150;
-    private static final int MAX_PAGE_SCROLL_DURATION = 750;
-
-    private final Launcher mLauncher;
-    private final Runnable mOnReachedHome;
-
-    // Only run mOnReachedHome when both of these are true.
-    private boolean mIsHomeStaggeredAnimFinished;
-    private boolean mIsOverviewHidden;
-
-    public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome) {
-        mLauncher = launcher;
-        mOnReachedHome = onReachedHome;
-    }
-
-    /**
-     * Starts the animation. If velocity < 0 (i.e. upwards), also plays a
-     * {@link StaggeredWorkspaceAnim}.
-     */
-    public void animateWithVelocity(float velocity) {
-        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
-        LauncherState startState = stateManager.getState();
-        if (startState != OVERVIEW) {
-            Log.e(TAG, "animateFromOverviewToHome: unexpected start state " + startState);
-        }
-        AnimatorSet anim = new AnimatorSet();
-
-        boolean playStaggeredWorkspaceAnim = velocity < 0;
-        if (playStaggeredWorkspaceAnim) {
-            StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
-                    mLauncher, velocity, false /* animateOverviewScrim */);
-            staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    mIsHomeStaggeredAnimFinished = true;
-                    maybeOverviewToHomeAnimComplete();
-                }
-            });
-            anim.play(staggeredWorkspaceAnim.getAnimators());
-        } else {
-            mIsHomeStaggeredAnimFinished = true;
-        }
-
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        int numPagesToScroll = recentsView.getNextPage() - DEFAULT_PAGE;
-        int scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
-                numPagesToScroll * PER_PAGE_SCROLL_DURATION);
-        int duration = Math.max(scrollDuration, startState.getTransitionDuration(mLauncher));
-
-        StateAnimationConfig config = new UseFirstInterpolatorStateAnimConfig();
-        config.duration = duration;
-        config.animFlags = playStaggeredWorkspaceAnim
-                // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
-                ? PLAY_ATOMIC_OVERVIEW_PEEK
-                : ANIM_ALL_COMPONENTS;
-        boolean isLayoutNaturalToLauncher = recentsView.getPagedOrientationHandler()
-                .isLayoutNaturalToLauncher();
-        config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, isLayoutNaturalToLauncher
-                ? clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f) : FINAL_FRAME);
-        config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
-        config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
-        config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, INSTANT);
-        if (!isLayoutNaturalToLauncher) {
-            config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL);
-        }
-        AnimatorSet stateAnim = stateManager.createAtomicAnimation(
-                startState, NORMAL, config);
-        stateAnim.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                mIsOverviewHidden = true;
-                maybeOverviewToHomeAnimComplete();
-            }
-        });
-        anim.play(stateAnim);
-        stateManager.setCurrentAnimation(anim, NORMAL);
-        anim.start();
-        recentsView.snapToPage(DEFAULT_PAGE, duration);
-    }
-
-    private void maybeOverviewToHomeAnimComplete() {
-        if (mIsHomeStaggeredAnimFinished && mIsOverviewHidden) {
-            mOnReachedHome.run();
-        }
-    }
-
-    /**
-     * Wrapper around StateAnimationConfig that doesn't allow interpolators to be set if they are
-     * already set. This ensures they aren't overridden before being used.
-     */
-    private static class UseFirstInterpolatorStateAnimConfig extends StateAnimationConfig {
-        @Override
-        public void setInterpolator(int animId, Interpolator interpolator) {
-            if (mInterpolators[animId] == null || interpolator == null) {
-                super.setInterpolator(animId, interpolator);
-            }
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java
deleted file mode 100644
index 190763a..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java
+++ /dev/null
@@ -1,127 +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.util;
-
-import static com.android.launcher3.tracing.nano.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_H;
-import static com.android.launcher3.tracing.nano.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_L;
-
-import android.content.Context;
-import android.os.SystemClock;
-
-import com.android.launcher3.tracing.nano.LauncherTraceProto;
-import com.android.launcher3.tracing.nano.LauncherTraceEntryProto;
-import com.android.launcher3.tracing.nano.LauncherTraceFileProto;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.tracing.FrameProtoTracer;
-import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams;
-import com.android.systemui.shared.tracing.ProtoTraceable;
-import com.google.protobuf.nano.MessageNano;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Queue;
-
-
-/**
- * Controller for coordinating winscope proto tracing.
- */
-public class ProtoTracer implements ProtoTraceParams<MessageNano,
-        LauncherTraceFileProto, LauncherTraceEntryProto, LauncherTraceProto> {
-
-    public static final MainThreadInitializedObject<ProtoTracer> INSTANCE =
-            new MainThreadInitializedObject<>(ProtoTracer::new);
-
-    private static final String TAG = "ProtoTracer";
-    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
-
-    private final Context mContext;
-    private final FrameProtoTracer<MessageNano,
-            LauncherTraceFileProto, LauncherTraceEntryProto, LauncherTraceProto> mProtoTracer;
-
-    public ProtoTracer(Context context) {
-        mContext = context;
-        mProtoTracer = new FrameProtoTracer<>(this);
-    }
-
-    @Override
-    public File getTraceFile() {
-        return new File(mContext.getFilesDir(), "launcher_trace.pb");
-    }
-
-    @Override
-    public LauncherTraceFileProto getEncapsulatingTraceProto() {
-        return new LauncherTraceFileProto();
-    }
-
-    @Override
-    public LauncherTraceEntryProto updateBufferProto(LauncherTraceEntryProto reuseObj,
-            ArrayList<ProtoTraceable<LauncherTraceProto>> traceables) {
-        LauncherTraceEntryProto proto = reuseObj != null
-                ? reuseObj
-                : new LauncherTraceEntryProto();
-        proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
-        proto.launcher = proto.launcher != null ? proto.launcher : new LauncherTraceProto();
-        for (ProtoTraceable t : traceables) {
-            t.writeToProto(proto.launcher);
-        }
-        return proto;
-    }
-
-    @Override
-    public byte[] serializeEncapsulatingProto(LauncherTraceFileProto encapsulatingProto,
-            Queue<LauncherTraceEntryProto> buffer) {
-        encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE;
-        encapsulatingProto.entry = buffer.toArray(new LauncherTraceEntryProto[0]);
-        return MessageNano.toByteArray(encapsulatingProto);
-    }
-
-    @Override
-    public byte[] getProtoBytes(MessageNano proto) {
-        return MessageNano.toByteArray(proto);
-    }
-
-    @Override
-    public int getProtoSize(MessageNano proto) {
-        return proto.getCachedSize();
-    }
-
-    public void start() {
-        mProtoTracer.start();
-    }
-
-    public void stop() {
-        mProtoTracer.stop();
-    }
-
-    public void add(ProtoTraceable<LauncherTraceProto> traceable) {
-        mProtoTracer.add(traceable);
-    }
-
-    public void remove(ProtoTraceable<LauncherTraceProto> traceable) {
-        mProtoTracer.remove(traceable);
-    }
-
-    public void scheduleFrameUpdate() {
-        mProtoTracer.scheduleFrameUpdate();
-    }
-
-    public void update() {
-        mProtoTracer.update();
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
deleted file mode 100644
index 85006da..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
+++ /dev/null
@@ -1,105 +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.util;
-
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.uioverrides.states.OverviewState;
-
-/**
- * Animates the shelf between states HIDE, PEEK, and OVERVIEW.
- */
-public class ShelfPeekAnim {
-
-    public static final Interpolator INTERPOLATOR = OVERSHOOT_1_2;
-    public static final long DURATION = 240;
-
-    private final Launcher mLauncher;
-
-    private ShelfAnimState mShelfState;
-    private boolean mIsPeeking;
-
-    public ShelfPeekAnim(Launcher launcher) {
-        mLauncher = launcher;
-    }
-
-    /**
-     * Animates to the given state, canceling the previous animation if it was still running.
-     */
-    public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
-        if (mShelfState == shelfState || FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
-            return;
-        }
-        mLauncher.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
-        mShelfState = shelfState;
-        mIsPeeking = mShelfState == ShelfAnimState.PEEK || mShelfState == ShelfAnimState.HIDE;
-        if (mShelfState == ShelfAnimState.CANCEL) {
-            return;
-        }
-        float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(mLauncher);
-        float shelfOverviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
-        // Peek based on default overview progress so we can see hotseat if we're showing
-        // that instead of predictions in overview.
-        float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
-        float shelfPeekingProgress = shelfHiddenProgress
-                - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f;
-        float toProgress = mShelfState == ShelfAnimState.HIDE
-                ? shelfHiddenProgress
-                : mShelfState == ShelfAnimState.PEEK
-                        ? shelfPeekingProgress
-                        : shelfOverviewProgress;
-        Animator shelfAnim = mLauncher.getStateManager()
-                .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress);
-        shelfAnim.setInterpolator(interpolator);
-        shelfAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mShelfState = ShelfAnimState.CANCEL;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animator) {
-                mIsPeeking = mShelfState == ShelfAnimState.PEEK;
-            }
-        });
-        shelfAnim.setDuration(duration).start();
-    }
-
-    /** @return Whether the shelf is currently peeking or animating to or from peeking. */
-    public boolean isPeeking() {
-        return mIsPeeking;
-    }
-
-    /** The various shelf states we can animate to. */
-    public enum ShelfAnimState {
-        HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
-
-        ShelfAnimState(boolean shouldPreformHaptic) {
-            this.shouldPreformHaptic = shouldPreformHaptic;
-        }
-
-        public final boolean shouldPreformHaptic;
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
deleted file mode 100644
index fd74357..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.views;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.widget.Button;
-
-import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.quickstep.views.RecentsView.PageCallbacks;
-import com.android.quickstep.views.RecentsView.ScrollState;
-
-public class ClearAllButton extends Button implements PageCallbacks {
-
-    public static final FloatProperty<ClearAllButton> VISIBILITY_ALPHA =
-            new FloatProperty<ClearAllButton>("visibilityAlpha") {
-                @Override
-                public Float get(ClearAllButton view) {
-                    return view.mVisibilityAlpha;
-                }
-
-                @Override
-                public void setValue(ClearAllButton view, float v) {
-                    view.setVisibilityAlpha(v);
-                }
-            };
-
-    private float mScrollAlpha = 1;
-    private float mContentAlpha = 1;
-    private float mVisibilityAlpha = 1;
-
-    private boolean mIsRtl;
-
-    private int mScrollOffset;
-
-    public ClearAllButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
-        mScrollOffset = orientationHandler.getClearAllScrollOffset(getRecentsView(), mIsRtl);
-    }
-
-    private RecentsView getRecentsView() {
-        return (RecentsView) getParent();
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-        mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    public void setContentAlpha(float alpha) {
-        if (mContentAlpha != alpha) {
-            mContentAlpha = alpha;
-            updateAlpha();
-        }
-    }
-
-    public void setVisibilityAlpha(float alpha) {
-        if (mVisibilityAlpha != alpha) {
-            mVisibilityAlpha = alpha;
-            updateAlpha();
-        }
-    }
-
-    @Override
-    public void onPageScroll(ScrollState scrollState) {
-        PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
-        float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight());
-        if (orientationSize == 0) {
-            return;
-        }
-
-        float shift = Math.min(scrollState.scrollFromEdge, orientationSize);
-        float translation = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift);
-        orientationHandler.setPrimaryAndResetSecondaryTranslate(this, translation);
-        mScrollAlpha = 1 - shift / orientationSize;
-        updateAlpha();
-    }
-
-    private void updateAlpha() {
-        final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha;
-        setAlpha(alpha);
-        setClickable(alpha == 1);
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
deleted file mode 100644
index b934c29..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.views;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
-import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
-import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.widget.FrameLayout;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.model.AppLaunchTracker;
-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.TraceHelper;
-import com.android.launcher3.views.ScrimView;
-import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.util.OverviewToHomeAnim;
-import com.android.quickstep.util.TransformParams;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.RecentsExtraCard;
-import com.android.systemui.shared.recents.model.Task;
-
-/**
- * {@link RecentsView} used in Launcher activity
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
-        implements StateListener<LauncherState> {
-
-    private final TransformParams mTransformParams = new TransformParams();
-
-    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) {
-        this(context, attrs, 0);
-    }
-
-    public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
-        mActivity.getStateManager().addStateListener(this);
-    }
-
-    @Override
-    public void init(OverviewActionsView actionsView) {
-        super.init(actionsView);
-        setContentAlpha(0);
-    }
-
-    @Override
-    public void startHome() {
-        Runnable onReachedHome = () -> mActivity.getStateManager().goToState(NORMAL, false);
-        OverviewToHomeAnim overviewToHomeAnim = new OverviewToHomeAnim(mActivity, onReachedHome);
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            switchToScreenshot(null,
-                    () -> finishRecentsAnimation(true /* toRecents */,
-                            () -> overviewToHomeAnim.animateWithVelocity(0)));
-        } else {
-            overviewToHomeAnim.animateWithVelocity(0);
-        }
-    }
-
-    @Override
-    public void setTranslationY(float translationY) {
-        super.setTranslationY(translationY);
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            LauncherState state = mActivity.getStateManager().getState();
-            if (state == OVERVIEW || state == ALL_APPS) {
-                redrawLiveTile(false);
-            }
-        }
-    }
-
-    /**
-     * Animates adjacent tasks and translate hotseat off screen as well.
-     */
-    @Override
-    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
-        AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv);
-
-        if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
-            // Hotseat doesn't move when opening recents with the button,
-            // so don't animate it here either.
-            return anim;
-        }
-
-        float allAppsProgressOffscreen = ALL_APPS_PROGRESS_OFF_SCREEN;
-        LauncherState state = mActivity.getStateManager().getState();
-        if ((state.getVisibleElements(mActivity) & ALL_APPS_HEADER_EXTRA) != 0) {
-            float maxShiftRange = mActivity.getDeviceProfile().heightPx;
-            float currShiftRange = mActivity.getAllAppsController().getShiftRange();
-            allAppsProgressOffscreen = 1f + (maxShiftRange - currShiftRange) / maxShiftRange;
-        }
-        anim.play(ObjectAnimator.ofFloat(
-                mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen));
-
-        ObjectAnimator dragHandleAnim = ObjectAnimator.ofInt(
-                mActivity.getScrimView(), ScrimView.DRAG_HANDLE_ALPHA, 0);
-        dragHandleAnim.setInterpolator(Interpolators.ACCEL_2);
-        anim.play(dragHandleAnim);
-
-        return anim;
-    }
-
-    @Override
-    protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (tv.isRunningTask()) {
-                mTransformParams.setProgress(1 - progress)
-                        .setSyncTransactionApplier(mSyncTransactionApplier);
-                // TODO: Revisit live tiles
-            } else {
-                redrawLiveTile(true);
-            }
-        }
-    }
-
-    @Override
-    protected void onTaskLaunchAnimationEnd(boolean success) {
-        if (success) {
-            mActivity.getStateManager().goToState(NORMAL, false /* animate */);
-        } else {
-            LauncherState state = mActivity.getStateManager().getState();
-            mActivity.getAllAppsController().setState(state);
-        }
-        super.onTaskLaunchAnimationEnd(success);
-    }
-
-    @Override
-    public void onTaskLaunched(Task task) {
-        UserHandle user =  UserHandle.of(task.key.userId);
-        AppLaunchTracker.INSTANCE.get(getContext()).onStartApp(task.getTopComponent(), user,
-                AppLaunchTracker.CONTAINER_OVERVIEW);
-    }
-
-    @Override
-    public boolean shouldUseMultiWindowTaskSizeStrategy() {
-        return TraceHelper.allowIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
-    }
-
-    @Override
-    public void scrollTo(int x, int y) {
-        super.scrollTo(x, y);
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
-            redrawLiveTile(true);
-        }
-    }
-
-    @Override
-    public TransformParams getLiveTileParams(
-            boolean mightNeedToRefill) {
-        if (!mEnableDrawingLiveTile || mRecentsAnimationController == null
-                || mRecentsAnimationTargets == null) {
-            return null;
-        }
-        TaskView taskView = getRunningTaskView();
-        if (taskView != null) {
-            taskView.getThumbnail().getGlobalVisibleRect(mTempRect);
-            int offsetX = (int) (mTaskWidth * taskView.getScaleX() * getScaleX()
-                    - mTempRect.width());
-            int offsetY = (int) (mTaskHeight * taskView.getScaleY() * getScaleY()
-                    - mTempRect.height());
-            if (((mCurrentPage != 0) || mightNeedToRefill) && offsetX > 0) {
-                if (mTempRect.left - offsetX < 0) {
-                    mTempRect.left -= offsetX;
-                } else {
-                    mTempRect.right += offsetX;
-                }
-            }
-            if (mightNeedToRefill && offsetY > 0) {
-                mTempRect.top -= offsetY;
-            }
-            mTransformParams.setProgress(1f)
-                    .setTargetAlpha(taskView.getAlpha())
-                    .setSyncTransactionApplier(mSyncTransactionApplier)
-                    .setTargetSet(mRecentsAnimationTargets);
-        }
-        return mTransformParams;
-    }
-
-    @Override
-    public void reset() {
-        super.reset();
-
-        setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
-        // We are moving to home or some other UI with no recents. Switch back to the home client,
-        // the home predictions should have been updated when the activity was resumed.
-        PredictionUiStateManager.INSTANCE.get(getContext()).switchClient(Client.HOME);
-    }
-
-    @Override
-    public void onStateTransitionStart(LauncherState toState) {
-        setOverviewStateEnabled(toState.overviewUi);
-        setFreezeViewVisibility(true);
-    }
-
-    @Override
-    public void onStateTransitionComplete(LauncherState finalState) {
-        if (finalState == NORMAL || finalState == SPRING_LOADED) {
-            // Clean-up logic that occurs when recents is no longer in use/visible.
-            reset();
-        }
-        setOverlayEnabled(finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK);
-        setFreezeViewVisibility(false);
-    }
-
-    @Override
-    public void setOverviewStateEnabled(boolean enabled) {
-        super.setOverviewStateEnabled(enabled);
-        if (enabled) {
-            LauncherState state = mActivity.getStateManager().getState();
-            boolean hasClearAllButton = (state.getVisibleElements(mActivity)
-                    & OVERVIEW_BUTTONS) != 0;
-            setDisallowScrollToClearAll(!hasClearAllButton);
-        }
-    }
-
-    @Override
-    protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            // Allow touches to go through to the hotseat.
-            Hotseat hotseat = mActivity.getHotseat();
-            boolean touchingHotseat = hotseat.isShown()
-                    && mActivity.getDragLayer().isEventOverView(hotseat, ev, this);
-            return !touchingHotseat;
-        }
-        return super.shouldStealTouchFromSiblingsBelow(ev);
-    }
-
-    @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();
-    }
-
-    @Override
-    public void setModalStateEnabled(boolean isModalState) {
-        super.setModalStateEnabled(isModalState);
-        if (isModalState) {
-            mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK);
-        } else {
-            if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
-                mActivity.getStateManager().goToState(LauncherState.OVERVIEW);
-            }
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
deleted file mode 100644
index 30c9f77..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
+++ /dev/null
@@ -1,152 +0,0 @@
-package com.android.quickstep.views;
-
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.util.FloatProperty;
-import android.view.ViewOverlay;
-
-import com.android.launcher3.anim.Interpolators;
-
-public class LiveTileOverlay extends Drawable {
-
-    private static final long ICON_ANIM_DURATION = 120;
-
-    private static final FloatProperty<LiveTileOverlay> PROGRESS =
-            new FloatProperty<LiveTileOverlay>("progress") {
-                @Override
-                public void setValue(LiveTileOverlay liveTileOverlay, float progress) {
-                    liveTileOverlay.setIconAnimationProgress(progress);
-                }
-
-                @Override
-                public Float get(LiveTileOverlay liveTileOverlay) {
-                    return liveTileOverlay.mIconAnimationProgress;
-                }
-            };
-
-    public static final LiveTileOverlay INSTANCE = new LiveTileOverlay();
-
-    private final Paint mPaint = new Paint();
-    private final Rect mBoundsRect = new Rect();
-
-    private RectF mCurrentRect;
-    private float mCornerRadius;
-    private Drawable mIcon;
-    private Animator mIconAnimator;
-
-    private boolean mDrawEnabled = true;
-    private float mIconAnimationProgress = 0f;
-    private boolean mIsAttached;
-
-    private LiveTileOverlay() {
-        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-    }
-
-    public void update(RectF currentRect, float cornerRadius) {
-        invalidateSelf();
-
-        mCurrentRect = currentRect;
-        mCornerRadius = cornerRadius;
-
-        mCurrentRect.roundOut(mBoundsRect);
-        setBounds(mBoundsRect);
-        invalidateSelf();
-    }
-
-    public void setIcon(Drawable icon) {
-        mIcon = icon;
-    }
-
-    public void startIconAnimation() {
-        if (mIconAnimator != null) {
-            mIconAnimator.cancel();
-        }
-        // This animator must match the icon part of {@link TaskView#FOCUS_TRANSITION} animation.
-        mIconAnimator = ObjectAnimator.ofFloat(this, PROGRESS, 1);
-        mIconAnimator.setDuration(ICON_ANIM_DURATION).setInterpolator(LINEAR);
-        mIconAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mIconAnimator = null;
-            }
-        });
-        mIconAnimator.start();
-    }
-
-    public float cancelIconAnimation() {
-        if (mIconAnimator != null) {
-            mIconAnimator.cancel();
-        }
-        return mIconAnimationProgress;
-    }
-
-    public void setDrawEnabled(boolean drawEnabled) {
-        if (mDrawEnabled != drawEnabled) {
-            mDrawEnabled = drawEnabled;
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mCurrentRect != null && mDrawEnabled) {
-            canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
-            if (mIcon != null && mIconAnimationProgress > 0f) {
-                canvas.save();
-                float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
-                        1f).getInterpolation(mIconAnimationProgress);
-                canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
-                        mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
-                canvas.scale(scale, scale);
-                mIcon.draw(canvas);
-                canvas.restore();
-            }
-        }
-    }
-
-    @Override
-    public void setAlpha(int i) { }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) { }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    public boolean attach(ViewOverlay overlay) {
-        if (overlay != null && !mIsAttached) {
-            overlay.add(this);
-            mIsAttached = true;
-            return true;
-        }
-
-        return false;
-    }
-
-    public void detach(ViewOverlay overlay) {
-        if (overlay != null) {
-            overlay.remove(this);
-            mIsAttached = false;
-        }
-    }
-
-    private void setIconAnimationProgress(float progress) {
-        mIconAnimationProgress = progress;
-        invalidateSelf();
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
deleted file mode 100644
index 23941fa..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ /dev/null
@@ -1,2480 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.views;
-
-import static android.view.Surface.ROTATION_0;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
-import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
-import static com.android.launcher3.Utilities.mapToRange;
-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_75;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-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.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
-import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.quickstep.views.OverviewActionsView.HIDDEN_GESTURE_RUNNING;
-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 android.animation.AnimatorSet;
-import android.animation.LayoutTransition;
-import android.animation.LayoutTransition.TransitionListener;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Canvas;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.text.Layout;
-import android.text.StaticLayout;
-import android.text.TextPaint;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.SparseBooleanArray;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.Interpolator;
-import android.widget.ListView;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherState;
-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.AnimatorPlaybackController;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.anim.PendingAnimation.EndState;
-import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.anim.SpringProperty;
-import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.DynamicResource;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.OverScroller;
-import com.android.launcher3.util.ResourceBasedOverride.Overrides;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.ViewPool;
-import com.android.quickstep.BaseActivityInterface;
-import com.android.quickstep.RecentsAnimationController;
-import com.android.quickstep.RecentsAnimationTargets;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskThumbnailCache;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.ViewUtils;
-import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.RecentsOrientedState;
-import com.android.quickstep.util.SplitScreenBounds;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TransformParams;
-import com.android.systemui.plugins.ResourceProvider;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.LauncherEventUtil;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * A list of recent tasks.
- */
-@TargetApi(Build.VERSION_CODES.R)
-public abstract class RecentsView<T extends StatefulActivity> extends PagedView implements
-        Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
-        InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
-        SplitScreenBounds.OnChangeListener {
-
-    private static final String TAG = RecentsView.class.getSimpleName();
-
-    public static final FloatProperty<RecentsView> CONTENT_ALPHA =
-            new FloatProperty<RecentsView>("contentAlpha") {
-                @Override
-                public void setValue(RecentsView view, float v) {
-                    view.setContentAlpha(v);
-                }
-
-                @Override
-                public Float get(RecentsView view) {
-                    return view.getContentAlpha();
-                }
-            };
-
-    public static final FloatProperty<RecentsView> FULLSCREEN_PROGRESS =
-            new FloatProperty<RecentsView>("fullscreenProgress") {
-                @Override
-                public void setValue(RecentsView recentsView, float v) {
-                    recentsView.setFullscreenProgress(v);
-                }
-
-                @Override
-                public Float get(RecentsView recentsView) {
-                    return recentsView.mFullscreenProgress;
-                }
-            };
-
-    public static final FloatProperty<RecentsView> TASK_MODALNESS =
-            new FloatProperty<RecentsView>("taskModalness") {
-                @Override
-                public void setValue(RecentsView recentsView, float v) {
-                    recentsView.setTaskModalness(v);
-                }
-
-                @Override
-                public Float get(RecentsView recentsView) {
-                    return recentsView.mTaskModalness;
-                }
-            };
-
-    public static final FloatProperty<RecentsView> ADJACENT_PAGE_OFFSET =
-            new FloatProperty<RecentsView>("adjacentPageOffset") {
-                @Override
-                public void setValue(RecentsView recentsView, float v) {
-                    if (recentsView.mAdjacentPageOffset != v) {
-                        recentsView.mAdjacentPageOffset = v;
-                        recentsView.updatePageOffsets();
-                    }
-                }
-
-                @Override
-                public Float get(RecentsView recentsView) {
-                    return recentsView.mAdjacentPageOffset;
-                }
-            };
-
-    public static final FloatProperty<RecentsView> TASK_SECONDARY_TRANSLATION =
-            new FloatProperty<RecentsView>("taskSecondaryTranslation") {
-                @Override
-                public void setValue(RecentsView recentsView, float v) {
-                    recentsView.setTaskViewsSecondaryTranslation(v);
-                }
-
-                @Override
-                public Float get(RecentsView recentsView) {
-                    return recentsView.mTaskViewsSecondaryTranslation;
-                }
-            };
-
-    /** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */
-    public static final FloatProperty<RecentsView> RECENTS_SCALE_PROPERTY =
-            new FloatProperty<RecentsView>("recentsScale") {
-                @Override
-                public void setValue(RecentsView view, float scale) {
-                    view.setScaleX(scale);
-                    view.setScaleY(scale);
-                    view.mLastComputedTaskPushOutDistance = null;
-                    view.updatePageOffsets();
-                    view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
-                }
-
-                @Override
-                public Float get(RecentsView view) {
-                    return view.getScaleX();
-                }
-            };
-
-    protected RecentsOrientedState mOrientationState;
-    protected final BaseActivityInterface mSizeStrategy;
-    protected RecentsAnimationController mRecentsAnimationController;
-    protected RecentsAnimationTargets mRecentsAnimationTargets;
-    protected SurfaceTransactionApplier mSyncTransactionApplier;
-    protected int mTaskWidth;
-    protected int mTaskHeight;
-    protected final Rect mLastComputedTaskSize = new Rect();
-    // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
-    protected Float mLastComputedTaskPushOutDistance = null;
-    protected boolean mEnableDrawingLiveTile = false;
-    protected final Rect mTempRect = new Rect();
-    protected final RectF mTempRectF = new RectF();
-    private final PointF mTempPointF = new PointF();
-
-    private static final int DISMISS_TASK_DURATION = 300;
-    private static final int ADDITION_TASK_DURATION = 200;
-    // The threshold at which we update the SystemUI flags when animating from the task into the app
-    public static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.85f;
-
-    protected final T mActivity;
-    private final float mFastFlingVelocity;
-    private final RecentsModel mModel;
-    private final int mTaskTopMargin;
-    private final ClearAllButton mClearAllButton;
-    private final Rect mClearAllButtonDeadZoneRect = new Rect();
-    private final Rect mTaskViewDeadZoneRect = new Rect();
-
-    private final ScrollState mScrollState = new ScrollState();
-    // Keeps track of the previously known visible tasks for purposes of loading/unloading task data
-    private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
-
-    private final InvariantDeviceProfile mIdp;
-
-    private final ViewPool<TaskView> mTaskViewPool;
-
-    private final TaskOverlayFactory mTaskOverlayFactory;
-
-    private boolean mDwbToastShown;
-    protected boolean mDisallowScrollToClearAll;
-    private boolean mOverlayEnabled;
-    protected boolean mFreezeViewVisibility;
-
-    private float mAdjacentPageOffset = 0;
-    private float mTaskViewsSecondaryTranslation = 0;
-
-    /**
-     * TODO: Call reloadIdNeeded in onTaskStackChanged.
-     */
-    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
-        @Override
-        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
-            if (!mHandleTaskStackChanges) {
-                return;
-            }
-            // Check this is for the right user
-            if (!checkCurrentOrManagedUserId(userId, getContext())) {
-                return;
-            }
-
-            // Remove the task immediately from the task list
-            TaskView taskView = getTaskView(taskId);
-            if (taskView != null) {
-                removeView(taskView);
-            }
-        }
-
-        @Override
-        public void onActivityUnpinned() {
-            if (!mHandleTaskStackChanges) {
-                return;
-            }
-
-            reloadIfNeeded();
-            enableLayoutTransitions();
-        }
-
-        @Override
-        public void onTaskRemoved(int taskId) {
-            if (!mHandleTaskStackChanges) {
-                return;
-            }
-
-            UI_HELPER_EXECUTOR.execute(() -> {
-                TaskView taskView = getTaskView(taskId);
-                if (taskView == null) {
-                    return;
-                }
-                Handler handler = taskView.getHandler();
-                if (handler == null) {
-                    return;
-                }
-
-                // TODO: Add callbacks from AM reflecting adding/removing from the recents list, and
-                //       remove all these checks
-                Task.TaskKey taskKey = taskView.getTask().key;
-                if (PackageManagerWrapper.getInstance().getActivityInfo(taskKey.getComponent(),
-                        taskKey.userId) == null) {
-                    // The package was uninstalled
-                    handler.post(() ->
-                            dismissTask(taskView, true /* animate */, false /* removeTask */));
-                } else {
-                    mModel.findTaskWithId(taskKey.id, (key) -> {
-                        if (key == null) {
-                            // The task was removed from the recents list
-                            handler.post(() -> dismissTask(taskView, true /* animate */,
-                                    false /* removeTask */));
-                        }
-                    });
-                }
-            });
-        }
-    };
-
-    private final PinnedStackAnimationListener mIPinnedStackAnimationListener =
-            new PinnedStackAnimationListener();
-
-    // Used to keep track of the last requested task list id, so that we do not request to load the
-    // tasks again if we have already requested it and the task list has not changed
-    private int mTaskListChangeId = -1;
-
-    // Only valid until the launcher state changes to NORMAL
-    protected int mRunningTaskId = -1;
-    protected boolean mRunningTaskTileHidden;
-    private Task mTmpRunningTask;
-
-    private boolean mRunningTaskIconScaledDown = false;
-
-    private boolean mOverviewStateEnabled;
-    private boolean mHandleTaskStackChanges;
-    private boolean mSwipeDownShouldLaunchApp;
-    private boolean mTouchDownToStartHome;
-    private final float mSquaredTouchSlop;
-    private int mDownX;
-    private int mDownY;
-
-    private PendingAnimation mPendingAnimation;
-    private LayoutTransition mLayoutTransition;
-
-    @ViewDebug.ExportedProperty(category = "launcher")
-    protected float mContentAlpha = 1;
-    @ViewDebug.ExportedProperty(category = "launcher")
-    protected float mFullscreenProgress = 0;
-    /**
-     * How modal is the current task to be displayed, 1 means the task is fully modal and no other
-     * tasks are show. 0 means the task is displays in context in the list with other tasks.
-     */
-    @ViewDebug.ExportedProperty(category = "launcher")
-    protected float mTaskModalness = 0;
-
-    // Keeps track of task id whose visual state should not be reset
-    private int mIgnoreResetTaskId = -1;
-
-    // Variables for empty state
-    private final Drawable mEmptyIcon;
-    private final CharSequence mEmptyMessage;
-    private final TextPaint mEmptyMessagePaint;
-    private final Point mLastMeasureSize = new Point();
-    private final int mEmptyMessagePadding;
-    private boolean mShowEmptyMessage;
-    private OnEmptyMessageUpdatedListener mOnEmptyMessageUpdatedListener;
-    private Layout mEmptyTextLayout;
-    private boolean mLiveTileOverlayAttached;
-
-    // Keeps track of the index where the first TaskView should be
-    private int mTaskViewStartIndex = 0;
-    private OverviewActionsView mActionsView;
-
-    private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
-            (inMultiWindowMode) -> {
-                if (mOrientationState != null) {
-                    mOrientationState.setMultiWindowMode(inMultiWindowMode);
-                    setLayoutRotation(mOrientationState.getTouchRotation(),
-                            mOrientationState.getDisplayRotation());
-                    updateChildTaskOrientations();
-                }
-                if (!inMultiWindowMode && mOverviewStateEnabled) {
-                    // TODO: Re-enable layout transitions for addition of the unpinned task
-                    reloadIfNeeded();
-                }
-            };
-
-    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
-            BaseActivityInterface sizeStrategy) {
-        super(context, attrs, defStyleAttr);
-        setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
-        setEnableFreeScroll(true);
-        mSizeStrategy = sizeStrategy;
-        mActivity = BaseActivity.fromContext(context);
-        mOrientationState = new RecentsOrientedState(
-                context, mSizeStrategy, this::animateRecentsRotationInPlace);
-        mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation());
-
-        mFastFlingVelocity = getResources()
-                .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
-        mModel = RecentsModel.INSTANCE.get(context);
-        mIdp = InvariantDeviceProfile.INSTANCE.get(context);
-
-        mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
-                .inflate(R.layout.overview_clear_all_button, this, false);
-        mClearAllButton.setOnClickListener(this::dismissAllTasks);
-        mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
-                10 /* initial size */);
-
-        mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
-        setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
-        mTaskTopMargin = getResources()
-                .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
-        mSquaredTouchSlop = squaredTouchSlop(context);
-
-        mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
-        mEmptyIcon.setCallback(this);
-        mEmptyMessage = context.getText(R.string.recents_empty_message);
-        mEmptyMessagePaint = new TextPaint();
-        mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
-        mEmptyMessagePaint.setTextSize(getResources()
-                .getDimension(R.dimen.recents_empty_message_text_size));
-        mEmptyMessagePaint.setTypeface(Typeface.create(Themes.getDefaultBodyFont(context),
-                Typeface.NORMAL));
-        mEmptyMessagePadding = getResources()
-                .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
-        setWillNotDraw(false);
-        updateEmptyMessage();
-        mOrientationHandler = mOrientationState.getOrientationHandler();
-
-        mTaskOverlayFactory = Overrides.getObject(
-                TaskOverlayFactory.class,
-                context.getApplicationContext(),
-                R.string.task_overlay_factory_class);
-
-        // Initialize quickstep specific cache params here, as this is constructed only once
-        mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
-    }
-
-    public OverScroller getScroller() {
-        return mScroller;
-    }
-
-    public boolean isRtl() {
-        return mIsRtl;
-    }
-
-    @Override
-    public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
-        if (mHandleTaskStackChanges) {
-            TaskView taskView = getTaskView(taskId);
-            if (taskView != null) {
-                Task task = taskView.getTask();
-                taskView.getThumbnail().setThumbnail(task, thumbnailData);
-                return task;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public void onTaskIconChanged(String pkg, UserHandle user) {
-        for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView tv = getTaskViewAt(i);
-            Task task = tv.getTask();
-            if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
-                    && task.key.userId == user.getIdentifier()) {
-                task.icon = null;
-                if (tv.getIconView().getDrawable() != null) {
-                    tv.onTaskListVisibilityChanged(true /* visible */);
-                }
-            }
-        }
-    }
-
-    /**
-     * Update the thumbnail of the task.
-     * @param refreshNow Refresh immediately if it's true.
-     */
-    public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData, boolean refreshNow) {
-        TaskView taskView = getTaskView(taskId);
-        if (taskView != null) {
-            taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData, refreshNow);
-        }
-        return taskView;
-    }
-
-    /** See {@link #updateThumbnail(int, ThumbnailData, boolean)} */
-    public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
-        return updateThumbnail(taskId, thumbnailData, true /* refreshNow */);
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-        updateTaskStackListenerState();
-    }
-
-    @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
-        if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) == 0) {
-            return;
-        }
-        mModel.getIconCache().clear();
-        unloadVisibleTaskData();
-        loadVisibleTaskData();
-    }
-
-    public void init(OverviewActionsView actionsView) {
-        mActionsView = actionsView;
-        mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        updateTaskStackListenerState();
-        mModel.getThumbnailCache().getHighResLoadingState().addCallback(this);
-        mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-        mSyncTransactionApplier = new SurfaceTransactionApplier(this);
-        RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
-        mIdp.addOnChangeListener(this);
-        mIPinnedStackAnimationListener.setActivity(mActivity);
-        SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
-                mIPinnedStackAnimationListener);
-        mOrientationState.initListeners();
-        SplitScreenBounds.INSTANCE.addOnChangeListener(this);
-        mTaskOverlayFactory.initListeners();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        updateTaskStackListenerState();
-        mModel.getThumbnailCache().getHighResLoadingState().removeCallback(this);
-        mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
-        mSyncTransactionApplier = null;
-        RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
-        mIdp.removeOnChangeListener(this);
-        SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
-        SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
-        mIPinnedStackAnimationListener.setActivity(null);
-        mOrientationState.destroyListeners();
-        mTaskOverlayFactory.removeListeners();
-    }
-
-    @Override
-    public void onViewRemoved(View child) {
-        super.onViewRemoved(child);
-
-        // Clear the task data for the removed child if it was visible
-        if (child instanceof TaskView) {
-            TaskView taskView = (TaskView) child;
-            mHasVisibleTaskData.delete(taskView.getTask().key.id);
-            mTaskViewPool.recycle(taskView);
-            mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
-        }
-        updateTaskStartIndex(child);
-    }
-
-    @Override
-    public void onViewAdded(View child) {
-        super.onViewAdded(child);
-        child.setAlpha(mContentAlpha);
-        // 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();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        maybeDrawEmptyMessage(canvas);
-        super.draw(canvas);
-    }
-
-    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) {
-        // For now, just check if it's the active task or an adjacent task
-        return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
-    }
-
-    public TaskView getTaskView(int taskId) {
-        for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView tv = getTaskViewAt(i);
-            if (tv.getTask() != null && tv.getTask().key != null && tv.getTask().key.id == taskId) {
-                return tv;
-            }
-        }
-        return null;
-    }
-
-    public void setOverviewStateEnabled(boolean enabled) {
-        mOverviewStateEnabled = enabled;
-        updateTaskStackListenerState();
-        mOrientationState.setRotationWatcherEnabled(enabled);
-        if (!enabled) {
-            // Reset the running task when leaving overview since it can still have a reference to
-            // its thumbnail
-            mTmpRunningTask = null;
-        }
-    }
-
-    public void onDigitalWellbeingToastShown() {
-        if (!mDwbToastShown) {
-            mDwbToastShown = true;
-            mActivity.getUserEventDispatcher().logActionTip(
-                    LauncherEventUtil.VISIBLE,
-                    LauncherLogProto.TipType.DWB_TOAST);
-        }
-    }
-
-    /**
-     * Whether the Clear All button is hidden or fully visible. Used to determine if center
-     * displayed page is a task or the Clear All button.
-     *
-     * @return True = Clear All button not fully visible, center page is a task. False = Clear All
-     * button fully visible, center page is Clear All button.
-     */
-    public boolean isClearAllHidden() {
-        return mClearAllButton.getAlpha() != 1f;
-    }
-
-    @Override
-    protected void onPageBeginTransition() {
-        super.onPageBeginTransition();
-        mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
-    }
-
-    @Override
-    protected void onPageEndTransition() {
-        super.onPageEndTransition();
-        if (isClearAllHidden()) {
-            mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
-        }
-        if (getNextPage() > 0) {
-            setSwipeDownShouldLaunchApp(true);
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        super.onTouchEvent(ev);
-
-        TaskView taskView = getCurrentPageTaskView();
-        if (taskView != null && taskView.offerTouchToChildren(ev)) {
-            // Keep consuming events to pass to delegate
-            return true;
-        }
-
-        final int x = (int) ev.getX();
-        final int y = (int) ev.getY();
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_UP:
-                if (mTouchDownToStartHome) {
-                    startHome();
-                }
-                mTouchDownToStartHome = false;
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                mTouchDownToStartHome = false;
-                break;
-            case MotionEvent.ACTION_MOVE:
-                // Passing the touch slop will not allow dismiss to home
-                if (mTouchDownToStartHome &&
-                        (isHandlingTouch() ||
-                                squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop)) {
-                    mTouchDownToStartHome = false;
-                }
-                break;
-            case MotionEvent.ACTION_DOWN:
-                // Touch down anywhere but the deadzone around the visible clear all button and
-                // between the task views will start home on touch up
-                if (!isHandlingTouch() && !isModal()) {
-                    if (mShowEmptyMessage) {
-                        mTouchDownToStartHome = true;
-                    } else {
-                        updateDeadZoneRects();
-                        final boolean clearAllButtonDeadZoneConsumed =
-                                mClearAllButton.getAlpha() == 1
-                                        && mClearAllButtonDeadZoneRect.contains(x, y);
-                        final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
-                        if (!clearAllButtonDeadZoneConsumed && !cameFromNavBar
-                                && !mTaskViewDeadZoneRect.contains(x + getScrollX(), y)) {
-                            mTouchDownToStartHome = true;
-                        }
-                    }
-                }
-                mDownX = x;
-                mDownY = y;
-                break;
-        }
-
-
-        // Do not let touch escape to siblings below this view.
-        return isHandlingTouch() || shouldStealTouchFromSiblingsBelow(ev);
-    }
-
-    @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()) {
-            super.determineScrollingStart(ev, touchSlopScale);
-        }
-    }
-    protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) {
-        return true;
-    }
-
-    protected void applyLoadPlan(ArrayList<Task> tasks) {
-        if (mPendingAnimation != null) {
-            mPendingAnimation.addEndListener((endState) -> applyLoadPlan(tasks));
-            return;
-        }
-
-        if (tasks == null || tasks.isEmpty()) {
-            removeTasksViewsAndClearAllButton();
-            onTaskStackUpdated();
-            return;
-        }
-
-        // Unload existing visible task data
-        unloadVisibleTaskData();
-
-        TaskView ignoreResetTaskView =
-                mIgnoreResetTaskId == -1 ? null : getTaskView(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);
-            }
-        }
-
-        // 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 (mNextPage == INVALID_PAGE) {
-            // Set the current page to the running task, but not if settling on new task.
-            TaskView runningTaskView = getRunningTaskView();
-            if (runningTaskView != null) {
-                setCurrentPage(indexOfChild(runningTaskView));
-            } else if (getTaskViewCount() > 0) {
-                setCurrentPage(indexOfChild(getTaskViewAt(0)));
-            }
-        }
-
-        if (mIgnoreResetTaskId != -1 && getTaskView(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.
-            mIgnoreResetTaskId = -1;
-        }
-        resetTaskVisuals();
-        onTaskStackUpdated();
-        updateEnabledOverlays();
-    }
-
-    private boolean isModal() {
-        return mTaskModalness > 0;
-    }
-
-    private void removeTasksViewsAndClearAllButton() {
-        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            removeView(getTaskViewAt(i));
-        }
-        if (indexOfChild(mClearAllButton) != -1) {
-            removeView(mClearAllButton);
-        }
-    }
-
-    public int getTaskViewCount() {
-        int taskViewCount = getChildCount() - mTaskViewStartIndex;
-        if (indexOfChild(mClearAllButton) != -1) {
-            taskViewCount--;
-        }
-        return taskViewCount;
-    }
-
-    protected void onTaskStackUpdated() {
-        // Lazily update the empty message only when the task stack is reapplied
-        updateEmptyMessage();
-    }
-
-    public void resetTaskVisuals() {
-        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            TaskView taskView = getTaskViewAt(i);
-            if (mIgnoreResetTaskId != taskView.getTask().key.id) {
-                taskView.resetViewTransforms();
-                taskView.setStableAlpha(mContentAlpha);
-                taskView.setFullscreenProgress(mFullscreenProgress);
-                taskView.setModalness(mTaskModalness);
-            }
-        }
-        if (mRunningTaskTileHidden) {
-            setRunningTaskHidden(mRunningTaskTileHidden);
-        }
-
-        // Force apply the scale.
-        if (mIgnoreResetTaskId != mRunningTaskId) {
-            applyRunningTaskIconScale();
-        }
-
-        updateCurveProperties();
-        // Update the set of visible task's data
-        loadVisibleTaskData();
-        setTaskModalness(0);
-    }
-
-    public void setFullscreenProgress(float fullscreenProgress) {
-        mFullscreenProgress = fullscreenProgress;
-        int taskCount = getTaskViewCount();
-        for (int i = 0; i < taskCount; i++) {
-            getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
-        }
-        // Fade out the actions view quickly (0.1 range)
-        mActionsView.getFullscreenAlpha().setValue(
-                mapToRange(fullscreenProgress, 0, 0.1f, 1f, 0f, LINEAR));
-    }
-
-    private void updateTaskStackListenerState() {
-        boolean handleTaskStackChanges = mOverviewStateEnabled && isAttachedToWindow()
-                && getWindowVisibility() == VISIBLE;
-        if (handleTaskStackChanges != mHandleTaskStackChanges) {
-            mHandleTaskStackChanges = handleTaskStackChanges;
-            if (handleTaskStackChanges) {
-                reloadIfNeeded();
-            }
-        }
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        mInsets.set(insets);
-        resetPaddingFromTaskSize();
-    }
-
-    private void resetPaddingFromTaskSize() {
-        DeviceProfile dp = mActivity.getDeviceProfile();
-        getTaskSize(mTempRect);
-        mTaskWidth = mTempRect.width();
-        mTaskHeight = mTempRect.height();
-
-        mTempRect.top -= mTaskTopMargin;
-        setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
-                dp.widthPx - mInsets.right - mTempRect.right,
-                dp.heightPx - mInsets.bottom - mTempRect.bottom);
-    }
-
-    public void getTaskSize(Rect outRect) {
-        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect,
-                mOrientationHandler);
-        mLastComputedTaskSize.set(outRect);
-    }
-
-    /** Gets the task size for modal state. */
-    public void getModalTaskSize(Rect outRect) {
-        mSizeStrategy.calculateModalTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
-    }
-
-    @Override
-    protected boolean computeScrollHelper() {
-        boolean scrolling = super.computeScrollHelper();
-        boolean isFlingingFast = false;
-        updateCurveProperties();
-        if (scrolling || isHandlingTouch()) {
-            if (scrolling) {
-                // Check if we are flinging quickly to disable high res thumbnail loading
-                isFlingingFast = mScroller.getCurrVelocity() > mFastFlingVelocity;
-            }
-
-            // After scrolling, update the visible task's data
-            loadVisibleTaskData();
-        }
-
-        // Update the high res thumbnail loader state
-        mModel.getThumbnailCache().getHighResLoadingState().setFlingingFast(isFlingingFast);
-        return scrolling;
-    }
-
-    /**
-     * Scales and adjusts translation of adjacent pages as if on a curved carousel.
-     */
-    public void updateCurveProperties() {
-        if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
-            return;
-        }
-        mOrientationHandler.getCurveProperties(this, mInsets, mScrollState);
-        mScrollState.scrollFromEdge =
-                mIsRtl ? mScrollState.scroll : (mMaxScroll - mScrollState.scroll);
-
-        final int pageCount = getPageCount();
-        for (int i = 0; i < pageCount; i++) {
-            View page = getPageAt(i);
-            mScrollState.updateInterpolation(
-                    mOrientationHandler.getChildStartWithTranslation(page));
-            ((PageCallbacks) page).onPageScroll(mScrollState);
-        }
-    }
-
-    /**
-     * Iterates through all the tasks, and loads the associated task data for newly visible tasks,
-     * and unloads the associated task data for tasks that are no longer visible.
-     */
-    public void loadVisibleTaskData() {
-        if (!mOverviewStateEnabled || 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;
-        }
-
-        int centerPageIndex = getPageNearestToCenterOfScreen();
-        int numChildren = getChildCount();
-        int lower = Math.max(0, centerPageIndex - 2);
-        int upper = Math.min(centerPageIndex + 2, numChildren - 1);
-
-        // Update the task data for the in/visible children
-        for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView taskView = getTaskViewAt(i);
-            Task task = taskView.getTask();
-            int index = indexOfChild(taskView);
-            boolean visible = lower <= index && index <= upper;
-            if (visible) {
-                if (task == mTmpRunningTask) {
-                    // Skip loading if this is the task that we are animating into
-                    continue;
-                }
-                if (!mHasVisibleTaskData.get(task.key.id)) {
-                    taskView.onTaskListVisibilityChanged(true /* visible */);
-                }
-                mHasVisibleTaskData.put(task.key.id, visible);
-            } else {
-                if (mHasVisibleTaskData.get(task.key.id)) {
-                    taskView.onTaskListVisibilityChanged(false /* visible */);
-                }
-                mHasVisibleTaskData.delete(task.key.id);
-            }
-        }
-    }
-
-    /**
-     * Unloads any associated data from the currently visible tasks
-     */
-    private void unloadVisibleTaskData() {
-        for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
-            if (mHasVisibleTaskData.valueAt(i)) {
-                TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
-                if (taskView != null) {
-                    taskView.onTaskListVisibilityChanged(false /* visible */);
-                }
-            }
-        }
-        mHasVisibleTaskData.clear();
-    }
-
-    @Override
-    public void onHighResLoadingStateChanged(boolean enabled) {
-        // Whenever the high res loading state changes, poke each of the visible tasks to see if
-        // 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));
-                if (taskView != null) {
-                    // Poke the view again, which will trigger it to load high res if the state
-                    // is enabled
-                    taskView.onTaskListVisibilityChanged(true /* visible */);
-                }
-            }
-        }
-    }
-
-    public abstract void startHome();
-
-    /** `true` if there is a +1 space available in overview. */
-    public boolean hasRecentsExtraCard() {
-        return false;
-    }
-
-    public void reset() {
-        setCurrentTask(-1);
-        mIgnoreResetTaskId = -1;
-        mTaskListChangeId = -1;
-
-        mRecentsAnimationController = null;
-        mRecentsAnimationTargets = null;
-
-        unloadVisibleTaskData();
-        setCurrentPage(0);
-        mDwbToastShown = false;
-        mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
-        LayoutUtils.setViewEnabled(mActionsView, true);
-        if (mOrientationState.setGestureActive(false)) {
-            updateOrientationHandler();
-        }
-    }
-
-    public @Nullable TaskView getRunningTaskView() {
-        return getTaskView(mRunningTaskId);
-    }
-
-    public int getRunningTaskIndex() {
-        return getTaskIndexForId(mRunningTaskId);
-    }
-
-    /**
-     * Get the index of the task view whose id matches {@param taskId}.
-     * @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);
-        return tv == null ? -1 : indexOfChild(tv);
-    }
-
-    public int getTaskViewStartIndex() {
-        return mTaskViewStartIndex;
-    }
-
-    /**
-     * Reloads the view if anything in recents changed.
-     */
-    public void reloadIfNeeded() {
-        if (!mModel.isTaskListValid(mTaskListChangeId)) {
-            mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
-        }
-    }
-
-    /**
-     * Called when a gesture from an app is starting.
-     */
-    public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
-        // This needs to be called before the other states are set since it can create the task view
-        if (mOrientationState.setGestureActive(true)) {
-            updateOrientationHandler();
-        }
-
-        showCurrentTask(runningTaskInfo);
-        setEnableFreeScroll(false);
-        setEnableDrawingLiveTile(false);
-        setRunningTaskHidden(true);
-        setRunningTaskIconScaledDown(true);
-        mActionsView.updateHiddenFlags(HIDDEN_GESTURE_RUNNING, true);
-    }
-
-    /**
-     * Called only when a swipe-up gesture from an app has completed. Only called after
-     * {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}.
-     */
-    public void onSwipeUpAnimationSuccess() {
-        if (getRunningTaskView() != null) {
-            float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlayAttached
-                    ? LiveTileOverlay.INSTANCE.cancelIconAnimation()
-                    : 0f;
-            animateUpRunningTaskIconScale(startProgress);
-        }
-        setSwipeDownShouldLaunchApp(true);
-    }
-
-    private void animateRecentsRotationInPlace(int newRotation) {
-        if (mOrientationState.canRecentsActivityRotate()) {
-            // Let system take care of the rotation
-            return;
-        }
-        AnimatorSet pa = setRecentsChangedOrientation(true);
-        pa.addListener(AnimationSuccessListener.forRunnable(() -> {
-            setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
-            mActivity.getDragLayer().recreateControllers();
-            updateChildTaskOrientations();
-            setRecentsChangedOrientation(false).start();
-        }));
-        pa.start();
-    }
-
-    public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) {
-        getRunningTaskIndex();
-        int runningIndex = getCurrentPage();
-        AnimatorSet as = new AnimatorSet();
-        for (int i = 0; i < getTaskViewCount(); i++) {
-            if (runningIndex == i) {
-                continue;
-            }
-            View taskView = getTaskViewAt(i);
-            as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1));
-        }
-        return as;
-    }
-
-
-    private void updateChildTaskOrientations() {
-        for (int i = 0; i < getTaskViewCount(); i++) {
-            getTaskViewAt(i).setOrientationState(mOrientationState);
-        }
-    }
-
-    /**
-     * Called when a gesture from an app has finished.
-     */
-    public void onGestureAnimationEnd() {
-        if (mOrientationState.setGestureActive(false)) {
-            updateOrientationHandler();
-        }
-
-        setOnScrollChangeListener(null);
-        setEnableFreeScroll(true);
-        setEnableDrawingLiveTile(true);
-        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            setRunningTaskViewShowScreenshot(true);
-        }
-        setRunningTaskHidden(false);
-        animateUpRunningTaskIconScale();
-        animateActionsViewIn();
-    }
-
-    /**
-     * Returns true if we should add a dummy taskView for the running task id
-     */
-    protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
-        return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null;
-    }
-
-    /**
-     * Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
-     *
-     * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
-     * is called.  Also scrolls the view to this task.
-     */
-    public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
-        if (shouldAddDummyTaskView(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);
-            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());
-        }
-
-        boolean runningTaskTileHidden = mRunningTaskTileHidden;
-        setCurrentTask(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
-        setCurrentPage(getRunningTaskIndex());
-        setRunningTaskViewShowScreenshot(false);
-        setRunningTaskHidden(runningTaskTileHidden);
-
-        // Reload the task list
-        mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
-    }
-
-    /**
-     * Sets the running task id, cleaning up the old running task if necessary.
-     * @param runningTaskId
-     */
-    public void setCurrentTask(int runningTaskId) {
-        if (mRunningTaskId == runningTaskId) {
-            return;
-        }
-
-        if (mRunningTaskId != -1) {
-            // Reset the state on the old running task view
-            setRunningTaskIconScaledDown(false);
-            setRunningTaskViewShowScreenshot(true);
-            setRunningTaskHidden(false);
-        }
-        mRunningTaskId = runningTaskId;
-    }
-
-    /**
-     * Hides the tile associated with {@link #mRunningTaskId}
-     */
-    public void setRunningTaskHidden(boolean isHidden) {
-        mRunningTaskTileHidden = isHidden;
-        TaskView runningTask = getRunningTaskView();
-        if (runningTask != null) {
-            runningTask.setStableAlpha(isHidden ? 0 : mContentAlpha);
-            if (!isHidden) {
-                AccessibilityManagerCompat.sendCustomAccessibilityEvent(runningTask,
-                        AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
-            }
-        }
-    }
-
-    private void setRunningTaskViewShowScreenshot(boolean showScreenshot) {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            TaskView runningTaskView = getRunningTaskView();
-            if (runningTaskView != null) {
-                runningTaskView.setShowScreenshot(showScreenshot);
-            }
-        }
-    }
-
-    public void showNextTask() {
-        TaskView runningTaskView = getRunningTaskView();
-        if (runningTaskView == null) {
-            // Launch the first task
-            if (getTaskViewCount() > 0) {
-                getTaskViewAt(0).launchTask(true);
-            }
-        } else {
-            if (getNextTaskView() != null) {
-                getNextTaskView().launchTask(true);
-            } else {
-                runningTaskView.launchTask(true);
-            }
-        }
-    }
-
-    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);
-        }
-    }
-
-    private void animateActionsViewIn() {
-        mActionsView.updateHiddenFlags(HIDDEN_GESTURE_RUNNING, false);
-        ObjectAnimator anim = ObjectAnimator.ofFloat(
-                mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, 0, 1);
-        anim.setDuration(TaskView.SCALE_ICON_DURATION);
-        anim.start();
-    }
-
-    public void animateUpRunningTaskIconScale() {
-        animateUpRunningTaskIconScale(0);
-    }
-
-    public void animateUpRunningTaskIconScale(float startProgress) {
-        mRunningTaskIconScaledDown = false;
-        TaskView firstTask = getRunningTaskView();
-        if (firstTask != null) {
-            firstTask.animateIconScaleAndDimIntoView();
-            firstTask.setIconScaleAnimStartProgress(startProgress);
-        }
-    }
-
-    private void enableLayoutTransitions() {
-        if (mLayoutTransition == null) {
-            mLayoutTransition = new LayoutTransition();
-            mLayoutTransition.enableTransitionType(LayoutTransition.APPEARING);
-            mLayoutTransition.setDuration(ADDITION_TASK_DURATION);
-            mLayoutTransition.setStartDelay(LayoutTransition.APPEARING, 0);
-
-            mLayoutTransition.addTransitionListener(new TransitionListener() {
-                @Override
-                public void startTransition(LayoutTransition transition, ViewGroup viewGroup,
-                    View view, int i) {
-                }
-
-                @Override
-                public void endTransition(LayoutTransition transition, ViewGroup viewGroup,
-                    View view, int i) {
-                    // When the unpinned task is added, snap to first page and disable transitions
-                    if (view instanceof TaskView) {
-                        snapToPage(0);
-                        disableLayoutTransitions();
-                    }
-
-                }
-            });
-        }
-        setLayoutTransition(mLayoutTransition);
-    }
-
-    private void disableLayoutTransitions() {
-        setLayoutTransition(null);
-    }
-
-    public void setSwipeDownShouldLaunchApp(boolean swipeDownShouldLaunchApp) {
-        mSwipeDownShouldLaunchApp = swipeDownShouldLaunchApp;
-    }
-
-    public boolean shouldSwipeDownLaunchApp() {
-        return mSwipeDownShouldLaunchApp;
-    }
-
-    public interface PageCallbacks {
-
-        /**
-         * Updates the page UI based on scroll params.
-         */
-        default void onPageScroll(ScrollState scrollState) {}
-    }
-
-    public static class ScrollState extends CurveProperties {
-
-        /**
-         * The progress from 0 to 1, where 0 is the center
-         * of the screen and 1 is the edge of the screen.
-         */
-        public float linearInterpolation;
-
-        /**
-         * The amount by which all the content is scrolled relative to the end of the list.
-         */
-        public float scrollFromEdge;
-
-        /**
-         * Updates linearInterpolation for the provided child position
-         */
-        public void updateInterpolation(float childStart) {
-            float pageCenter = childStart + halfPageSize;
-            float distanceFromScreenCenter = screenCenter - pageCenter;
-            // How far the page has to move from the center to be offscreen, taking into account
-            // the EDGE_SCALE_DOWN_FACTOR that will be applied at that position.
-            float distanceToReachEdge = halfScreenSize
-                    + halfPageSize * (1 - TaskView.EDGE_SCALE_DOWN_FACTOR);
-            linearInterpolation = Math.min(1,
-                    Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
-        }
-    }
-
-    public void setIgnoreResetTask(int taskId) {
-        mIgnoreResetTaskId = taskId;
-    }
-
-    public void clearIgnoreResetTask(int taskId) {
-        if (mIgnoreResetTaskId == taskId) {
-            mIgnoreResetTaskId = -1;
-        }
-    }
-
-    private void addDismissedTaskAnimations(View taskView, long duration, 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, ACCEL_2);
-        FloatProperty<View> secondaryViewTranslate =
-            mOrientationHandler.getSecondaryViewTranslate();
-        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));
-
-        anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
-                verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp);
-    }
-
-    private void removeTask(TaskView taskView, int index, EndState endState) {
-        if (taskView.getTask() != null) {
-            ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
-            ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
-            mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                    endState.logAction, Direction.UP, index, compKey);
-            mActivity.getStatsLogManager().logger().withItemInfo(taskView.getItemInfo())
-                    .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
-        }
-    }
-
-    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
-            boolean shouldRemoveTask, long duration) {
-        if (mPendingAnimation != null) {
-            mPendingAnimation.finish(false, Touch.SWIPE);
-        }
-        PendingAnimation anim = new PendingAnimation(duration);
-
-        int count = getPageCount();
-        if (count == 0) {
-            return anim;
-        }
-
-        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 needsCurveUpdates = false;
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            if (child == taskView) {
-                if (animateTaskView) {
-                    addDismissedTaskAnimations(taskView, duration, anim);
-                }
-            } else {
-                // 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:
-                // - In RTL, add page offset to all pages, since we want pages to move to the right
-                // Additionally, add a page offset if:
-                // - 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) {
-                    int lastPage = taskCount - 1;
-                    if (mCurrentPage == lastPage) {
-                        offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
-                    }
-                } else {
-                    // Dragging an adjacent page.
-                    int negativeAdjacent = mCurrentPage - 1; // (Right in RTL, left in LTR)
-                    if (draggedIndex == negativeAdjacent) {
-                        offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
-                    }
-                }
-                int scrollDiff = newScroll[i] - oldScroll[i] + offset;
-                if (scrollDiff != 0) {
-                    FloatProperty translationProperty = child instanceof TaskView
-                            ? ((TaskView) child).getPrimaryFillDismissGapTranslationProperty()
-                            : 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));
-                    anim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff)
-                            .setDuration(duration), ACCEL, sp);
-                    needsCurveUpdates = true;
-                }
-            }
-        }
-
-        if (needsCurveUpdates) {
-            anim.addOnFrameCallback(this::updateCurveProperties);
-        }
-
-        // Add a tiny bit of translation Z, so that it draws on top of other views
-        if (animateTaskView) {
-            taskView.setTranslationZ(0.1f);
-        }
-
-        mPendingAnimation = anim;
-        mPendingAnimation.addEndListener(new Consumer<EndState>() {
-            @Override
-            public void accept(EndState endState) {
-                if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
-                        taskView.isRunningTask() && endState.isSuccess) {
-                    finishRecentsAnimation(true /* toHome */, () -> onEnd(endState));
-                } else {
-                    onEnd(endState);
-                }
-            }
-
-            @SuppressWarnings("WrongCall")
-            private void onEnd(EndState endState) {
-                if (endState.isSuccess) {
-                    if (shouldRemoveTask) {
-                        removeTask(taskView, draggedIndex, endState);
-                    }
-
-                    int pageToSnapTo = mCurrentPage;
-                    if (draggedIndex < pageToSnapTo ||
-                            pageToSnapTo == (getTaskViewCount() - 1)) {
-                        pageToSnapTo -= 1;
-                    }
-                    removeViewInLayout(taskView);
-
-                    if (getTaskViewCount() == 0) {
-                        removeViewInLayout(mClearAllButton);
-                        startHome();
-                    } else {
-                        snapToPageImmediately(pageToSnapTo);
-                    }
-                    // Update the layout synchronously so that the position of next view is
-                    // immediately available.
-                    onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
-                }
-                resetTaskVisuals();
-                mPendingAnimation = null;
-            }
-        });
-        return anim;
-    }
-
-    public PendingAnimation createAllTasksDismissAnimation(long duration) {
-        if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
-            throw new IllegalStateException("Another pending animation is still running");
-        }
-        PendingAnimation anim = new PendingAnimation(duration);
-
-        int count = getTaskViewCount();
-        for (int i = 0; i < count; i++) {
-            addDismissedTaskAnimations(getTaskViewAt(i), duration, anim);
-        }
-
-        mPendingAnimation = anim;
-        mPendingAnimation.addEndListener((endState) -> {
-            if (endState.isSuccess) {
-                // Remove all the task views now
-                ActivityManagerWrapper.getInstance().removeAllRecentTasks();
-                removeTasksViewsAndClearAllButton();
-                startHome();
-            }
-            mPendingAnimation = null;
-        });
-        return anim;
-    }
-
-    private boolean snapToPageRelative(int pageCount, int delta, boolean cycle) {
-        if (pageCount == 0) {
-            return false;
-        }
-        final int newPageUnbound = getNextPage() + delta;
-        if (!cycle && (newPageUnbound < 0 || newPageUnbound >= pageCount)) {
-            return false;
-        }
-        snapToPage((newPageUnbound + pageCount) % pageCount);
-        getChildAt(getNextPage()).requestFocus();
-        return true;
-    }
-
-    protected void runDismissAnimation(PendingAnimation pendingAnim) {
-        AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
-        controller.dispatchOnStart();
-        controller.setEndAction(() -> pendingAnim.finish(true, Touch.SWIPE));
-        controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
-        controller.start();
-    }
-
-    public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
-        runDismissAnimation(createTaskDismissAnimation(taskView, animateTaskView, removeTask,
-                DISMISS_TASK_DURATION));
-    }
-
-    @SuppressWarnings("unused")
-    private void dismissAllTasks(View view) {
-        runDismissAnimation(createAllTasksDismissAnimation(DISMISS_TASK_DURATION));
-        mActivity.getUserEventDispatcher().logActionOnControl(TAP, CLEAR_ALL_BUTTON);
-    }
-
-    private void dismissCurrentTask() {
-        TaskView taskView = getNextPageTaskView();
-        if (taskView != null) {
-            dismissTask(taskView, true /*animateTaskView*/, true /*removeTask*/);
-        }
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            switch (event.getKeyCode()) {
-                case KeyEvent.KEYCODE_TAB:
-                    return snapToPageRelative(getTaskViewCount(), event.isShiftPressed() ? -1 : 1,
-                            event.isAltPressed() /* cycle */);
-                case KeyEvent.KEYCODE_DPAD_RIGHT:
-                    return snapToPageRelative(getPageCount(), mIsRtl ? -1 : 1, false /* cycle */);
-                case KeyEvent.KEYCODE_DPAD_LEFT:
-                    return snapToPageRelative(getPageCount(), mIsRtl ? 1 : -1, false /* cycle */);
-                case KeyEvent.KEYCODE_DEL:
-                case KeyEvent.KEYCODE_FORWARD_DEL:
-                    dismissCurrentTask();
-                    return true;
-                case KeyEvent.KEYCODE_NUMPAD_DOT:
-                    if (event.isAltPressed()) {
-                        // Numpad DEL pressed while holding Alt.
-                        dismissCurrentTask();
-                        return true;
-                    }
-            }
-        }
-        return super.dispatchKeyEvent(event);
-    }
-
-    @Override
-    protected void onFocusChanged(boolean gainFocus, int direction,
-            @Nullable Rect previouslyFocusedRect) {
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        if (gainFocus && getChildCount() > 0) {
-            switch (direction) {
-                case FOCUS_FORWARD:
-                    setCurrentPage(0);
-                    break;
-                case FOCUS_BACKWARD:
-                case FOCUS_RIGHT:
-                case FOCUS_LEFT:
-                    setCurrentPage(getChildCount() - 1);
-                    break;
-            }
-        }
-    }
-
-    public float getContentAlpha() {
-        return mContentAlpha;
-    }
-
-    public void setContentAlpha(float alpha) {
-        if (alpha == mContentAlpha) {
-            return;
-        }
-        alpha = Utilities.boundToRange(alpha, 0, 1);
-        mContentAlpha = alpha;
-        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            TaskView child = getTaskViewAt(i);
-            if (!mRunningTaskTileHidden || child.getTask().key.id != mRunningTaskId) {
-                child.setStableAlpha(alpha);
-            }
-        }
-        mClearAllButton.setContentAlpha(mContentAlpha);
-        int alphaInt = Math.round(alpha * 255);
-        mEmptyMessagePaint.setAlpha(alphaInt);
-        mEmptyIcon.setAlpha(alphaInt);
-        mActionsView.getContentAlpha().setValue(mContentAlpha);
-
-        if (alpha > 0) {
-            setVisibility(VISIBLE);
-        } else if (!mFreezeViewVisibility) {
-            setVisibility(GONE);
-        }
-    }
-
-    /**
-     * Freezes the view visibility change. When frozen, the view will not change its visibility
-     * to gone due to alpha changes.
-     */
-    public void setFreezeViewVisibility(boolean freezeViewVisibility) {
-        if (mFreezeViewVisibility != freezeViewVisibility) {
-            mFreezeViewVisibility = freezeViewVisibility;
-            if (!mFreezeViewVisibility) {
-                setVisibility(mContentAlpha > 0 ? VISIBLE : GONE);
-            }
-        }
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-        if (mActionsView != null) {
-            mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE);
-            if (visibility != VISIBLE) {
-                mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
-            }
-        }
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        if (mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation())) {
-            updateOrientationHandler();
-        }
-        // 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();
-        }
-    }
-
-    public void setLayoutRotation(int touchRotation, int displayRotation) {
-        if (mOrientationState.update(touchRotation, displayRotation)) {
-            updateOrientationHandler();
-        }
-    }
-
-    private void updateOrientationHandler() {
-        mOrientationHandler = mOrientationState.getOrientationHandler();
-        mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
-        setLayoutDirection(mIsRtl
-                ? View.LAYOUT_DIRECTION_RTL
-                : View.LAYOUT_DIRECTION_LTR);
-        mClearAllButton.setLayoutDirection(mIsRtl
-                ? View.LAYOUT_DIRECTION_LTR
-                : View.LAYOUT_DIRECTION_RTL);
-        mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
-        mActivity.getDragLayer().recreateControllers();
-        boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
-                || mOrientationState.getRecentsActivityRotation() != ROTATION_0;
-        mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
-                !mOrientationState.canRecentsActivityRotate() && isInLandscape);
-        updateChildTaskOrientations();
-        resetPaddingFromTaskSize();
-        requestLayout();
-        // Reapply the current page to update page scrolls.
-        setCurrentPage(mCurrentPage);
-    }
-
-    public RecentsOrientedState getPagedViewOrientedState() {
-        return mOrientationState;
-    }
-
-    public PagedOrientationHandler getPagedOrientationHandler() {
-        return mOrientationHandler;
-    }
-
-    @Nullable
-    public TaskView getNextTaskView() {
-        return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() + 1);
-    }
-
-    @Nullable
-    public TaskView getCurrentPageTaskView() {
-        return getTaskViewAtByAbsoluteIndex(getCurrentPage());
-    }
-
-    @Nullable
-    public TaskView getNextPageTaskView() {
-        return getTaskViewAtByAbsoluteIndex(getNextPage());
-    }
-
-    @Nullable
-    public TaskView getTaskViewNearestToCenterOfScreen() {
-        return getTaskViewAtByAbsoluteIndex(getPageNearestToCenterOfScreen());
-    }
-
-    /**
-     * Returns null instead of indexOutOfBoundsError when index is not in range
-     */
-    @Nullable
-    public TaskView getTaskViewAt(int index) {
-        return getTaskViewAtByAbsoluteIndex(index + mTaskViewStartIndex);
-    }
-
-    @Nullable
-    private TaskView getTaskViewAtByAbsoluteIndex(int index) {
-        if (index < getChildCount() && index >= 0) {
-            View child = getChildAt(index);
-            return child instanceof TaskView ? (TaskView) child : null;
-        }
-        return null;
-    }
-
-    public void setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener listener) {
-        mOnEmptyMessageUpdatedListener = listener;
-    }
-
-    public void updateEmptyMessage() {
-        boolean isEmpty = getTaskViewCount() == 0;
-        boolean hasSizeChanged = mLastMeasureSize.x != getWidth()
-                || mLastMeasureSize.y != getHeight();
-        if (isEmpty == mShowEmptyMessage && !hasSizeChanged) {
-            return;
-        }
-        setContentDescription(isEmpty ? mEmptyMessage : "");
-        mShowEmptyMessage = isEmpty;
-        updateEmptyStateUi(hasSizeChanged);
-        invalidate();
-
-        if (mOnEmptyMessageUpdatedListener != null) {
-            mOnEmptyMessageUpdatedListener.onEmptyMessageUpdated(mShowEmptyMessage);
-        }
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        updateEmptyStateUi(changed);
-
-        // Update the pivots such that when the task is scaled, it fills the full page
-        getTaskSize(mTempRect);
-        getPagedViewOrientedState().getFullScreenScaleAndPivot(
-                mTempRect, mActivity.getDeviceProfile(), mTempPointF);
-        setPivotX(mTempPointF.x);
-        setPivotY(mTempPointF.y);
-        setTaskModalness(mTaskModalness);
-        mLastComputedTaskPushOutDistance = null;
-        updatePageOffsets();
-        setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
-                : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-    }
-
-    private void updatePageOffsets() {
-        float offset = mAdjacentPageOffset;
-        float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
-        if (mIsRtl) {
-            offset = -offset;
-            modalOffset = -modalOffset;
-        }
-        int count = getChildCount();
-
-        TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
-                ? null : getTaskView(mRunningTaskId);
-        int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
-        int modalMidpoint = getCurrentPage();
-
-        float midpointOffsetSize = 0;
-        float leftOffsetSize = midpoint - 1 >= 0
-                ? -getOffsetSize(midpoint - 1, midpoint, offset)
-                : 0;
-        float rightOffsetSize = midpoint + 1 < count
-                ? getOffsetSize(midpoint + 1, midpoint, offset)
-                : 0;
-
-        float modalMidpointOffsetSize = 0;
-        float modalLeftOffsetSize = modalMidpoint - 1 >= 0
-                ? -getOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset)
-                : 0;
-        float modalRightOffsetSize = modalMidpoint + 1 < count
-                ? getOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset)
-                : 0;
-
-        for (int i = 0; i < count; i++) {
-            float translation = i == midpoint
-                    ? midpointOffsetSize
-                    : i < midpoint
-                            ? leftOffsetSize
-                            : rightOffsetSize;
-            float modalTranslation = i == modalMidpoint
-                    ? modalMidpointOffsetSize
-                    : i < modalMidpoint
-                            ? modalLeftOffsetSize
-                            : modalRightOffsetSize;
-            float totalTranslation = translation + modalTranslation;
-            View child = getChildAt(i);
-            FloatProperty translationProperty = child instanceof TaskView
-                    ? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
-                    : mOrientationHandler.getPrimaryViewTranslate();
-            translationProperty.set(child,
-                    totalTranslation * mOrientationHandler.getPrimaryTranslationDirectionFactor());
-        }
-        updateCurveProperties();
-    }
-
-    /**
-     * Computes the distance to offset the given child such that it is completely offscreen when
-     * translating away from the given midpoint.
-     * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
-     */
-    private float getOffsetSize(int childIndex, int midpointIndex, float offsetProgress) {
-        if (offsetProgress == 0) {
-            // Don't bother calculating everything below if we won't offset anyway.
-            return 0;
-        }
-        // First, get the position of the task relative to the midpoint. If there is no midpoint
-        // then we just use the normal (centered) task position.
-        mTempRectF.set(mLastComputedTaskSize);
-        RectF taskPosition = mTempRectF;
-        float desiredLeft = getWidth();
-        float distanceToOffscreen = desiredLeft - taskPosition.left;
-        // Used to calculate the scale of the task view based on its new offset.
-        float centerToOffscreenProgress = Math.abs(offsetProgress);
-        if (midpointIndex > -1) {
-            // When there is a midpoint reference task, adjacent tasks have less distance to travel
-            // to reach offscreen. Offset the task position to the task's starting point.
-            View child = getChildAt(childIndex);
-            View midpointChild = getChildAt(midpointIndex);
-            int distanceFromMidpoint = Math.abs(mOrientationHandler.getChildStart(child)
-                    - mOrientationHandler.getChildStart(midpointChild)
-                    + getDisplacementFromScreenCenter(midpointIndex));
-            taskPosition.offset(distanceFromMidpoint, 0);
-            centerToOffscreenProgress = Utilities.mapRange(centerToOffscreenProgress,
-                    distanceFromMidpoint / distanceToOffscreen, 1);
-        }
-        // Find the task's scale based on its offscreen progress, then see how far it still needs to
-        // move to be completely offscreen.
-        Utilities.scaleRectFAboutCenter(taskPosition,
-                TaskView.getCurveScaleForInterpolation(centerToOffscreenProgress));
-        distanceToOffscreen = desiredLeft - taskPosition.left;
-        // Finally, we need to account for RecentsView scale, because it moves tasks based on its
-        // pivot. To do this, we move the task position to where it would be offscreen at scale = 1
-        // (computed above), then we apply the scale via getMatrix() to determine how much that
-        // moves the task from its desired position, and adjust the computed distance accordingly.
-        if (mLastComputedTaskPushOutDistance == null) {
-            taskPosition.offsetTo(desiredLeft, 0);
-            getMatrix().mapRect(taskPosition);
-            mLastComputedTaskPushOutDistance = (taskPosition.left - desiredLeft) / getScaleX();
-        }
-        distanceToOffscreen -= mLastComputedTaskPushOutDistance;
-        return distanceToOffscreen * offsetProgress;
-    }
-
-    private void setTaskViewsSecondaryTranslation(float translation) {
-        mTaskViewsSecondaryTranslation = translation;
-        for (int i = 0; i < getTaskViewCount(); i++) {
-            TaskView task = getTaskViewAt(i);
-            mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
-        }
-    }
-
-    /**
-     * TODO: Do not assume motion across X axis for adjacent page
-     */
-    public float getPageOffsetScale() {
-        return Math.max(getWidth(), 1);
-    }
-
-    /**
-     * Resets the visuals when exit modal state.
-     */
-    public void resetModalVisuals() {
-        TaskView taskView = getCurrentPageTaskView();
-        if (taskView != null) {
-            taskView.getThumbnail().getTaskOverlay().resetModalVisuals();
-        }
-    }
-
-    private void updateDeadZoneRects() {
-        // Get the deadzone rect surrounding the clear all button to not dismiss overview to home
-        mClearAllButtonDeadZoneRect.setEmpty();
-        if (mClearAllButton.getWidth() > 0) {
-            int verticalMargin = getResources()
-                    .getDimensionPixelSize(R.dimen.recents_clear_all_deadzone_vertical_margin);
-            mClearAllButton.getHitRect(mClearAllButtonDeadZoneRect);
-            mClearAllButtonDeadZoneRect.inset(-getPaddingRight() / 2, -verticalMargin);
-        }
-
-        // Get the deadzone rect between the task views
-        mTaskViewDeadZoneRect.setEmpty();
-        int count = getTaskViewCount();
-        if (count > 0) {
-            final View taskView = getTaskViewAt(0);
-            getTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
-            mTaskViewDeadZoneRect.union(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
-                    taskView.getBottom());
-        }
-    }
-
-    private void updateEmptyStateUi(boolean sizeChanged) {
-        boolean hasValidSize = getWidth() > 0 && getHeight() > 0;
-        if (sizeChanged && hasValidSize) {
-            mEmptyTextLayout = null;
-            mLastMeasureSize.set(getWidth(), getHeight());
-        }
-
-        if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
-            int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
-            mEmptyTextLayout = StaticLayout.Builder.obtain(mEmptyMessage, 0, mEmptyMessage.length(),
-                    mEmptyMessagePaint, availableWidth)
-                    .setAlignment(Layout.Alignment.ALIGN_CENTER)
-                    .build();
-            int totalHeight = mEmptyTextLayout.getHeight()
-                    + mEmptyMessagePadding + mEmptyIcon.getIntrinsicHeight();
-
-            int top = (mLastMeasureSize.y - totalHeight) / 2;
-            int left = (mLastMeasureSize.x - mEmptyIcon.getIntrinsicWidth()) / 2;
-            mEmptyIcon.setBounds(left, top, left + mEmptyIcon.getIntrinsicWidth(),
-                    top + mEmptyIcon.getIntrinsicHeight());
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || (mShowEmptyMessage && who == mEmptyIcon);
-    }
-
-    protected void maybeDrawEmptyMessage(Canvas canvas) {
-        if (mShowEmptyMessage && mEmptyTextLayout != null) {
-            // Offset to center in the visible (non-padded) part of RecentsView
-            mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
-                    mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
-            canvas.save();
-            canvas.translate(getScrollX() + (mTempRect.left - mTempRect.right) / 2,
-                    (mTempRect.top - mTempRect.bottom) / 2);
-            mEmptyIcon.draw(canvas);
-            canvas.translate(mEmptyMessagePadding,
-                    mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
-            mEmptyTextLayout.draw(canvas);
-            canvas.restore();
-        }
-    }
-
-    /**
-     * Animate adjacent tasks off screen while scaling up.
-     *
-     * If launching one of the adjacent tasks, parallax the center task and other adjacent task
-     * to the right.
-     */
-    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
-        AnimatorSet anim = new AnimatorSet();
-
-        int taskIndex = indexOfChild(tv);
-        int centerTaskIndex = getCurrentPage();
-        boolean launchingCenterTask = taskIndex == centerTaskIndex;
-
-        float toScale = getMaxScaleForFullScreen();
-        if (launchingCenterTask) {
-            RecentsView recentsView = tv.getRecentsView();
-            anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale));
-            anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1));
-        } else {
-            // We are launching an adjacent task, so parallax the center and other adjacent task.
-            float displacementX = tv.getWidth() * (toScale - tv.getCurveScale());
-            anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex), TRANSLATION_X,
-                    mIsRtl ? -displacementX : displacementX));
-
-            int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
-            if (otherAdjacentTaskIndex >= 0 && otherAdjacentTaskIndex < getPageCount()) {
-                anim.play(new PropertyListBuilder()
-                        .translationX(mIsRtl ? -displacementX : displacementX)
-                        .scale(1)
-                        .build(getPageAt(otherAdjacentTaskIndex)));
-            }
-        }
-        return anim;
-    }
-
-    /**
-     * Returns the scale up required on the view, so that it coves the screen completely
-     */
-    public float getMaxScaleForFullScreen() {
-        getTaskSize(mTempRect);
-        return getPagedViewOrientedState().getFullScreenScaleAndPivot(
-                mTempRect, mActivity.getDeviceProfile(), mTempPointF);
-    }
-
-    public PendingAnimation createTaskLaunchAnimation(
-            TaskView tv, long duration, Interpolator interpolator) {
-        if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
-            throw new IllegalStateException("Another pending animation is still running");
-        }
-
-        int count = getTaskViewCount();
-        if (count == 0) {
-            return new PendingAnimation(duration);
-        }
-
-        int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
-        final boolean[] passedOverviewThreshold = new boolean[] {false};
-        ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
-        progressAnim.addUpdateListener(animator -> {
-            // Once we pass a certain threshold, update the sysui flags to match the target
-            // tasks' flags
-            mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW,
-                    animator.getAnimatedFraction() > UPDATE_SYSUI_FLAGS_THRESHOLD
-                            ? targetSysUiFlags
-                            : 0);
-
-            onTaskLaunchAnimationUpdate(animator.getAnimatedFraction(), tv);
-
-            // Passing the threshold from taskview to fullscreen app will vibrate
-            final boolean passed = animator.getAnimatedFraction() >=
-                    SUCCESS_TRANSITION_PROGRESS;
-            if (passed != passedOverviewThreshold[0]) {
-                passedOverviewThreshold[0] = passed;
-                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-            }
-        });
-
-        AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv);
-
-        DepthController depthController = getDepthController();
-        if (depthController != null) {
-            ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController, DEPTH,
-                    BACKGROUND_APP.getDepth(mActivity));
-            anim.play(depthAnimator);
-        }
-        anim.play(progressAnim);
-        anim.setInterpolator(interpolator);
-
-        mPendingAnimation = new PendingAnimation(duration);
-        mPendingAnimation.add(anim);
-        mPendingAnimation.addEndListener((endState) -> {
-            if (endState.isSuccess) {
-                Consumer<Boolean> onLaunchResult = (result) -> {
-                    onTaskLaunchAnimationEnd(result);
-                    if (!result) {
-                        tv.notifyTaskLaunchFailed(TAG);
-                    }
-                };
-                tv.launchTask(false, onLaunchResult, getHandler());
-                Task task = tv.getTask();
-                if (task != null) {
-                    mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                            endState.logAction, Direction.DOWN, indexOfChild(tv),
-                            TaskUtils.getLaunchComponentKeyForTask(task.key));
-                    mActivity.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
-                            .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
-                }
-            } else {
-                onTaskLaunchAnimationEnd(false);
-            }
-            mPendingAnimation = null;
-        });
-        return mPendingAnimation;
-    }
-
-    protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
-    }
-
-    public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
-
-    protected void onTaskLaunchAnimationEnd(boolean success) {
-        if (success) {
-            resetTaskVisuals();
-        }
-    }
-
-    /**
-     * Called when task activity is launched
-     */
-    public void onTaskLaunched(Task task){ }
-
-    @Override
-    protected void notifyPageSwitchListener(int prevPage) {
-        super.notifyPageSwitchListener(prevPage);
-        loadVisibleTaskData();
-        updateEnabledOverlays();
-    }
-
-    @Override
-    protected String getCurrentPageDescription() {
-        return "";
-    }
-
-    @Override
-    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
-        // Add children in reverse order
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            outChildren.add(getChildAt(i));
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        final AccessibilityNodeInfo.CollectionInfo
-                collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain(
-                1, getTaskViewCount(), false,
-                AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE);
-        info.setCollectionInfo(collectionInfo);
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-
-        final int taskViewCount = getTaskViewCount();
-        event.setScrollable(taskViewCount > 0);
-
-        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
-            final int[] visibleTasks = getVisibleChildrenRange();
-            event.setFromIndex(taskViewCount - visibleTasks[1]);
-            event.setToIndex(taskViewCount - visibleTasks[0]);
-            event.setItemCount(taskViewCount);
-        }
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName() {
-        // To hear position-in-list related feedback from Talkback.
-        return ListView.class.getName();
-    }
-
-    @Override
-    protected boolean isPageOrderFlipped() {
-        return true;
-    }
-
-    public void setEnableDrawingLiveTile(boolean enableDrawingLiveTile) {
-        mEnableDrawingLiveTile = enableDrawingLiveTile;
-    }
-
-    public void redrawLiveTile(boolean mightNeedToRefill) { }
-
-    // TODO: To be removed in a follow up CL
-    public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
-            RecentsAnimationTargets recentsAnimationTargets) {
-        mRecentsAnimationController = recentsAnimationController;
-        mRecentsAnimationTargets = recentsAnimationTargets;
-    }
-
-    public void setLiveTileOverlayAttached(boolean liveTileOverlayAttached) {
-        mLiveTileOverlayAttached = liveTileOverlayAttached;
-    }
-
-    public void updateLiveTileIcon(Drawable icon) {
-        if (mLiveTileOverlayAttached) {
-            LiveTileOverlay.INSTANCE.setIcon(icon);
-        }
-    }
-
-    public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
-        if (mRecentsAnimationController == null) {
-            if (onFinishComplete != null) {
-                onFinishComplete.run();
-            }
-            return;
-        }
-
-        mRecentsAnimationController.finish(toRecents, () -> {
-            if (onFinishComplete != null) {
-                onFinishComplete.run();
-                // After we finish the recents animation, the current task id should be correctly
-                // reset so that when the task is launched from Overview later, it goes through the
-                // flow of starting a new task instead of finishing recents animation to app. A
-                // typical example of this is (1) user swipes up from app to Overview (2) user
-                // taps on QSB (3) user goes back to Overview and launch the most recent task.
-                setCurrentTask(-1);
-            }
-        });
-    }
-
-    public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
-        if (mDisallowScrollToClearAll != disallowScrollToClearAll) {
-            mDisallowScrollToClearAll = disallowScrollToClearAll;
-            updateMinAndMaxScrollX();
-        }
-    }
-
-    @Override
-    protected int computeMinScroll() {
-        if (getTaskViewCount() > 0) {
-            if (mDisallowScrollToClearAll) {
-                // We aren't showing the clear all button,
-                // so use the leftmost task as the min scroll.
-                if (mIsRtl) {
-                    return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
-                }
-                return getScrollForPage(mTaskViewStartIndex);
-            }
-            if (mIsRtl) {
-                return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
-            }
-            return getScrollForPage(mTaskViewStartIndex);
-        }
-        return super.computeMinScroll();
-    }
-
-    @Override
-    protected int computeMaxScroll() {
-        if (getTaskViewCount() > 0) {
-            if (mDisallowScrollToClearAll) {
-                // We aren't showing the clear all button,
-                // so use the rightmost task as the min scroll.
-                if (mIsRtl) {
-                    return getScrollForPage(mTaskViewStartIndex);
-                }
-                return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
-            }
-            if (mIsRtl) {
-                return getScrollForPage(mTaskViewStartIndex);
-            }
-            return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
-        }
-        return super.computeMaxScroll();
-    }
-
-    public ClearAllButton getClearAllButton() {
-        return mClearAllButton;
-    }
-
-    @Override
-    protected boolean onOverscroll(int amount) {
-        // overscroll should only be accepted on -1 direction (for clear all button)
-        if ((amount > 0 && !mIsRtl) || (amount < 0 && mIsRtl)) return false;
-        return super.onOverscroll(amount);
-    }
-
-    /**
-     * @return How many pixels the running task is offset on the currently laid out dominant axis.
-     */
-    public int getScrollOffset() {
-        return getScrollOffset(getRunningTaskIndex());
-    }
-
-    /**
-     * @return How many pixels the page is offset on the currently laid out dominant axis.
-     */
-    public int getScrollOffset(int pageIndex) {
-        if (pageIndex == -1) {
-            return 0;
-        }
-        // Unbound the scroll (due to overscroll) if the adjacent tasks are offset away from it.
-        // This allows the page to move freely, given there's no visual indication why it shouldn't.
-        int boundedScroll = mOrientationHandler.getPrimaryScroll(this);
-        int unboundedScroll = getUnboundedScroll();
-        float unboundedProgress = mAdjacentPageOffset;
-        int scroll = Math.round(unboundedScroll * unboundedProgress
-                + boundedScroll * (1 - unboundedProgress));
-        return getScrollForPage(pageIndex) - scroll;
-    }
-
-    public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
-        float degreesRotated;
-        if (navbarRotation == 0) {
-            degreesRotated = mOrientationHandler.getDegreesRotated();
-        } else {
-            degreesRotated = -navbarRotation;
-        }
-        if (degreesRotated == 0) {
-            return super::onTouchEvent;
-        }
-
-        // At this point the event coordinates have already been transformed, so we need to
-        // undo that transformation since PagedView also accommodates for the transformation via
-        // PagedOrientationHandler
-        return e -> {
-            if (navbarRotation != 0
-                    && mOrientationState.isMultipleOrientationSupportedByDevice()
-                    && !mOrientationState.getOrientationHandler().isLayoutNaturalToLauncher()) {
-                mOrientationState.flipVertical(e);
-                super.onTouchEvent(e);
-                mOrientationState.flipVertical(e);
-                return;
-            }
-            mOrientationState.transformEvent(-degreesRotated, e, true);
-            super.onTouchEvent(e);
-            mOrientationState.transformEvent(-degreesRotated, e, false);
-        };
-    }
-
-    public TransformParams getLiveTileParams(
-            boolean mightNeedToRefill) {
-        return null;
-    }
-
-    private void updateEnabledOverlays() {
-        int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
-        int taskCount = getTaskViewCount();
-        for (int i = mTaskViewStartIndex; i < mTaskViewStartIndex + taskCount; i++) {
-            getTaskViewAtByAbsoluteIndex(i).setOverlayEnabled(i == overlayEnabledPage);
-        }
-    }
-
-    public void setOverlayEnabled(boolean overlayEnabled) {
-        if (mOverlayEnabled != overlayEnabled) {
-            mOverlayEnabled = overlayEnabled;
-            updateEnabledOverlays();
-        }
-    }
-
-    /** If it's in the live tile mode, switch the running task into screenshot mode. */
-    public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
-        TaskView taskView = getRunningTaskView();
-        if (taskView != null) {
-            taskView.setShowScreenshot(true);
-            if (thumbnailData != null) {
-                taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
-            } else {
-                taskView.getThumbnail().refresh();
-            }
-            ViewUtils.postDraw(taskView, onFinishRunnable);
-        } else {
-            onFinishRunnable.run();
-        }
-    }
-
-    /**
-     * 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.
-     */
-    private void setTaskModalness(float modalness) {
-        mTaskModalness = modalness;
-        updatePageOffsets();
-        if (getCurrentPageTaskView() != null) {
-            getCurrentPageTaskView().setModalness(modalness);
-        }
-        // Only show actions view when it's modal for in-place landscape mode.
-        boolean inPlaceLandscape = !mOrientationState.canRecentsActivityRotate()
-                && mOrientationState.getTouchRotation() != ROTATION_0;
-        mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
-    }
-
-    @Nullable
-    protected DepthController getDepthController() {
-        return null;
-    }
-
-    @Override
-    public void onSecondaryWindowBoundsChanged() {
-        // Invalidate the task view size
-        setInsets(mInsets);
-        requestLayout();
-    }
-
-    /**
-     * Enables or disables modal state for RecentsView
-     * @param isModalState
-     */
-    public void setModalStateEnabled(boolean isModalState) { }
-
-    public TaskOverlayFactory getTaskOverlayFactory() {
-        return mTaskOverlayFactory;
-    }
-
-    public BaseActivityInterface getSizeStrategy() {
-        return mSizeStrategy;
-    }
-
-    /**
-     * Used to register callbacks for when our empty message state changes.
-     *
-     * @see #setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener)
-     * @see #updateEmptyMessage()
-     */
-    public interface OnEmptyMessageUpdatedListener {
-        /** @param isEmpty Whether RecentsView is empty (i.e. has no children) */
-        void onEmptyMessageUpdated(boolean isEmpty);
-    }
-
-    private static class PinnedStackAnimationListener<T extends BaseActivity> extends
-            IPinnedStackAnimationListener.Stub {
-        private T mActivity;
-
-        public void setActivity(T activity) {
-            mActivity = activity;
-        }
-
-        @Override
-        public void onPinnedStackAnimationStarted() {
-            // Needed for activities that auto-enter PiP, which will not trigger a remote
-            // animation to be created
-            mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
deleted file mode 100644
index a8f2a1c..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ /dev/null
@@ -1,1221 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.views;
-
-import static android.view.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.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.Utilities.comp;
-import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.AttributeSet;
-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.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.Toast;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.testing.TestLogging;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.TransformingTouchDelegate;
-import com.android.launcher3.util.ViewPool.Reusable;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.TaskIconCache;
-import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskThumbnailCache;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.util.RecentsOrientedState;
-import com.android.quickstep.util.TaskCornerRadius;
-import com.android.quickstep.views.RecentsView.PageCallbacks;
-import com.android.quickstep.views.RecentsView.ScrollState;
-import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * A task in the Recents view.
- */
-public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
-
-    private static final String TAG = TaskView.class.getSimpleName();
-
-    /** A curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */
-    private static final TimeInterpolator CURVE_INTERPOLATOR
-            = x -> (float) -Math.cos(x * Math.PI) / 2f + .5f;
-
-    /**
-     * The alpha of a black scrim on a page in the carousel as it leaves the screen.
-     * In the resting position of the carousel, the adjacent pages have about half this scrim.
-     */
-    public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
-
-    /**
-     * How much to scale down pages near the edge of the screen.
-     */
-    public static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
-
-    public static final long SCALE_ICON_DURATION = 120;
-    private static final long DIM_ANIM_DURATION = 700;
-    /**
-     * This technically can be a vanilla {@link TouchDelegate} class, however that class requires
-     * setting the touch bounds at construction, so we'd repeatedly be created many instances
-     * unnecessarily as scrolling occurs, whereas {@link TransformingTouchDelegate} allows touch
-     * delegated bounds only to be updated.
-     */
-    private TransformingTouchDelegate mIconTouchDelegate;
-    private TransformingTouchDelegate mChipTouchDelegate;
-
-    private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
-            Collections.singletonList(new Rect());
-
-    private static final FloatProperty<TaskView> FOCUS_TRANSITION =
-            new FloatProperty<TaskView>("focusTransition") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setIconAndDimTransitionProgress(v, false /* invert */);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mFocusTransitionProgress;
-                }
-            };
-
-    private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_X =
-            new FloatProperty<TaskView>("fillDismissGapTranslationX") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setFillDismissGapTranslationX(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mFillDismissGapTranslationX;
-                }
-            };
-
-    private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_Y =
-            new FloatProperty<TaskView>("fillDismissGapTranslationY") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setFillDismissGapTranslationY(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mFillDismissGapTranslationY;
-                }
-            };
-
-    private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_X =
-            new FloatProperty<TaskView>("taskOffsetTranslationX") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setTaskOffsetTranslationX(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mTaskOffsetTranslationX;
-                }
-            };
-
-    private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_Y =
-            new FloatProperty<TaskView>("taskOffsetTranslationY") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setTaskOffsetTranslationY(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mTaskOffsetTranslationY;
-                }
-            };
-
-    private final OnAttachStateChangeListener mTaskMenuStateListener =
-            new OnAttachStateChangeListener() {
-                @Override
-                public void onViewAttachedToWindow(View view) {
-                }
-
-                @Override
-                public void onViewDetachedFromWindow(View view) {
-                    if (mMenuView != null) {
-                        mMenuView.removeOnAttachStateChangeListener(this);
-                        mMenuView = null;
-                    }
-                }
-            };
-
-    private final TaskOutlineProvider mOutlineProvider;
-
-    private Task mTask;
-    private TaskThumbnailView mSnapshotView;
-    private TaskMenuView mMenuView;
-    private IconView mIconView;
-    private final DigitalWellBeingToast mDigitalWellBeingToast;
-    private float mCurveScale;
-    private float mFullscreenProgress;
-    private final FullscreenDrawParams mCurrentFullscreenParams;
-    private final BaseDraggingActivity mActivity;
-
-    // Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
-    // TODO: We should do this for secondary translation properties as well.
-    private float mFillDismissGapTranslationX;
-    private float mFillDismissGapTranslationY;
-    private float mTaskOffsetTranslationX;
-    private float mTaskOffsetTranslationY;
-
-    private ObjectAnimator mIconAndDimAnimator;
-    private float mIconScaleAnimStartProgress = 0;
-    private float mFocusTransitionProgress = 1;
-    private float mModalness = 0;
-    private float mStableAlpha = 1;
-
-    private boolean mShowScreenshot;
-
-    // The current background requests to load the task thumbnail and icon
-    private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
-    private TaskIconCache.IconLoadRequest mIconLoadRequest;
-
-    // Order in which the footers appear. Lower order appear below higher order.
-    public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
-    private final FooterWrapper[] mFooters = new FooterWrapper[2];
-    private float mFooterVerticalOffset = 0;
-    private float mFooterAlpha = 1;
-    private int mStackHeight;
-    private View mContextualChipWrapper;
-    private View mContextualChip;
-    private final float[] mIconCenterCoords = new float[2];
-    private final float[] mChipCenterCoords = new float[2];
-
-    public TaskView(Context context) {
-        this(context, null);
-    }
-
-    public TaskView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mActivity = BaseDraggingActivity.fromContext(context);
-        setOnClickListener((view) -> {
-            if (getTask() == null) {
-                return;
-            }
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-                if (isRunningTask()) {
-                    createLaunchAnimationForRunningTask().start();
-                } else {
-                    launchTask(true /* animate */);
-                }
-            } else {
-                launchTask(true /* animate */);
-            }
-
-            mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                    Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
-                    TaskUtils.getLaunchComponentKeyForTask(getTask().key));
-            mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
-                    .log(LAUNCHER_TASK_LAUNCH_TAP);
-        });
-
-        mCurrentFullscreenParams = new FullscreenDrawParams(context);
-        mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
-
-        mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams);
-        setOutlineProvider(mOutlineProvider);
-    }
-
-    /**
-     * Builds proto for logging
-     */
-    public WorkspaceItemInfo getItemInfo() {
-        ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(getTask().key);
-        WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
-        dummyInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
-        dummyInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
-        dummyInfo.user = componentKey.user;
-        dummyInfo.intent = new Intent().setComponent(componentKey.componentName);
-        dummyInfo.title = TaskUtils.getTitle(getContext(), getTask());
-        dummyInfo.screenId = getRecentsView().indexOfChild(this);
-        return dummyInfo;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mSnapshotView = findViewById(R.id.snapshot);
-        mIconView = findViewById(R.id.icon);
-        mIconTouchDelegate = new TransformingTouchDelegate(mIconView);
-    }
-
-    /**
-     * Whether the taskview should take the touch event from parent. Events passed to children
-     * that might require special handling.
-     */
-    public boolean offerTouchToChildren(MotionEvent event) {
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            computeAndSetIconTouchDelegate();
-            computeAndSetChipTouchDelegate();
-        }
-        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,
-                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));
-        }
-    }
-
-    /**
-     * The modalness of this view is how it should be displayed when it is shown on its own in the
-     * modal state of overview.
-     *
-     * @param modalness [0, 1] 0 being in context with other tasks, 1 being shown on its own.
-     */
-    public void setModalness(float modalness) {
-        if (mModalness == modalness) {
-            return;
-        }
-        mModalness = modalness;
-        mIconView.setAlpha(comp(modalness));
-        if (mContextualChip != null) {
-            mContextualChip.setScaleX(comp(modalness));
-            mContextualChip.setScaleY(comp(modalness));
-        }
-        if (mContextualChipWrapper != null) {
-            mContextualChipWrapper.setAlpha(comp(modalness));
-        }
-        updateFooterVerticalOffset(mFooterVerticalOffset);
-    }
-
-    public TaskMenuView getMenuView() {
-        return mMenuView;
-    }
-
-    public DigitalWellBeingToast getDigitalWellBeingToast() {
-        return mDigitalWellBeingToast;
-    }
-
-    /**
-     * Updates this task view to the given {@param task}.
-     *
-     * TODO(b/142282126) Re-evaluate if we need to pass in isMultiWindowMode after
-     *   that issue is fixed
-     */
-    public void bind(Task task, RecentsOrientedState orientedState) {
-        cancelPendingLoadTasks();
-        mTask = task;
-        mSnapshotView.bind(task);
-        setOrientationState(orientedState);
-    }
-
-    public Task getTask() {
-        return mTask;
-    }
-
-    public TaskThumbnailView getThumbnail() {
-        return mSnapshotView;
-    }
-
-    public IconView getIconView() {
-        return mIconView;
-    }
-
-    public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
-        final PendingAnimation pendingAnimation = getRecentsView().createTaskLaunchAnimation(
-                this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
-        AnimatorPlaybackController currentAnimation = pendingAnimation.createPlaybackController();
-        currentAnimation.setEndAction(() -> {
-            pendingAnimation.finish(true, Touch.SWIPE);
-            launchTask(false);
-        });
-        return currentAnimation;
-    }
-
-    public void launchTask(boolean animate) {
-        launchTask(animate, false /* freezeTaskList */);
-    }
-
-    public void launchTask(boolean animate, boolean freezeTaskList) {
-        launchTask(animate, freezeTaskList, (result) -> {
-            if (!result) {
-                notifyTaskLaunchFailed(TAG);
-            }
-        }, getHandler());
-    }
-
-    public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
-            Handler resultCallbackHandler) {
-        launchTask(animate, false /* freezeTaskList */, resultCallback, resultCallbackHandler);
-    }
-
-    public void launchTask(boolean animate, boolean freezeTaskList, Consumer<Boolean> resultCallback,
-            Handler resultCallbackHandler) {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            RecentsView recentsView = getRecentsView();
-            if (isRunningTask()) {
-                recentsView.finishRecentsAnimation(false /* toRecents */,
-                        () -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
-            } else {
-                // This is a workaround against the WM issue that app open is not correctly animated
-                // when recents animation is being cleaned up (b/143774568). When that's possible,
-                // we should rely on the framework side to cancel the recents animation, and we will
-                // clean up the screenshot on the launcher side while we launch the next task.
-                recentsView.switchToScreenshot(null,
-                        () -> recentsView.finishRecentsAnimation(true /* toRecents */,
-                                () -> launchTaskInternal(animate, freezeTaskList, resultCallback,
-                                        resultCallbackHandler)));
-            }
-        } else {
-            launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
-        }
-    }
-
-    private void launchTaskInternal(boolean animate, boolean freezeTaskList,
-            Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
-        if (mTask != null) {
-            final ActivityOptions opts;
-            TestLogging.recordEvent(
-                    TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
-            if (animate) {
-                opts = mActivity.getActivityLaunchOptions(this);
-                if (freezeTaskList) {
-                    ActivityOptionsCompat.setFreezeRecentTasksList(opts);
-                }
-                ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
-                        opts, resultCallback, resultCallbackHandler);
-            } else {
-                opts = ActivityOptionsCompat.makeCustomAnimation(getContext(), 0, 0, () -> {
-                    if (resultCallback != null) {
-                        // Only post the animation start after the system has indicated that the
-                        // transition has started
-                        resultCallbackHandler.post(() -> resultCallback.accept(true));
-                    }
-                }, resultCallbackHandler);
-                if (freezeTaskList) {
-                    ActivityOptionsCompat.setFreezeRecentTasksList(opts);
-                }
-                ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
-                        opts, (success) -> {
-                            if (resultCallback != null && !success) {
-                                // If the call to start activity failed, then post the result
-                                // immediately, otherwise, wait for the animation start callback
-                                // from the activity options above
-                                resultCallbackHandler.post(() -> resultCallback.accept(false));
-                            }
-                        }, resultCallbackHandler);
-            }
-            getRecentsView().onTaskLaunched(mTask);
-        }
-    }
-
-    public void onTaskListVisibilityChanged(boolean visible) {
-        if (mTask == null) {
-            return;
-        }
-        cancelPendingLoadTasks();
-        if (visible) {
-            // These calls are no-ops if the data is already loaded, try and load the high
-            // resolution thumbnail if the state permits
-            RecentsModel model = RecentsModel.INSTANCE.get(getContext());
-            TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
-            TaskIconCache iconCache = model.getIconCache();
-            mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
-                    mTask, thumbnail -> mSnapshotView.setThumbnail(mTask, thumbnail));
-            mIconLoadRequest = iconCache.updateIconInBackground(mTask,
-                    (task) -> {
-                        setIcon(task.icon);
-                        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
-                            getRecentsView().updateLiveTileIcon(task.icon);
-                        }
-                        mDigitalWellBeingToast.initialize(mTask);
-                    });
-        } else {
-            mSnapshotView.setThumbnail(null, null);
-            setIcon(null);
-            // Reset the task thumbnail reference as well (it will be fetched from the cache or
-            // reloaded next time we need it)
-            mTask.thumbnail = null;
-        }
-    }
-
-    private void cancelPendingLoadTasks() {
-        if (mThumbnailLoadRequest != null) {
-            mThumbnailLoadRequest.cancel();
-            mThumbnailLoadRequest = null;
-        }
-        if (mIconLoadRequest != null) {
-            mIconLoadRequest.cancel();
-            mIconLoadRequest = null;
-        }
-    }
-
-    private boolean showTaskMenu(int action) {
-        if (!getRecentsView().isClearAllHidden()) {
-            getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
-        } else {
-            mMenuView = TaskMenuView.showForTask(this);
-            mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
-                    .log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
-            UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
-                    LauncherLogProto.ItemType.TASK_ICON);
-            if (mMenuView != null) {
-                mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
-            }
-        }
-        return mMenuView != null;
-    }
-
-    private void setIcon(Drawable icon) {
-        if (icon != null) {
-            mIconView.setDrawable(icon);
-            mIconView.setOnClickListener(v -> showTaskMenu(Touch.TAP));
-            mIconView.setOnLongClickListener(v -> {
-                requestDisallowInterceptTouchEvent(true);
-                return showTaskMenu(Touch.LONGPRESS);
-            });
-        } else {
-            mIconView.setDrawable(null);
-            mIconView.setOnClickListener(null);
-            mIconView.setOnLongClickListener(null);
-        }
-    }
-
-    public void setOrientationState(RecentsOrientedState orientationState) {
-        PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
-        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
-        LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
-        int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
-        LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
-        switch (orientationHandler.getRotation()) {
-            case ROTATION_90:
-                iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
-                iconParams.rightMargin = -thumbnailPadding;
-                iconParams.leftMargin = 0;
-                iconParams.topMargin = snapshotParams.topMargin / 2;
-                break;
-            case ROTATION_180:
-                iconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
-                iconParams.bottomMargin = -thumbnailPadding;
-                iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0;
-                break;
-            case ROTATION_270:
-                iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
-                iconParams.leftMargin = -thumbnailPadding;
-                iconParams.rightMargin = 0;
-                iconParams.topMargin = snapshotParams.topMargin / 2;
-                break;
-            case Surface.ROTATION_0:
-            default:
-                iconParams.gravity = TOP | CENTER_HORIZONTAL;
-                iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0;
-                break;
-        }
-        mIconView.setLayoutParams(iconParams);
-        mIconView.setRotation(orientationHandler.getDegreesRotated());
-
-        if (mMenuView != null) {
-            mMenuView.onRotationChanged();
-        }
-    }
-
-    private void setIconAndDimTransitionProgress(float progress, boolean invert) {
-        if (invert) {
-            progress = 1 - progress;
-        }
-        mFocusTransitionProgress = progress;
-        mSnapshotView.setDimAlphaMultipler(progress);
-        float iconScalePercentage = (float) SCALE_ICON_DURATION / DIM_ANIM_DURATION;
-        float lowerClamp = invert ? 1f - iconScalePercentage : 0;
-        float upperClamp = invert ? 1 : iconScalePercentage;
-        float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
-                .getInterpolation(progress);
-        mIconView.setScaleX(scale);
-        mIconView.setScaleY(scale);
-        if (mContextualChip != null && mContextualChipWrapper != null) {
-            mContextualChipWrapper.setAlpha(scale);
-            mContextualChip.setScaleX(scale);
-            mContextualChip.setScaleY(scale);
-        }
-        updateFooterVerticalOffset(1.0f - scale);
-    }
-
-    public void setIconScaleAnimStartProgress(float startProgress) {
-        mIconScaleAnimStartProgress = startProgress;
-    }
-
-    public void animateIconScaleAndDimIntoView() {
-        if (mIconAndDimAnimator != null) {
-            mIconAndDimAnimator.cancel();
-        }
-        mIconAndDimAnimator = ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1);
-        mIconAndDimAnimator.setCurrentFraction(mIconScaleAnimStartProgress);
-        mIconAndDimAnimator.setDuration(DIM_ANIM_DURATION).setInterpolator(LINEAR);
-        mIconAndDimAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mIconAndDimAnimator = null;
-            }
-        });
-        mIconAndDimAnimator.start();
-    }
-
-    protected void setIconScaleAndDim(float iconScale) {
-        setIconScaleAndDim(iconScale, false);
-    }
-
-    private void setIconScaleAndDim(float iconScale, boolean invert) {
-        if (mIconAndDimAnimator != null) {
-            mIconAndDimAnimator.cancel();
-        }
-        setIconAndDimTransitionProgress(iconScale, invert);
-    }
-
-    protected void resetViewTransforms() {
-        setCurveScale(1);
-        mFillDismissGapTranslationX = mTaskOffsetTranslationX = 0f;
-        mFillDismissGapTranslationY = mTaskOffsetTranslationY = 0f;
-        setTranslationX(0f);
-        setTranslationY(0f);
-        setTranslationZ(0);
-        setAlpha(mStableAlpha);
-        setIconScaleAndDim(1);
-    }
-
-    public void setStableAlpha(float parentAlpha) {
-        mStableAlpha = parentAlpha;
-        setAlpha(mStableAlpha);
-    }
-
-    @Override
-    public void onRecycle() {
-        resetViewTransforms();
-        // 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);
-        setOverlayEnabled(false);
-        onTaskListVisibilityChanged(false);
-    }
-
-    @Override
-    public void onPageScroll(ScrollState scrollState) {
-        // Don't do anything if it's modal.
-        if (mModalness > 0) {
-            return;
-        }
-
-        float curveInterpolation =
-                CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
-        float curveScaleForCurveInterpolation = getCurveScaleForCurveInterpolation(
-                curveInterpolation);
-        mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
-        setCurveScale(curveScaleForCurveInterpolation);
-
-        mFooterAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
-        for (FooterWrapper footer : mFooters) {
-            if (footer != null) {
-                footer.mView.setAlpha(mFooterAlpha);
-            }
-        }
-
-        if (mMenuView != null) {
-            PagedOrientationHandler pagedOrientationHandler = getPagedOrientationHandler();
-            RecentsView recentsView = getRecentsView();
-            mMenuView.setPosition(getX() - recentsView.getScrollX(),
-                    getY() - recentsView.getScrollY(), pagedOrientationHandler);
-            mMenuView.setScaleX(getScaleX());
-            mMenuView.setScaleY(getScaleY());
-        }
-    }
-
-    /**
-     * Sets the footer at the specific index and returns the previously set footer.
-     */
-    public View setFooter(int index, View view) {
-        View oldFooter = null;
-
-        // If the footer are is already collapsed, do not animate entry
-        boolean shouldAnimateEntry = mFooterVerticalOffset <= 0;
-
-        if (mFooters[index] != null) {
-            oldFooter = mFooters[index].mView;
-            mFooters[index].release();
-            removeView(oldFooter);
-
-            // If we are replacing an existing footer, do not animate entry
-            shouldAnimateEntry = false;
-        }
-        if (view != null) {
-            int indexToAdd = getChildCount();
-            for (int i = index - 1; i >= 0; i--) {
-                if (mFooters[i] != null) {
-                    indexToAdd = indexOfChild(mFooters[i].mView);
-                    break;
-                }
-            }
-
-            addView(view, indexToAdd);
-            LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
-            layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
-            layoutParams.bottomMargin =
-                    ((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin;
-            view.setAlpha(mFooterAlpha);
-            mFooters[index] = new FooterWrapper(view);
-            if (shouldAnimateEntry) {
-                mFooters[index].animateEntry();
-            }
-        } else {
-            mFooters[index] = null;
-        }
-
-        mStackHeight = 0;
-        for (FooterWrapper footer : mFooters) {
-            if (footer != null) {
-                footer.setVerticalShift(mStackHeight);
-                mStackHeight += footer.mExpectedHeight;
-            }
-        }
-
-        return oldFooter;
-    }
-
-    /**
-     * 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(LayoutParams.MATCH_PARENT,
-                    LayoutParams.WRAP_CONTENT);
-            layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
-            int expectedChipHeight = getExpectedViewHeight(view);
-            float chipOffset = getResources().getDimension(R.dimen.chip_hint_vertical_offset);
-            layoutParams.bottomMargin = (int)
-                    (((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin
-                            - expectedChipHeight + chipOffset);
-            mContextualChip = ((FrameLayout) mContextualChipWrapper).getChildAt(0);
-            mContextualChip.setScaleX(0f);
-            mContextualChip.setScaleY(0f);
-            GradientDrawable scrimDrawable = (GradientDrawable) getResources().getDrawable(
-                    R.drawable.chip_scrim_gradient, mActivity.getTheme());
-            float cornerRadius = getTaskCornerRadius();
-            scrimDrawable.setCornerRadii(
-                    new float[]{0, 0, 0, 0, cornerRadius, cornerRadius, cornerRadius,
-                            cornerRadius});
-            InsetDrawable scrimDrawableInset = new InsetDrawable(scrimDrawable, 0, 0, 0,
-                    (int) (expectedChipHeight - chipOffset));
-            mContextualChipWrapper.setBackground(scrimDrawableInset);
-            mContextualChipWrapper.setPadding(0, 0, 0, 0);
-            mContextualChipWrapper.setAlpha(0f);
-            addView(view, getChildCount(), layoutParams);
-            if (mContextualChip != null) {
-                mContextualChip.animate().scaleX(1f).scaleY(1f).setDuration(50);
-            }
-            if (mContextualChipWrapper != null) {
-                mChipTouchDelegate = new TransformingTouchDelegate(mContextualChipWrapper);
-                mContextualChipWrapper.animate().alpha(1f).setDuration(50);
-            }
-        }
-    }
-
-    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;
-        mContextualChip = 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);
-        setPivotX((right - left) * 0.5f);
-        setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
-        if (Utilities.ATLEAST_Q) {
-            SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
-            setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
-        }
-
-        mStackHeight = 0;
-        for (FooterWrapper footer : mFooters) {
-            if (footer != null) {
-                mStackHeight += footer.mView.getHeight();
-            }
-        }
-        updateFooterVerticalOffset(0);
-    }
-
-    private void updateFooterVerticalOffset(float offset) {
-        mFooterVerticalOffset = offset;
-
-        for (FooterWrapper footer : mFooters) {
-            if (footer != null) {
-                footer.updateFooterOffset();
-            }
-        }
-    }
-
-    public static float getCurveScaleForInterpolation(float linearInterpolation) {
-        float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(linearInterpolation);
-        return getCurveScaleForCurveInterpolation(curveInterpolation);
-    }
-
-    private static float getCurveScaleForCurveInterpolation(float curveInterpolation) {
-        return 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
-    }
-
-    private void setCurveScale(float curveScale) {
-        mCurveScale = curveScale;
-        setScaleX(mCurveScale);
-        setScaleY(mCurveScale);
-    }
-
-    public float getCurveScale() {
-        return mCurveScale;
-    }
-
-    private void setFillDismissGapTranslationX(float x) {
-        mFillDismissGapTranslationX = x;
-        applyTranslationX();
-    }
-
-    private void setFillDismissGapTranslationY(float y) {
-        mFillDismissGapTranslationY = y;
-        applyTranslationY();
-    }
-
-    private void setTaskOffsetTranslationX(float x) {
-        mTaskOffsetTranslationX = x;
-        applyTranslationX();
-    }
-
-    private void setTaskOffsetTranslationY(float y) {
-        mTaskOffsetTranslationY = y;
-        applyTranslationY();
-    }
-
-    private void applyTranslationX() {
-        setTranslationX(mFillDismissGapTranslationX + mTaskOffsetTranslationX);
-    }
-
-    private void applyTranslationY() {
-        setTranslationY(mFillDismissGapTranslationY + mTaskOffsetTranslationY);
-    }
-
-    public FloatProperty<TaskView> getPrimaryFillDismissGapTranslationProperty() {
-        return getPagedOrientationHandler().getPrimaryValue(
-                FILL_DISMISS_GAP_TRANSLATION_X, FILL_DISMISS_GAP_TRANSLATION_Y);
-    }
-
-    public FloatProperty<TaskView> getPrimaryTaskOffsetTranslationProperty() {
-        return getPagedOrientationHandler().getPrimaryValue(
-                TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
-        return false;
-    }
-
-    private static final class TaskOutlineProvider extends ViewOutlineProvider {
-
-        private final int mMarginTop;
-        private FullscreenDrawParams mFullscreenParams;
-
-        TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams) {
-            mMarginTop = context.getResources().getDimensionPixelSize(
-                    R.dimen.task_thumbnail_top_margin);
-            mFullscreenParams = fullscreenParams;
-        }
-
-        public void setFullscreenParams(FullscreenDrawParams params) {
-            mFullscreenParams = params;
-        }
-
-        @Override
-        public void getOutline(View view, Outline outline) {
-            RectF insets = mFullscreenParams.mCurrentDrawnInsets;
-            float scale = mFullscreenParams.mScale;
-            outline.setRoundRect(0,
-                    (int) (mMarginTop * scale),
-                    (int) ((insets.left + view.getWidth() + insets.right) * scale),
-                    (int) ((insets.top + view.getHeight() + insets.bottom) * scale),
-                    mFullscreenParams.mCurrentDrawnCornerRadius);
-        }
-    }
-
-    private class FooterWrapper extends ViewOutlineProvider {
-
-        final View mView;
-        final ViewOutlineProvider mOldOutlineProvider;
-        final ViewOutlineProvider mDelegate;
-
-        final int mExpectedHeight;
-        final int mOldPaddingBottom;
-
-        int mAnimationOffset = 0;
-        int mEntryAnimationOffset = 0;
-
-        public FooterWrapper(View view) {
-            mView = view;
-            mOldOutlineProvider = view.getOutlineProvider();
-            mDelegate = mOldOutlineProvider == null
-                    ? ViewOutlineProvider.BACKGROUND : mOldOutlineProvider;
-
-            mExpectedHeight = getExpectedViewHeight(view);
-            mOldPaddingBottom = view.getPaddingBottom();
-
-            if (mOldOutlineProvider != null) {
-                view.setOutlineProvider(this);
-                view.setClipToOutline(true);
-            }
-        }
-
-        public void setVerticalShift(int shift) {
-            mView.setPadding(mView.getPaddingLeft(), mView.getPaddingTop(),
-                    mView.getPaddingRight(), mOldPaddingBottom + shift);
-        }
-
-        @Override
-        public void getOutline(View view, Outline outline) {
-            mDelegate.getOutline(view, outline);
-            outline.offset(0, -mAnimationOffset - mEntryAnimationOffset);
-        }
-
-        void updateFooterOffset() {
-            float offset = Utilities.or(mFooterVerticalOffset, mModalness);
-            mAnimationOffset = Math.round(mStackHeight * offset);
-            mView.setTranslationY(mAnimationOffset + mEntryAnimationOffset
-                    + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom
-                    + mCurrentFullscreenParams.mCurrentDrawnInsets.top);
-            mView.invalidateOutline();
-        }
-
-        void release() {
-            mView.setOutlineProvider(mOldOutlineProvider);
-            setVerticalShift(0);
-        }
-
-        void animateEntry() {
-            ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
-            animator.addUpdateListener(anim -> {
-               float factor = 1 - anim.getAnimatedFraction();
-               int totalShift = mExpectedHeight + mView.getPaddingBottom() - mOldPaddingBottom;
-                mEntryAnimationOffset = Math.round(factor * totalShift);
-                updateFooterOffset();
-            });
-            animator.setDuration(100);
-            animator.start();
-        }
-    }
-
-    private int getExpectedViewHeight(View view) {
-        int expectedHeight;
-        int h = view.getLayoutParams().height;
-        if (h > 0) {
-            expectedHeight = h;
-        } else {
-            int m = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY - 1, MeasureSpec.AT_MOST);
-            view.measure(m, m);
-            expectedHeight = view.getMeasuredHeight();
-        }
-        return expectedHeight;
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-
-        info.addAction(
-                new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close,
-                        getContext().getText(R.string.accessibility_close)));
-
-        final Context context = getContext();
-        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
-            info.addAction(s.createAccessibilityAction(context));
-        }
-
-        if (mDigitalWellBeingToast.hasLimit()) {
-            info.addAction(
-                    new AccessibilityNodeInfo.AccessibilityAction(
-                            R.string.accessibility_app_usage_settings,
-                            getContext().getText(R.string.accessibility_app_usage_settings)));
-        }
-
-        final RecentsView recentsView = getRecentsView();
-        final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
-                AccessibilityNodeInfo.CollectionItemInfo.obtain(
-                        0, 1, recentsView.getTaskViewCount() - recentsView.indexOfChild(this) - 1,
-                        1, false);
-        info.setCollectionItemInfo(itemInfo);
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (action == R.string.accessibility_close) {
-            getRecentsView().dismissTask(this, true /*animateTaskView*/,
-                    true /*removeTask*/);
-            return true;
-        }
-
-        if (action == R.string.accessibility_app_usage_settings) {
-            mDigitalWellBeingToast.openAppUsageSettings(this);
-            return true;
-        }
-
-        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
-            if (s.hasHandlerForAction(action)) {
-                s.onClick(this);
-                return true;
-            }
-        }
-
-        return super.performAccessibilityAction(action, arguments);
-    }
-
-    public RecentsView getRecentsView() {
-        return (RecentsView) getParent();
-    }
-
-    PagedOrientationHandler getPagedOrientationHandler() {
-        return getRecentsView().mOrientationState.getOrientationHandler();
-    }
-
-    public void notifyTaskLaunchFailed(String tag) {
-        String msg = "Failed to launch task";
-        if (mTask != null) {
-            msg += " (task=" + mTask.key.baseIntent + " userId=" + mTask.key.userId + ")";
-        }
-        Log.w(tag, msg);
-        Toast.makeText(getContext(), R.string.activity_not_available, LENGTH_SHORT).show();
-    }
-
-    /**
-     * Hides the icon and shows insets when this TaskView is about to be shown fullscreen.
-     *
-     * @param progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
-     */
-    public void setFullscreenProgress(float progress) {
-        progress = Utilities.boundToRange(progress, 0, 1);
-        mFullscreenProgress = progress;
-        boolean isFullscreen = mFullscreenProgress > 0;
-        mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
-        setClipChildren(!isFullscreen);
-        setClipToPadding(!isFullscreen);
-
-        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.setFullscreenParams(mCurrentFullscreenParams);
-        invalidateOutline();
-    }
-
-    void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
-        if (getRecentsView() == null) {
-            return;
-        }
-        mCurrentFullscreenParams.setProgress(
-                mFullscreenProgress,
-                getRecentsView().getScaleX(),
-                getWidth(), mActivity.getDeviceProfile(),
-                previewPositionHelper);
-    }
-
-    public boolean isRunningTask() {
-        if (getRecentsView() == null) {
-            return false;
-        }
-        return this == getRecentsView().getRunningTaskView();
-    }
-
-    public void setShowScreenshot(boolean showScreenshot) {
-        mShowScreenshot = showScreenshot;
-    }
-
-    public boolean showScreenshot() {
-        if (!isRunningTask()) {
-            return true;
-        }
-        return mShowScreenshot;
-    }
-
-    public void setOverlayEnabled(boolean overlayEnabled) {
-        mSnapshotView.setOverlayEnabled(overlayEnabled);
-    }
-
-    /**
-     * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
-     */
-    public static class FullscreenDrawParams {
-
-        private final float mCornerRadius;
-        private final float mWindowCornerRadius;
-
-        public RectF mCurrentDrawnInsets = new RectF();
-        public float mCurrentDrawnCornerRadius;
-        /** The current scale we apply to the thumbnail to adjust for new left/right insets. */
-        public float mScale = 1;
-
-        public FullscreenDrawParams(Context context) {
-            mCornerRadius = TaskCornerRadius.get(context);
-            mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
-
-            mCurrentDrawnCornerRadius = mCornerRadius;
-        }
-
-        /**
-         * Sets the progress in range [0, 1]
-         */
-        public void setProgress(float fullscreenProgress, float parentScale, int previewWidth,
-                DeviceProfile dp, PreviewPositionHelper pph) {
-            RectF insets = pph.getInsetsToDrawInFullscreen();
-
-            float currentInsetsLeft = insets.left * fullscreenProgress;
-            float currentInsetsRight = insets.right * fullscreenProgress;
-            mCurrentDrawnInsets.set(currentInsetsLeft, insets.top * fullscreenProgress,
-                    currentInsetsRight, insets.bottom * fullscreenProgress);
-            float fullscreenCornerRadius = dp.isMultiWindowMode ? 0 : mWindowCornerRadius;
-
-            mCurrentDrawnCornerRadius =
-                    Utilities.mapRange(fullscreenProgress, mCornerRadius, fullscreenCornerRadius)
-                            / parentScale;
-
-            // 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.
-            if (previewWidth > 0) {
-                mScale = previewWidth / (previewWidth + currentInsetsLeft + currentInsetsRight);
-            }
-        }
-
-    }
-}
diff --git a/quickstep/recents_ui_overrides/res/drawable/all_apps_edu_circle.xml b/quickstep/res/drawable/all_apps_edu_circle.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/drawable/all_apps_edu_circle.xml
rename to quickstep/res/drawable/all_apps_edu_circle.xml
diff --git a/quickstep/res/drawable/bg_sandbox_close_button.xml b/quickstep/res/drawable/bg_sandbox_close_button.xml
new file mode 100644
index 0000000..2811071
--- /dev/null
+++ b/quickstep/res/drawable/bg_sandbox_close_button.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="#303030"/>
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/bg_sandbox_feedback.xml b/quickstep/res/drawable/bg_sandbox_feedback.xml
new file mode 100644
index 0000000..83a3dea
--- /dev/null
+++ b/quickstep/res/drawable/bg_sandbox_feedback.xml
@@ -0,0 +1,20 @@
+<!--
+    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">
+  <corners android:radius="28dp"/>
+  <solid android:color="?android:attr/colorBackgroundFloating"/>
+</shape>
diff --git a/quickstep/res/drawable/bg_wellbeing_toast.xml b/quickstep/res/drawable/bg_wellbeing_toast.xml
index 65730f6..4dc981f 100644
--- a/quickstep/res/drawable/bg_wellbeing_toast.xml
+++ b/quickstep/res/drawable/bg_wellbeing_toast.xml
@@ -13,7 +13,9 @@
      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="#E61A73E8" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:priv-android="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <solid android:color="?priv-android:attr/colorAccentPrimary" />
     <corners android:radius="?android:attr/dialogCornerRadius" />
 </shape>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml b/quickstep/res/drawable/chip_hint_background_light.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml
rename to quickstep/res/drawable/chip_hint_background_light.xml
diff --git a/quickstep/res/drawable/close_icon.xml b/quickstep/res/drawable/close_icon.xml
new file mode 100644
index 0000000..07f4336
--- /dev/null
+++ b/quickstep/res/drawable/close_icon.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="#909090"
+      android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/go/res/values/dimens.xml b/quickstep/res/drawable/default_sandbox_app_icon.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to quickstep/res/drawable/default_sandbox_app_icon.xml
index f1b1053..e9c9701 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/drawable/default_sandbox_app_icon.xml
@@ -1,20 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
+<!--
+     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.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+  <solid android:color="@color/gesture_tutorial_fake_task_view_color" />
+</shape>
diff --git a/go/res/values/dimens.xml b/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
similarity index 70%
copy from go/res/values/dimens.xml
copy to quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
index f1b1053..9c95497 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
@@ -1,20 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
+<!--
+     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.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<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>
diff --git a/quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml b/quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml
new file mode 100644
index 0000000..0d85c9a
--- /dev/null
+++ b/quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml
@@ -0,0 +1,247 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="412dp"
+    android:height="892dp"
+    android:viewportWidth="412"
+    android:viewportHeight="892">
+  <path
+      android:pathData="M0,101h412v791h-412z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M28,222L282,222A4,4 0,0 1,286 226L286,254A4,4 0,0 1,282 258L28,258A4,4 0,0 1,24 254L24,226A4,4 0,0 1,28 222z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,266L344,266A4,4 0,0 1,348 270L348,298A4,4 0,0 1,344 302L28,302A4,4 0,0 1,24 298L24,270A4,4 0,0 1,28 266z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,310L257,310A4,4 0,0 1,261 314L261,342A4,4 0,0 1,257 346L28,346A4,4 0,0 1,24 342L24,314A4,4 0,0 1,28 310z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,354L67,354A4,4 0,0 1,71 358L71,362A4,4 0,0 1,67 366L28,366A4,4 0,0 1,24 362L24,358A4,4 0,0 1,28 354z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M86,354L125,354A4,4 0,0 1,129 358L129,362A4,4 0,0 1,125 366L86,366A4,4 0,0 1,82 362L82,358A4,4 0,0 1,86 354z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,390L384,390A4,4 0,0 1,388 394L388,626A4,4 0,0 1,384 630L28,630A4,4 0,0 1,24 626L24,394A4,4 0,0 1,28 390z"
+      android:fillColor="#5F6368"/>
+  <path
+      android:pathData="M101.322,469.362L71.101,577.73H131.543L101.322,469.362Z"
+      android:strokeWidth="2"
+      android:fillColor="#5F6368"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M101.322,522.516V543.644V611.743"
+      android:fillColor="#5F6368"/>
+  <path
+      android:pathData="M101.322,522.516V543.644V611.743"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M109.577,532.822L101.322,548.135"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M109.577,558.221L101.322,574.196"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M94.54,524.945L101.322,534.442"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M90.929,549.239L101.322,561.166"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M62.772,412.307L30.855,563.669H94.688L62.772,412.307Z"
+      android:strokeWidth="2"
+      android:fillColor="#5F6368"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M62.772,486.516V516.037V611.154"
+      android:fillColor="#5F6368"/>
+  <path
+      android:pathData="M62.772,486.516V516.037V611.154"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M74.344,500.944L62.772,522.368"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M74.344,536.43L62.772,558.736"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M53.263,489.976L62.772,503.154"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M48.251,523.914L62.772,540.552"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#9ADCB2"/>
+  <path
+      android:pathData="M37,612H377"
+      android:strokeWidth="4"
+      android:fillColor="#00000000"
+      android:strokeColor="#9AA0A6"/>
+  <path
+      android:pathData="M81.24,441.187L84.066,443.127L87.333,441.98L86.273,445.244L88.393,447.978H84.949L83.006,450.8L81.947,447.537L78.679,446.655L81.417,444.626L81.24,441.187Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M172.561,469L175.387,471.029L178.654,469.794L177.594,473.057L179.714,475.791H176.27L174.327,478.702L173.267,475.438L170,474.468L172.737,472.44L172.561,469Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M278.073,481.229L280.81,483.257L284.078,482.022L283.018,485.286L285.137,488.02H281.693L279.839,490.93L278.691,487.667L275.424,486.697L278.161,484.668L278.073,481.229Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M325.561,459L328.387,460.94L331.654,459.794L330.594,463.057L332.714,465.791H329.27L327.327,468.613L326.267,465.35L323,464.468L325.737,462.44L325.561,459Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M209.649,517L212.475,519.029L215.654,517.794L214.682,521.057L216.802,523.791H213.358L211.415,526.702L210.356,523.438L207,522.468L209.737,520.44L209.649,517Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M128.836,482.816L129.455,485.11L131.662,485.815L129.808,487.314L129.896,489.784L128.042,488.285L125.922,489.078L126.629,486.785L125.304,484.757L127.512,484.845L128.836,482.816Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M37.532,450L38.239,452.381L40.358,453.087L38.504,454.498L38.592,456.968L36.737,455.556L34.618,456.35L35.325,453.969L34,452.029H36.208L37.532,450Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M284.077,516.947L284.784,519.329L286.903,520.034L285.048,521.445L285.137,523.915L283.282,522.415L281.163,523.297L281.869,520.916L280.545,518.976H282.753L284.077,516.947Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M232.771,444.893L233.478,447.274L235.597,447.979L233.743,449.391L233.831,451.86L231.977,450.449L229.857,451.243L230.564,448.861L229.239,446.921H231.447L232.771,444.893Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M152.384,521L152.914,522.676L154.415,523.117L153.091,524.175L153.179,525.851L151.854,524.881L150.442,525.41L150.883,523.734L150,522.411H151.501L152.384,521Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M136.473,515L136.914,516.676L138.415,517.117L137.179,518.175V519.851L135.943,518.881L134.442,519.41L134.971,517.822L134,516.411H135.589L136.473,515Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M314.454,410.406L314.896,412.082L316.397,412.523L315.16,413.493V415.257L313.924,414.199L312.423,414.816L312.953,413.14L311.981,411.817H313.571L314.454,410.406Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M344.212,488.195L344.742,489.871L346.243,490.4L344.919,491.37L345.007,493.046L343.683,492.076L342.27,492.605L342.711,491.018L341.828,489.606H343.329L344.212,488.195Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M47.067,420.814L47.508,422.402L49.009,422.931L47.773,423.901V425.665L46.537,424.607L45.036,425.136L45.566,423.549L44.594,422.137L46.184,422.226L47.067,420.814Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M265.887,448.42L266.328,450.007L267.829,450.537L266.593,451.507V453.271L265.357,452.212L263.856,452.83L264.385,451.154L263.414,449.831H265.004L265.887,448.42Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M110.912,413.759L111.441,415.435L112.854,415.876L111.618,416.934V418.61L110.382,417.551L108.969,418.169L109.41,416.493L108.439,415.17H110.028L110.912,413.759Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M149.412,418.344L152.238,420.372L155.505,419.138L154.445,422.489L156.565,425.135L153.121,425.223L151.178,428.045L150.118,424.782L146.851,423.812L149.589,421.783L149.412,418.344Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M37.532,450L38.239,452.381L40.358,453.087L38.504,454.498L38.592,456.968L36.737,455.556L34.618,456.35L35.325,453.969L34,452.029H36.208L37.532,450Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M141.906,455.74L142.612,458.122L144.731,458.827L142.877,460.238L142.965,462.708L141.111,461.208L139.08,462.09L139.786,459.709L138.374,457.769H140.669L141.906,455.74Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M201.777,459.267L202.395,461.56L204.603,462.265L202.748,463.765L202.836,466.146L200.982,464.735L198.863,465.529L199.569,463.235L198.245,461.207H200.452L201.777,459.267Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M370.175,504.248L370.793,506.629L373.001,507.335L371.146,508.746L371.234,511.216L369.38,509.716L367.261,510.598L367.967,508.217L366.643,506.277H368.85L370.175,504.248Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M300.532,437L301.239,439.381L303.358,440.087L301.592,441.498V443.968L299.826,442.468L297.706,443.35L298.413,440.969L297,439.029H299.296L300.532,437Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M193.384,420L193.914,421.676L195.415,422.117L194.091,423.087L194.179,424.851L192.854,423.792L191.442,424.41L191.883,422.734L191,421.411H192.501L193.384,420Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M248.313,469.323L248.843,470.911L250.256,471.44L249.02,472.41V474.174L247.784,473.116L246.371,473.645L246.812,472.057L245.841,470.646L247.43,470.734L248.313,469.323Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M169.988,496.751L170.518,498.427L172.019,498.956L170.694,499.926L170.782,501.602L169.458,500.632L168.045,501.161L168.487,499.573L167.604,498.162H169.105L169.988,496.751Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M222.441,400L222.882,401.676L224.384,402.117L223.059,403.175L223.147,404.851L221.911,403.792L220.41,404.41L220.851,402.734L219.968,401.411H221.469L222.441,400Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M211.754,492.341L212.284,493.928L213.785,494.458L212.461,495.428L212.549,497.192L211.225,496.133L209.812,496.662L210.253,495.075L209.37,493.664L210.871,493.752L211.754,492.341Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M254.054,420.814L254.584,422.402L256.085,422.931L254.761,423.901L254.849,425.665L253.524,424.607L252.111,425.136L252.553,423.549L251.67,422.137L253.171,422.226L254.054,420.814Z"
+      android:fillColor="#DADCE0"/>
+  <path
+      android:pathData="M307.162,602.392H205L255.565,502.27H322.273L374.238,602.392H307.162L255.565,502.785L307.162,602.392Z"
+      android:fillColor="#5F6368"/>
+  <path
+      android:pathData="M307.162,602.392H205L255.565,502.27H322.273L374.238,602.392H307.162ZM307.162,602.392L255.565,502.785"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#A8CBFE"/>
+  <path
+      android:pathData="M255.565,503.079V602.392"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#A8CBFE"/>
+  <path
+      android:pathData="M356.686,430.931C358.218,423.774 353.2,416.635 345.477,414.986C337.754,413.337 330.251,417.801 328.719,424.958C327.186,432.115 332.205,439.254 339.928,440.903C347.651,442.553 355.154,438.088 356.686,430.931Z"
+      android:fillColor="#FEEFC3"/>
+  <path
+      android:pathData="M344.708,427.026C345.789,421.976 342.244,416.939 336.79,415.774C331.336,414.609 326.038,417.759 324.957,422.808C323.876,427.858 327.421,432.895 332.875,434.06C338.329,435.225 343.627,432.076 344.708,427.026Z"
+      android:fillColor="#5F6368"/>
+  <path
+      android:pathData="M2,101L410,101A2,2 0,0 1,412 103L412,187A2,2 0,0 1,410 189L2,189A2,2 0,0 1,0 187L0,103A2,2 0,0 1,2 101z"
+      android:fillColor="#E8EAED"/>
+  <path
+      android:pathData="M99,129L313,129A2,2 0,0 1,315 131L315,159A2,2 0,0 1,313 161L99,161A2,2 0,0 1,97 159L97,131A2,2 0,0 1,99 129z"
+      android:fillColor="#80868B"/>
+  <path
+      android:pathData="M32,123L60,123A8,8 0,0 1,68 131L68,159A8,8 0,0 1,60 167L32,167A8,8 0,0 1,24 159L24,131A8,8 0,0 1,32 123z"
+      android:fillColor="#80868B"/>
+  <path
+      android:pathData="M0,0h412v101h-412z"
+      android:fillColor="#202124"/>
+  <path
+      android:pathData="M34.5,48L377.5,48A18.5,18.5 0,0 1,396 66.5L396,66.5A18.5,18.5 0,0 1,377.5 85L34.5,85A18.5,18.5 0,0 1,16 66.5L16,66.5A18.5,18.5 0,0 1,34.5 48z"
+      android:fillColor="#3C4043"/>
+  <path
+      android:pathData="M28,654L356,654A4,4 0,0 1,360 658L360,672A4,4 0,0 1,356 676L28,676A4,4 0,0 1,24 672L24,658A4,4 0,0 1,28 654z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,684L367,684A4,4 0,0 1,371 688L371,702A4,4 0,0 1,367 706L28,706A4,4 0,0 1,24 702L24,688A4,4 0,0 1,28 684z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,714L337,714A4,4 0,0 1,341 718L341,732A4,4 0,0 1,337 736L28,736A4,4 0,0 1,24 732L24,718A4,4 0,0 1,28 714z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,744L210,744A4,4 0,0 1,214 748L214,762A4,4 0,0 1,210 766L28,766A4,4 0,0 1,24 762L24,748A4,4 0,0 1,28 744z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,790L344,790A4,4 0,0 1,348 794L348,808A4,4 0,0 1,344 812L28,812A4,4 0,0 1,24 808L24,794A4,4 0,0 1,28 790z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M28,820L337,820A4,4 0,0 1,341 824L341,838A4,4 0,0 1,337 842L28,842A4,4 0,0 1,24 838L24,824A4,4 0,0 1,28 820z"
+      android:fillColor="#BDC1C6"/>
+</vector>
diff --git a/quickstep/res/drawable/default_sandbox_mock_launcher.xml b/quickstep/res/drawable/default_sandbox_mock_launcher.xml
new file mode 100644
index 0000000..38fbcf0
--- /dev/null
+++ b/quickstep/res/drawable/default_sandbox_mock_launcher.xml
@@ -0,0 +1,21 @@
+<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/go/res/values/dimens.xml b/quickstep/res/drawable/default_sandbox_wallpaper.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to quickstep/res/drawable/default_sandbox_wallpaper.xml
index f1b1053..816d1d6 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/drawable/default_sandbox_wallpaper.xml
@@ -1,20 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
+<!--
+     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.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+  <solid android:color="?android:attr/colorBackground" />
+</shape>
diff --git a/quickstep/res/drawable/gesture_tutorial_action_button_background.xml b/quickstep/res/drawable/gesture_tutorial_action_button_background.xml
index 3f3b288..ac6a52a 100644
--- a/quickstep/res/drawable/gesture_tutorial_action_button_background.xml
+++ b/quickstep/res/drawable/gesture_tutorial_action_button_background.xml
@@ -13,8 +13,19 @@
     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">
-    <corners android:radius="@dimen/default_dialog_corner_radius"/>
-    <solid android:color="@color/gesture_tutorial_primary_color"/>
-</shape>
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape
+            android:id="@android:id/mask"
+            android:shape="rectangle">
+            <corners android:radius="50dp"/>
+        </shape>
+    </item>
+    <item>
+        <shape
+            android:shape="rectangle">
+            <corners android:radius="50dp"/>
+            <solid android:color="@color/gesture_tutorial_primary_color"/>
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_cancel_button_background.xml b/quickstep/res/drawable/gesture_tutorial_cancel_button_background.xml
new file mode 100644
index 0000000..0a34af6
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_cancel_button_background.xml
@@ -0,0 +1,21 @@
+<!--
+    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">
+    <corners android:radius="50dp"/>
+    <solid android:color="@android:color/transparent"/>
+    <stroke android:width="1dp" android:color="@color/gesture_tutorial_primary_color"/>
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/gesture_tutorial_loop_back.xml b/quickstep/res/drawable/gesture_tutorial_loop_back.xml
new file mode 100644
index 0000000..d2909ff
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_loop_back.xml
@@ -0,0 +1,96 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <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="0"
+                    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="783"
+                    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="1000"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0.25"
+                    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="1000"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="1000"
+                    android:valueFrom="0.75"
+                    android:valueTo="0.25"
+                    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="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="2000"
+                    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" />
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="12.5"
+                    android:translateY="446">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="0.25"
+                        android:fillColor="@color/gesture_tutorial_primary_color"
+                        android:fillType="nonZero"
+                        android:pathData=" M12.5 -446 C12.5,-446 12.5,446 12.5,446 C12.5,446 -12.5,446 -12.5,446 C-12.5,446 -12.5,-446 -12.5,-446 C-12.5,-446 12.5,-446 12.5,-446c " />
+                </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_loop_home.xml b/quickstep/res/drawable/gesture_tutorial_loop_home.xml
new file mode 100644
index 0000000..931f8c0
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_loop_home.xml
@@ -0,0 +1,96 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1000"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0.25"
+                    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="1000"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="1000"
+                    android:valueFrom="0.75"
+                    android:valueTo="0.25"
+                    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">
+        <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="850"
+                    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="2000"
+                    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="879.5">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="0.25"
+                        android:fillColor="@color/gesture_tutorial_primary_color"
+                        android:fillType="nonZero"
+                        android:pathData=" M206 -12.5 C206,-12.5 206,12.5 206,12.5 C206,12.5 -206,12.5 -206,12.5 C-206,12.5 -206,-12.5 -206,-12.5 C-206,-12.5 206,-12.5 206,-12.5c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="206"
+                    android:translateY="446" />
+            </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_loop_overview.xml b/quickstep/res/drawable/gesture_tutorial_loop_overview.xml
new file mode 100644
index 0000000..a4c532b
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_loop_overview.xml
@@ -0,0 +1,96 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1000"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="0"
+                    android:valueFrom="0.25"
+                    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="1000"
+                    android:propertyName="fillAlpha"
+                    android:startOffset="1000"
+                    android:valueFrom="0.75"
+                    android:valueTo="0.25"
+                    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">
+        <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="1500"
+                    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="2000"
+                    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="879.5">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="0.25"
+                        android:fillColor="@color/gesture_tutorial_primary_color"
+                        android:fillType="nonZero"
+                        android:pathData=" M206 -12.5 C206,-12.5 206,12.5 206,12.5 C206,12.5 -206,12.5 -206,12.5 C-206,12.5 -206,-12.5 -206,-12.5 C-206,-12.5 206,-12.5 206,-12.5c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="206"
+                    android:translateY="446" />
+            </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_back.xml b/quickstep/res/drawable/gesture_tutorial_motion_back.xml
new file mode 100644
index 0000000..a6860fa
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_motion_back.xml
@@ -0,0 +1,1233 @@
+<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
new file mode 100644
index 0000000..aff35c1
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_motion_home_dark_mode.xml
@@ -0,0 +1,1254 @@
+<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
new file mode 100644
index 0000000..98d97ad
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_motion_home_light_mode.xml
@@ -0,0 +1,1254 @@
+<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
new file mode 100644
index 0000000..b007d20
--- /dev/null
+++ b/quickstep/res/drawable/gesture_tutorial_motion_overview_dark_mode.xml
@@ -0,0 +1,1623 @@
+<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/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml b/quickstep/res/drawable/hotseat_edu_notification_icon.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml
rename to quickstep/res/drawable/hotseat_edu_notification_icon.xml
diff --git a/res/drawable-v26/ic_deepshortcut_placeholder.xml b/quickstep/res/drawable/ic_all_set.xml
similarity index 60%
rename from res/drawable-v26/ic_deepshortcut_placeholder.xml
rename to quickstep/res/drawable/ic_all_set.xml
index 3fa8506..f718b8b 100644
--- a/res/drawable-v26/ic_deepshortcut_placeholder.xml
+++ b/quickstep/res/drawable/ic_all_set.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2017 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.
@@ -14,7 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="?attr/popupColorSecondary"/>
-    <foreground android:drawable="?attr/popupColorSecondary"/>
-</adaptive-icon>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/quickstep/res/drawable/ic_ime_switcher.xml b/quickstep/res/drawable/ic_ime_switcher.xml
new file mode 100644
index 0000000..a86d390
--- /dev/null
+++ b/quickstep/res/drawable/ic_ime_switcher.xml
@@ -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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:pathData="M19,7h2v2h-2V7zM15,7h2v2h-2V7zM3,7h2v2H3V7zM7,7h2v2H7V7zM11,7h2v2h-2V7zM19,11h2v2h-2V11zM15,11h2v2h-2V11zM3,11h2v2H3V11zM7,11h2v2H7V11zM11,11h2v2h-2V11zM7,15h10v2H7V15z"
+        android:fillColor="@android:color/white" />
+</vector>
diff --git a/quickstep/res/drawable/ic_sysbar_back.xml b/quickstep/res/drawable/ic_sysbar_back.xml
new file mode 100644
index 0000000..1eea677
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_back.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:autoMirrored="true"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M6.49,14.86c-0.66-0.39-0.66-1.34,0-1.73l6.02-3.53l5.89-3.46C19.11,5.73,20,6.26,20,7.1V14v6.9 c0,0.84-0.89,1.37-1.6,0.95l-5.89-3.46L6.49,14.86z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_home.xml b/quickstep/res/drawable/ic_sysbar_home.xml
new file mode 100644
index 0000000..b4b397b
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_home.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 14 7 C 17.8659932488 7 21 10.1340067512 21 14 C 21 17.8659932488 17.8659932488 21 14 21 C 10.1340067512 21 7 17.8659932488 7 14 C 7 10.1340067512 10.1340067512 7 14 7 Z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_recent.xml b/quickstep/res/drawable/ic_sysbar_recent.xml
new file mode 100644
index 0000000..f8c4778
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_recent.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19.9,21.5H8.1c-0.88,0-1.6-0.72-1.6-1.6V8.1c0-0.88,0.72-1.6,1.6-1.6h11.8c0.88,0,1.6,0.72,1.6,1.6v11.8 C21.5,20.78,20.78,21.5,19.9,21.5z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/mock_conversation.xml b/quickstep/res/drawable/mock_conversation.xml
new file mode 100644
index 0000000..272d9ed
--- /dev/null
+++ b/quickstep/res/drawable/mock_conversation.xml
@@ -0,0 +1,212 @@
+<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
new file mode 100644
index 0000000..2dbc88f
--- /dev/null
+++ b/quickstep/res/drawable/mock_conversations_list.xml
@@ -0,0 +1,361 @@
+<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
new file mode 100644
index 0000000..93b22b7
--- /dev/null
+++ b/quickstep/res/drawable/mock_webpage_dark_mode.xml
@@ -0,0 +1,251 @@
+<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
new file mode 100644
index 0000000..98abb92
--- /dev/null
+++ b/quickstep/res/drawable/mock_webpage_light_mode.xml
@@ -0,0 +1,263 @@
+<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_bg.xml b/quickstep/res/drawable/task_menu_bg.xml
index 7334d98..be1b387 100644
--- a/quickstep/res/drawable/task_menu_bg.xml
+++ b/quickstep/res/drawable/task_menu_bg.xml
@@ -14,25 +14,8 @@
      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 android:gravity="bottom">
-        <!-- Shadow -->
-        <shape>
-            <gradient android:angle="270"
-                android:endColor="@android:color/transparent"
-                android:startColor="#26000000" />
-            <size android:height="@dimen/task_card_menu_shadow_height" />
-        </shape>
-    </item>
-    <item android:bottom="@dimen/task_card_menu_shadow_height">
-        <!-- Background -->
-        <shape>
-            <corners
-                android:topLeftRadius="?android:attr/dialogCornerRadius"
-                android:topRightRadius="?android:attr/dialogCornerRadius"
-                android:bottomLeftRadius="0dp"
-                android:bottomRightRadius="0dp" />
-            <solid android:color="?attr/popupColorPrimary" />
-        </shape>
-    </item>
-</layer-list>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@android:color/transparent"/>
+    <corners android:radius="@dimen/task_menu_corner_radius"/>
+</shape>
diff --git a/go/res/values/dimens.xml b/quickstep/res/drawable/task_menu_item_bg.xml
similarity index 69%
copy from go/res/values/dimens.xml
copy to quickstep/res/drawable/task_menu_item_bg.xml
index f1b1053..b6a8b90 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/drawable/task_menu_item_bg.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
@@ -14,7 +15,7 @@
      limitations under the License.
 -->
 
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<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>
diff --git a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
new file mode 100644
index 0000000..d6160de
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/taskbar_icon_selection_ripple">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+            <corners android:radius="8dp" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/quickstep/res/drawable/tutorial_step_indicator_pill.xml b/quickstep/res/drawable/tutorial_step_indicator_pill.xml
new file mode 100644
index 0000000..d4e8f84
--- /dev/null
+++ b/quickstep/res/drawable/tutorial_step_indicator_pill.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="3dp"/>
+    <size android:width="16dp" android:height="6dp"/>
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
new file mode 100644
index 0000000..e79e57e
--- /dev/null
+++ b/quickstep/res/layout/activity_allset.xml
@@ -0,0 +1,81 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    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">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_gravity="start"
+        android:gravity="start"
+        android:orientation="vertical">
+
+        <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"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/allset_title_margin_top"
+            android:gravity="start"
+            android:text="@string/allset_title"/>
+
+        <TextView
+            android:id="@+id/subtitle"
+            style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/allset_subtitle_margin_top"
+            android:gravity="start"
+            android:text="@string/allset_description"/>
+    </LinearLayout>
+
+    <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" />
+
+    <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>
diff --git a/quickstep/recents_ui_overrides/res/layout/all_apps_edu_view.xml b/quickstep/res/layout/all_apps_edu_view.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/all_apps_edu_view.xml
rename to quickstep/res/layout/all_apps_edu_view.xml
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
index 83594e4..c4642e4 100644
--- a/quickstep/res/layout/digital_wellbeing_toast.xml
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -16,12 +16,13 @@
 -->
 <TextView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:priv-android="http://schemas.android.com/apk/prv/res/android"
+    style="@style/TextTitle"
     android:layout_width="match_parent"
     android:layout_height="48dp"
     android:background="@drawable/bg_wellbeing_toast"
-    android:fontFamily="sans-serif"
     android:forceHasOverlappingRendering="false"
     android:gravity="center"
     android:importantForAccessibility="noHideDescendants"
-    android:textColor="@android:color/white"
+    android:textColor="?priv-android:attr/textColorOnAccent"
     android:textSize="14sp"/>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
similarity index 75%
rename from quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
rename to quickstep/res/layout/fallback_recents_activity.xml
index cd64a94..a43296f 100644
--- a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -19,12 +19,26 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
+    <com.android.quickstep.views.SplitPlaceholderView
+        android:id="@+id/split_placeholder"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/split_placeholder_size"
+        android:background="@android:color/white"
+        android:alpha=".8"
+        android:visibility="gone" />
+
     <com.android.quickstep.fallback.RecentsDragLayer
         android:id="@+id/drag_layer"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:clipChildren="false">
 
+        <com.android.launcher3.views.ScrimView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/scrim_view"
+            android:background="@android:color/transparent" />
+
         <com.android.quickstep.fallback.FallbackRecentsView
             android:id="@id/overview_panel"
             android:layout_width="match_parent"
diff --git a/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml b/quickstep/res/layout/floating_header_content.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/floating_header_content.xml
rename to quickstep/res/layout/floating_header_content.xml
diff --git a/quickstep/res/layout/gesture_tutorial_dialog.xml b/quickstep/res/layout/gesture_tutorial_dialog.xml
new file mode 100644
index 0000000..59bf7b9
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_dialog.xml
@@ -0,0 +1,74 @@
+<?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:background="@drawable/bg_sandbox_feedback"
+    android:paddingTop="24dp"
+    android:paddingBottom="24dp"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp">
+
+    <TextView
+        android:id="@+id/gesture_tutorial_dialog_title"
+        style="@style/TextAppearance.GestureTutorial.Dialog.Title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:text="@string/skip_tutorial_dialog_title"
+
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"/>
+
+    <TextView
+        android:id="@+id/gesture_tutorial_dialog_subtitle"
+        style="@style/TextAppearance.GestureTutorial.Dialog.Subtitle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:text="@string/skip_tutorial_dialog_subtitle"
+
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_dialog_title"/>
+
+    <!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
+         of elevation and shadow) which is replaced by ripple effect in android:foreground -->
+    <Button
+        android:id="@+id/gesture_tutorial_dialog_cancel_button"
+        style="@style/TextAppearance.GestureTutorial.CancelButtonLabel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="46dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:background="@drawable/gesture_tutorial_cancel_button_background"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:stateListAnimator="@null"
+        android:text="@string/gesture_tutorial_action_button_label_cancel"
+
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_dialog_subtitle"/>
+
+    <Button
+        android:id="@+id/gesture_tutorial_dialog_confirm_button"
+        style="@style/TextAppearance.GestureTutorial.ButtonLabel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="46dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:background="@drawable/gesture_tutorial_action_button_background"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:stateListAnimator="@null"
+        android:text="@string/gesture_tutorial_action_button_label_skip"
+
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_dialog_subtitle"/>
+
+</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 43bf0ea..cdda43c 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -13,10 +13,47 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.quickstep.interaction.RootSandboxLayout
+    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="?android:attr/colorBackground">
+    android:clipChildren="false">
+
+    <RelativeLayout
+        android:id="@+id/gesture_tutorial_fake_launcher_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <ImageView
+            android:id="@+id/gesture_tutorial_fake_hotseat_view"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="70dp"/>
+
+    </RelativeLayout>
+
+    <com.android.launcher3.views.ClipIconView
+        android:id="@+id/gesture_tutorial_fake_icon_view"
+        android:layout_width="20dp"
+        android:layout_height="20dp"
+        android:visibility="invisible" />
+
+    <View
+        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" />
+
+    <View
+        android:id="@+id/gesture_tutorial_fake_task_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="visible" />
 
     <View
         android:id="@+id/gesture_tutorial_ripple_view"
@@ -24,106 +61,112 @@
         android:layout_height="match_parent"
         android:background="@drawable/gesture_tutorial_ripple"/>
 
-    <com.android.launcher3.views.ClipIconView
-        android:id="@+id/gesture_tutorial_fake_icon_view"
-        android:layout_width="20dp"
-        android:layout_height="20dp"
-        android:background="@drawable/bg_circle"
-        android:backgroundTint="@color/gesture_tutorial_fake_task_view_color"
-        android:visibility="invisible" />
-
-    <View
-        android:id="@+id/gesture_tutorial_fake_task_view"
+    <ImageView
+        android:id="@+id/gesture_tutorial_feedback_video"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/gesture_tutorial_fake_task_view_color"
-        android:visibility="invisible" />
+        android:layout_alignParentTop="true"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentEnd="true"
+        android:scaleType="fitXY"
+        android:visibility="gone"/>
 
     <ImageView
-        android:id="@+id/gesture_tutorial_fragment_hand_coaching"
+        android:id="@+id/gesture_tutorial_gesture_video"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:scaleType="centerCrop"/>
-
-    <ImageButton
-        android:id="@+id/gesture_tutorial_fragment_close_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
-        android:layout_marginStart="4dp"
-        android:layout_marginTop="30dp"
-        android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_titles_container"
-        android:background="@android:color/transparent"
-        android:contentDescription="@string/gesture_tutorial_close_button_content_description"
-        android:padding="18dp"
-        android:src="@drawable/gesture_tutorial_close_button"
-        android:tint="?android:attr/textColorPrimary"/>
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentEnd="true"
+        android:scaleType="fitXY"
+        android:visibility="gone"/>
 
-    <LinearLayout
-        android:id="@+id/gesture_tutorial_fragment_titles_container"
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/gesture_tutorial_fragment_feedback_view"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
-        android:layout_marginTop="70dp"
-        android:focusable="true"
-        android:gravity="center_horizontal"
-        android:orientation="vertical">
-
-        <TextView
-            android:id="@+id/gesture_tutorial_fragment_title_view"
-            style="@style/TextAppearance.GestureTutorial.Title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/gesture_tutorial_title_margin_start_end"
-            android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"/>
-
-        <TextView
-            android:id="@+id/gesture_tutorial_fragment_subtitle_view"
-            style="@style/TextAppearance.GestureTutorial.Subtitle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/gesture_tutorial_subtitle_margin_start_end"
-            android:layout_marginTop="10dp"
-            android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"/>
-    </LinearLayout>
-
-    <TextView
-        android:id="@+id/gesture_tutorial_fragment_feedback_view"
-        style="@style/TextAppearance.GestureTutorial.Feedback"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_above="@id/gesture_tutorial_fragment_action_button"
         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_marginBottom="10dp"/>
+        android:layout_marginTop="24dp"
+        android:paddingTop="24dp"
+        android:paddingBottom="16dp"
+        android:background="@drawable/bg_sandbox_feedback">
 
-    <!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
-         of elevation and shadow) which is replaced by ripple effect in android:foreground -->
-    <Button
-        android:id="@+id/gesture_tutorial_fragment_action_button"
-        style="@style/TextAppearance.GestureTutorial.ButtonLabel"
-        android:layout_width="142dp"
-        android:layout_height="49dp"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentBottom="true"
-        android:layout_marginEnd="@dimen/gesture_tutorial_button_margin_start_end"
-        android:layout_marginBottom="48dp"
-        android:background="@drawable/gesture_tutorial_action_button_background"
-        android:foreground="?android:attr/selectableItemBackgroundBorderless"
-        android:stateListAnimator="@null"/>
+        <TextView
+            android:id="@+id/gesture_tutorial_fragment_feedback_title"
+            style="@style/TextAppearance.GestureTutorial.Feedback.Title"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="24dp"
+            android:layout_marginEnd="24dp"
 
-    <Button
-        android:id="@+id/gesture_tutorial_fragment_action_text_button"
-        style="@style/TextAppearance.GestureTutorial.TextButtonLabel"
-        android:layout_width="142dp"
-        android:layout_height="49dp"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentBottom="true"
-        android:layout_marginStart="@dimen/gesture_tutorial_button_margin_start_end"
-        android:layout_marginBottom="48dp"
-        android:background="@null"
-        android:foreground="?android:attr/selectableItemBackgroundBorderless"
-        android:stateListAnimator="@null"/>
-</RelativeLayout>
\ No newline at end of file
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"/>
+
+        <TextView
+            android:id="@+id/gesture_tutorial_fragment_feedback_subtitle"
+            style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="24dp"
+            android:layout_marginStart="24dp"
+            android:layout_marginEnd="24dp"
+
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_title"/>
+
+        <com.android.quickstep.interaction.TutorialStepIndicator
+            android:id="@+id/gesture_tutorial_fragment_feedback_tutorial_step"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@id/gesture_tutorial_fragment_action_button"
+            app:layout_constraintBottom_toBottomOf="@id/gesture_tutorial_fragment_action_button"/>
+
+        <Button
+            android:id="@+id/gesture_tutorial_fragment_action_button"
+            style="@style/TextAppearance.GestureTutorial.ButtonLabel"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="32dp"
+            android:layout_marginEnd="16dp"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp"
+            android:paddingStart="26dp"
+            android:paddingEnd="26dp"
+            android:text="@string/gesture_tutorial_action_button_label"
+            android:background="@drawable/gesture_tutorial_action_button_background"
+            android:stateListAnimator="@null"
+            android:visibility="invisible"
+
+            app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_subtitle"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <Button
+            style="@style/TextAppearance.GestureTutorial.Feedback.Subtext"
+            android:id="@+id/gesture_tutorial_fragment_close_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="32dp"
+            android:layout_marginEnd="16dp"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp"
+            android:paddingStart="26dp"
+            android:paddingEnd="26dp"
+            android:text="@string/gesture_tutorial_action_button_label_skip"
+            android:background="?android:attr/selectableItemBackgroundBorderless"
+
+            app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_subtitle"
+            app:layout_constraintStart_toStartOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</com.android.quickstep.interaction.RootSandboxLayout>
\ 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 8ed09ef..68680d3 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -14,16 +14,17 @@
      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="@dimen/overview_actions_height"
-    android:layout_gravity="center_horizontal|bottom">
+    android:layout_height="wrap_content">
 
     <LinearLayout
         android:id="@+id/action_buttons"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="bottom|center_horizontal"
         android:orientation="horizontal">
 
         <Space
@@ -57,7 +58,7 @@
             android:visibility="gone" />
 
         <Space
-            android:id="@+id/share_space"
+            android:id="@+id/oav_three_button_space"
             android:layout_width="0dp"
             android:layout_height="1dp"
             android:layout_weight="1"
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index fc06ba0..1ee726e 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -21,7 +21,5 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="@string/recents_clear_all"
-    android:textColor="?attr/workspaceTextColor"
-    android:textSize="14sp"
-    android:translationY="@dimen/task_thumbnail_half_top_margin"
-    />
\ No newline at end of file
+    android:textColor="?android:attr/textColorPrimary"
+    android:textSize="14sp" />
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
similarity index 80%
rename from quickstep/recents_ui_overrides/res/layout/overview_panel.xml
rename to quickstep/res/layout/overview_panel.xml
index fe57e9b..f303f31 100644
--- a/quickstep/recents_ui_overrides/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -15,6 +15,7 @@
 -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
+
     <com.android.quickstep.views.LauncherRecentsView
         android:id="@+id/overview_panel"
         android:layout_width="match_parent"
@@ -22,9 +23,15 @@
         android:accessibilityPaneTitle="@string/accessibility_recent_apps"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:theme="@style/HomeScreenElementTheme"
         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/recents_ui_overrides/res/layout/predicted_app_icon.xml b/quickstep/res/layout/predicted_app_icon.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/predicted_app_icon.xml
rename to quickstep/res/layout/predicted_app_icon.xml
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/res/layout/predicted_hotseat_edu.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
rename to quickstep/res/layout/predicted_hotseat_edu.xml
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index f9bb2f2..7e5b85c 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -13,19 +13,20 @@
      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. -->
 <com.android.quickstep.views.TaskView
     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:elevation="4dp"
     android:focusable="true">
 
     <com.android.quickstep.views.TaskThumbnailView
         android:id="@+id/snapshot"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/task_thumbnail_top_margin"/>
+        android:layout_height="match_parent"/>
 
     <com.android.quickstep.views.IconView
         android:id="@+id/icon"
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index 098b34f..a5c9445 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -16,39 +16,30 @@
 -->
 <com.android.quickstep.views.TaskMenuView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:animateLayoutChanges="true"
     android:background="@drawable/task_menu_bg"
-    android:paddingBottom="@dimen/task_card_menu_shadow_height"
     android:orientation="vertical"
     android:visibility="invisible">
 
-    <com.android.quickstep.views.IconView
-      android:id="@+id/task_icon"
-      android:layout_width="@dimen/task_thumbnail_icon_size"
-      android:layout_height="@dimen/task_thumbnail_icon_size"
-      android:layout_gravity="top|center_horizontal"
-      android:layout_marginBottom="@dimen/deep_shortcut_drawable_padding"
-      android:focusable="false"
-      android:importantForAccessibility="no" />
-
     <TextView
         android:id="@+id/task_name"
+        android:background="@drawable/task_menu_item_bg"
+        android:textColor="?android:attr/textColorPrimary"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_horizontal"
-        android:layout_marginBottom="16dp"
-        android:textSize="12sp"/>
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:layout_marginBottom="2dp"
+        android:paddingTop="@dimen/task_menu_vertical_padding"
+        android:paddingBottom="@dimen/task_menu_vertical_padding"
+        android:textSize="16sp"/>
 
     <LinearLayout
         android:id="@+id/menu_option_layout"
-        style="@style/TaskMenu"
-        android:divider="@drawable/all_apps_divider"
-        android:showDividers="beginning"
-        android:paddingStart="@dimen/task_card_menu_horizontal_padding"
-        android:paddingEnd="@dimen/task_card_menu_horizontal_padding"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"/>
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:showDividers="middle" />
 
 </com.android.quickstep.views.TaskMenuView>
\ 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 102ae9b..5978b97 100644
--- a/quickstep/res/layout/task_view_menu_option.xml
+++ b/quickstep/res/layout/task_view_menu_option.xml
@@ -16,35 +16,30 @@
 
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/TaskMenu.Option"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_gravity="center"
     android:orientation="vertical"
     android:paddingTop="@dimen/task_card_menu_option_vertical_padding"
     android:paddingBottom="@dimen/task_card_menu_option_vertical_padding"
-    android:background="?android:attr/selectableItemBackground"
+    android:background="@drawable/task_menu_item_bg"
     android:theme="@style/PopupItem" >
 
     <View
       android:id="@+id/icon"
       android:layout_width="@dimen/system_shortcut_icon_size"
       android:layout_height="@dimen/system_shortcut_icon_size"
-      android:layout_marginTop="@dimen/system_shortcut_header_icon_padding"
-      android:layout_marginBottom="@dimen/deep_shortcut_drawable_padding"
+      android:layout_marginStart="@dimen/task_menu_option_start_margin"
       android:layout_gravity="center_horizontal"
-      android:backgroundTint="?android:attr/textColorTertiary"/>
+      android:backgroundTint="?android:attr/textColorPrimary"/>
 
     <TextView
         style="@style/BaseIcon"
         android:id="@+id/text"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingBottom="@dimen/popup_padding_end"
-        android:textSize="12sp"
+        android:layout_marginStart="@dimen/task_menu_option_start_margin"
+        android:textSize="14sp"
         android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="sans-serif"
-        android:gravity="center_horizontal"
-        android:layout_gravity="center_horizontal"
         android:focusable="false" />
 
 </LinearLayout>
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
new file mode 100644
index 0000000..e680233
--- /dev/null
+++ b/quickstep/res/layout/taskbar.xml
@@ -0,0 +1,55 @@
+<?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.TaskbarDragLayer
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/taskbar_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <com.android.launcher3.taskbar.TaskbarView
+        android:id="@+id/taskbar_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:forceHasOverlappingRendering="false"
+        android:layout_gravity="bottom" >
+
+        <LinearLayout
+            android:id="@+id/system_button_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
+            android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
+            android:forceHasOverlappingRendering="false"
+            android:gravity="center" />
+
+        <LinearLayout
+            android:id="@+id/hotseat_icons_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:forceHasOverlappingRendering="false"
+            android:gravity="center" />
+
+    </com.android.launcher3.taskbar.TaskbarView>
+
+    <com.android.launcher3.taskbar.ImeBarView
+        android:id="@+id/ime_bar_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"/>
+
+</com.android.launcher3.taskbar.TaskbarDragLayer>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/quickstep/res/layout/taskbar_app_icon.xml
similarity index 78%
copy from go/res/values/dimens.xml
copy to quickstep/res/layout/taskbar_app_icon.xml
index f1b1053..6fefdb6 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/layout/taskbar_app_icon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
@@ -14,7 +14,4 @@
      limitations under the License.
 -->
 
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace.Taskbar" />
diff --git a/go/res/values/dimens.xml b/quickstep/res/layout/taskbar_predicted_app_icon.xml
similarity index 78%
copy from go/res/values/dimens.xml
copy to quickstep/res/layout/taskbar_predicted_app_icon.xml
index f1b1053..211ebc8 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/layout/taskbar_predicted_app_icon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
@@ -14,7 +14,4 @@
      limitations under the License.
 -->
 
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<com.android.launcher3.uioverrides.PredictedAppIcon style="@style/BaseIcon.Workspace.Taskbar" />
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 03d774d..815ba04 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Verdeelde skerm"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Speld vas"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Vormvry"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Geen onlangse items nie"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Programvoorstelle is geaktiveer"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Programvoorstelle is gedeaktiveer"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Voorspelde program: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Maak seker dat jy van die rand heel links af swiep."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Maak seker dat jy van die linkerrand na die middel van die skerm swiep en laat los."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Dis al! Probeer nou om van die regterrand af te swiep."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Maak seker dat jy van die rand heel regs af swiep."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Maak seker dat jy van die regterrand na die middel van die skerm swiep en laat los."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Jy het die \"gaan terug\"-gebaar voltooi. Nou kan jy leer hoe om na tuisskerm te gaan."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Maak seker dat jy nie te naby aan die onderkant van die skerm swiep nie."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Gaan na Instellings om sensitiwiteit van teruggebaar te verander"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Swiep om terug te gaan"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Swiep van die linker- of regterrand na die middel van die skerm om na die vorige skerm terug te gaan."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Maak seker dat jy van die onderrand van die skerm af opswiep."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Maak seker jy onderbreek nie voordat jy laat los nie."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Maak seker jy swiep reguit op."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Jy het die \"gaan na tuisskerm\"-gebaar voltooi. Nou kan jy leer hoe om tussen programme te wissel."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Swiep om na tuisskerm toe te gaan"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swiep op van die onderkant van jou skerm af. Hierdie gebaar neem jou altyd na die tuisskerm toe."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Maak seker dat jy van die onderrand van die skerm af opswiep."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Probeer om die venster langer te hou voordat jy laat los."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Maak seker dat jy reguit opswiep en dan onderbreek."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Jy het die \"wissel tussen programme\"-gebaar voltooi. Jy is gereed om jou foon te gebruik!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swiep om tussen programme te wissel"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Swiep op van die onderkant van jou skerm af, hou en laat los dan."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Gereed"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Volgende"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Klaar"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Instellings"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Probeer weer"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Mooi so!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutoriaal <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Deel"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skermkiekie"</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>
 </resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index bd54283..5a3df9d 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"የተከፈለ ማያ ገጽ"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"ሰካ"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ነጻ ቅጽ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"የመተግበሪያ አስተያየት ጥቆማዎች ነቅቷል"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"የመተግበሪያ አስተያየቶች ቦዝነዋል"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"የተገመተው መተግበሪያ፦ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ከግራ ጠርዝ ጥግ ጀምሮ ማንሸራተትዎን ያረጋግጡ።"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ከግራ ጠርዝ ወደ ማያ ገጹ መሃል ማንሸራተትዎን እና መልቀቅዎን ያረጋግጡ።"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"ይሄው ነው! አሁን ደግሞ ከግራ ጥግ ጠረግ ማንሸራተትን ይሞክሩ።"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ከቀኝ ጠርዝ ጥግ ጀምሮ ማንሸራተትዎን ያረጋግጡ።"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ከቀኝ ጠርዝ ወደ ማያ ገጹ መሃል ማንሸራተትዎን እና መልቀቅዎን ያረጋግጡ።"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"ወደኋላ የመመለስ ምልክትን አጠናቀዋል። ቀጥሎም ወደ ቤት እንዴት መሄድ እንደሚችሉ ይወቁ።"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ከማያ ገጹ ታችኛው ክፍል ጋር በጣም ጠጋ ብለው አለማንሸራተትዎን ያረጋግጡ።"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ከኋላ ስሜት ሰጭነት ደረጃ ለመለወጥ ወደ ቅንብሮች ይመለሱ"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"ወደኋላ ለመመለስ ያንሸራትቱ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ወደ መጨረሻው ማያ ገጽ ለመመለስ ከግራ ወይም ከቀኝ ጠርዝ ወደ ማያ ገጹ መሃል ያንሸራትቱ።"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ከማያ ገጹ የታችኛው ጫፍ ወደ ላይ ማንሸራተትዎን ያረጋግጡ።"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ከመልቀቅዎ በፊት ለአፍታ እንዳልቆሙ ያረጋግጡ።"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"በቀጥታ ወደ ላይ ማንሸራተትዎን ያረጋግጡ።"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"የመነሻ ምልክትን አጠናቀዋል። ቀጥሎም መተግበሪያዎችን እንዴት መቀየር እንደሚችሉ ይወቁ።"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ወደ መነሻ ለመሄድ ያንሸራትቱ"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ከእርስዎ ማያ ገጽ ግርጌ ላይ ወደ ላይ በጣት ጠረግ ያድርጉ። ይህ የእጅ ውዝዋዜ ሁልጊዜ ወደ መነሻ ማያ ገጽ ይወስድዎታል።"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ከማያ ገጹ የታችኛው ጫፍ ወደ ላይ ማንሸራተትዎን ያረጋግጡ።"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ከመልቀቅዎ በፊት መስኮቱን ረዘም ላለ ጊዜ ለመያዝ ይሞክሩ።"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"በቀጥታ ወደ ላይ ማንሸራተትዎን ያረጋግጡ፣ ከዚያ ለአፍታ ያቁሙ።"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"መተግበሪያዎችን የመቀያየር ምልክትን አጠናቀዋል። ስልክዎን ለመጠቀም ዝግጁ ነዎት!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"መተግበሪያዎችን ለመቀየር ያንሸራትቱ"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"ከእርስዎ ማያ ገጽ ግርጌ ላይ ወደ ላይ በጣት ጠረግ ያድርጉ፣ ይያዙ፣ በመቀጠል ይልቀቁ።"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ሁሉም ዝግጁ"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"ቀጣይ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"ተጠናቋል"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ቅንብሮች"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"እንደገና ሞክር"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ጥሩ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"አጋዥ ሥልጠና <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"አጋራ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ቅጽበታዊ ገጽ እይታ"</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>
 </resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index c2348ec..8205f89 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"تقسيم الشاشة"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"تثبيت"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"شكل مجاني"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"تم تفعيل ميزة \"التطبيقات المقترحة\"."</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ميزة \"التطبيقات المقترحة\" غير مفعّلة."</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"التطبيق المتوقع: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"تأكّد من التمرير سريعًا من أقصى الحافة اليمنى."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"تأكّد من التمرير سريعًا من الحافة اليمنى إلى وسط الشاشة ثم ارفع إصبعك."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"أكملت هذه الخطوة. جرِّب الآن التمرير سريعًا من الحافة اليسرى"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"تأكّد من التمرير سريعًا من أقصى الحافة اليسرى."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"تأكّد من التمرير سريعًا من الحافة اليسرى إلى وسط الشاشة ثم ارفع إصبعك."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"لقد أكملت التدريب على إيماءة الرجوع. تعرّف بعد ذلك على كيفية الانتقال إلى الشاشة الرئيسية."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"تأكّد من عدم التمرير بالقرب من أسفل الشاشة."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"لتغيير مستوى حساسية إيماءة الرجوع، انتقِل إلى \"الإعدادات\""</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"مرِّر سريعًا للرجوع."</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"للرجوع إلى الشاشة السابقة، مرِّر سريعًا من الحافة اليسرى أو الحافة اليمنى إلى وسط الشاشة."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"تأكّد من التمرير سريعًا من الحافة السفلى للشاشة إلى أعلاها."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"تأكّد من عدم التوقّف قليلاً قبل رفع إصبعك."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"تأكّد من التمرير إلى الأعلى مباشرةً."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"أكملت التدريب على إيماءة الانتقال إلى الشاشة الرئيسية. تعرّف بعد ذلك على كيفية التبديل بين التطبيقات."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"مرِّر سريعًا للانتقال إلى الشاشة الرئيسية"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"مرِّر سريعًا من أسفل الشاشة إلى أعلاها. تنقلك هذه الإيماءة دائمًا إلى الشاشة الرئيسية."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"تأكّد من التمرير سريعًا من الحافة السفلى للشاشة إلى أعلاها."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"حاوِل إبقاء إصبعك على النافذة لمدة أطول قبل رفعه."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"تأكّد من التمرير سريعًا للأعلى مباشرةً ثم التوقّف قليلاً."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"أكملت التدريب على إيماءة التبديل بين التطبيقات. أنت جاهز لاستخدام هاتفك."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"مرِّر سريعًا للتبديل بين التطبيقات"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"مرِّر سريعًا من أسفل الشاشة إلى أعلاها، وأبقِ إصبعك على الشاشة قليلاً ثم ارفعه."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"اكتمل التدريب على الإيماءة"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"التالي"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"تم"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"الإعدادات"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"إعادة المحاولة"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"أحسنت"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"الدليل التوجيهي <xliff:g id="CURRENT">%1$d</xliff:g> من إجمالي <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"مشاركة"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"لقطة شاشة"</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>
 </resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index ff0dc20..9b22c62 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"বিভাজিত স্ক্ৰীণ"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"পিন"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"কোনো শেহতীয়া বস্তু নাই"</string>
@@ -31,7 +30,7 @@
     <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="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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"আপোনাৰ সকলোতকৈ বেছিকৈ ব্যৱহৃত এপ্‌সমূহ গৃহ স্ক্ৰীনতে সহজে এক্সেছ কৰক। আপোনাৰ ৰুটিনসমূহৰ ভিত্তিত পৰামর্শসমূহ সলনি হ\'ব। একেবাৰে তলৰ শাৰীটোত থকা এপ্‌সমূহ ওপৰৰ আপোনাৰ গৃহ স্ক্ৰীনলৈ যাব।"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"এপৰ পৰামৰ্শসমূহ সক্ষম কৰা আছে"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"এপৰ পৰামৰ্শসমূহ অক্ষম কৰা আছে"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"পূৰ্বানুমান কৰা এপ্: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"আপুনি বাওঁ প্ৰান্তৰৰ একেবাৰে সীমাৰ পৰা ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"আপুনি স্ক্ৰীনৰ বাওঁ প্ৰান্তৰৰ পৰা সোঁ প্ৰান্তৰৰ ফালে মধ্যভাগলৈকে ছোৱাইপ কৰি এৰি দিয়াটো নিশ্চিত কৰক।"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"ইমানেই! এতিয়া বাওঁ প্ৰান্তৰৰ পৰা ছোৱাইপ কৰি চাওক।"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"আপুনি সোঁ প্ৰান্তৰৰ একেবাৰে সীমাৰ পৰা ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"আপুনি স্ক্ৰীনৰ সোঁ প্ৰান্তৰৰ পৰা মধ্যভাগলৈকে ছোৱাইপ কৰি এৰি দিয়াটো নিশ্চিত কৰক।"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"আপুনি উভতি যাওক নিৰ্দেশটো সম্পূৰ্ণ কৰিলে। ইয়াৰ পাছত, গৃহ স্ক্ৰীনলৈ কেনেকৈ যাব সেয়া জানক।"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"আপুনি স্ক্ৰীনৰ তলৰ অংশৰ বেছি ওচৰলৈ ছোৱাইপ নকৰাটো নিশ্চিত কৰক।"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"উভতি যোৱাৰ নির্দেশটোৰ সংবেদনশীলতা সলনি কৰিবলৈ ছেটিঙলৈ যাওক"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"উভতি যাবলৈ ছোৱাইপ কৰক"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"অন্তিম স্ক্ৰীনখনলৈ উভতি যাবলৈ বাওঁ অথবা সোঁ প্ৰান্তৰৰ পৰা মাজলৈ ছোৱাইপ কৰক।"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"আপুনি স্ক্ৰীনৰ তলৰ প্ৰান্তৰ পৰা ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"আপুনি এৰি দিয়াৰ পূৰ্বে অলপো নোৰোৱাটো নিশ্চিত কৰক।"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"আপুনি পোনকৈ ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে। ইয়াৰ পাছত, এপ্‌ কেনেকৈ সলনি কৰিব সেয়া জানক।"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"গৃহ স্ক্ৰীনলৈ যাবলৈ ছোৱাইপ কৰক"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"আপোনাৰ স্ক্ৰীনৰ তলৰ অংশৰ পৰা ওপৰলৈ ছোৱাইপ কৰক। এই নিৰ্দেশটোৱে আপোনাক সদায় গৃহ স্ক্ৰীনলৈ লৈ যায়।"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"আপুনি স্ক্ৰীনৰ তলৰ প্ৰান্তৰ পৰা ওপৰলৈ ছোৱাইপ কৰাটো নিশ্চিত কৰক।"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"এৰি দিয়াৰ পূৰ্বে ৱিণ্ডখন দীৰ্ঘ সময়ৰ বাবে ধৰি ৰাখিবলৈ চেষ্টা কৰক।"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"আপুনি স্ক্ৰীনৰ ওপৰলৈ পোনকৈ ছোৱাইপ কৰি তাৰ পাছত ৰোৱাটো নিশ্চিত কৰক।"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"আপুনি এপ্‌ সলনি কৰাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে। আপুনি নিজৰ ফ’নটো ব্যৱহাৰ কৰিবলৈ সাজু!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"এপ্‌ সলনি কৰিবলৈ ছোৱাইপ কৰক"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"আপোনাৰ স্ক্ৰীনৰ একেবাৰে তলৰ অংশৰ পৰা ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক আৰু তাৰ পাছত এৰি দিয়ক।"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"সম্পূৰ্ণ সাজু"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"পৰৱৰ্তী"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"হ’ল"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ছেটিং"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"পুনৰ চেষ্টা কৰক"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"সুন্দৰ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"টিউট’ৰিয়েল <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"শ্বেয়াৰ কৰক"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"স্ক্ৰীনশ্বট"</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>
 </resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 6e169b4..bd88392 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Bölünmüş ekran"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Sancın"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Sərbəst rejim"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Son elementlər yoxdur"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Tətbiq təklifləri aktivdir"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Tətbiq təklifləri deaktivdir"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Proqnozlaşdırılan tətbiq: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Ən sol tərəfdən sürüşdürdüyünüzə əmin olun."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Ekranın sol kənarından ortasına sürüşdürüb buraxın."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Bu qədər! İndi isə sağ kənardan sürüşdürün."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Ən sağ tərəfdən sürüşdürdüyünüzə əmin olun."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Ekranın sağ kənarından ortasına sürüşdürüb buraxın."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Geri getmə jestini tamamladınız. Sonra Əsas səhifəyə keçməyi öyrənin."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Ekranın altına çox yaxın sürüşdürmədiyinizə əmin olun."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Geri qayıtma jestinin həssaslığını dəyişmək üçün Ayarlara keçin"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Geri qayıtmaq üçün sürüşdürün"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Sonuncu ekrana qayıtmaq üçün ekranın sol, yaxud sağ kənarından mərkəzinə doğru sürüşdürün."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Ekranın aşağı kənarından yuxarı sürüşdürdüyünüzə əmin olun."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Buraxmazdan əvvəl durdurmadığınıza əmin olun."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Birbaşa yuxarı sürüşdürdüyünüzə əmin olun."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Əsas səhifəyə keçmə jestini tamamladınız. Sonra tətbiqləri keçirməyi öyrənin."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Əsas səhifəyə keçmək üçün sürüşdürün"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Ekranın aşağısından yuxarısına sürüşdürün. Bu jest həmişə Əsas səhifəyə aparır."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Ekranın aşağı kənarından yuxarı sürüşdürdüyünüzə əmin olun."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Buraxmadan əvvəl pəncərəni daha uzun müddət saxlamağa çalışın."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Birbaşa yuxarı sürüşdürdüyünüzə, sonra durdurduğunuza əmin olun."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Tətbiqləri keçirmə jestini tamamladınız. Telefonunuzu istifadə etməyə hazırsınız!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Tətbiqi keçirmək üçün sürüşdürün"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Ekranın aşağısından yuxarı doğru sürüşdürüb saxlayın, sonra buraxın."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Tam hazır"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Sonra"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Oldu"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ayarlar"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Yenə sınayın"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Əla!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Dərslik <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Paylaşın"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekran şəkli"</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>
 </resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 4d12c9b..21295fc 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Podeljeni ekran"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodni oblik"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
@@ -36,7 +35,7 @@
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Dobijajte predloge aplikacija u redu sa omiljenim stavkama na početnom ekranu"</string>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Lako pristupajte aplikacijama koje najčešće koristite direktno sa početnog ekrana. Predlozi se menjaju na osnovu upotrebe. Aplikacije iz donjeg reda se premeštaju nagore na početni ekran."</string>
     <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Lako pristupajte aplikacijama koje najčešće koristite direktno sa početnog ekrana. Predlozi se menjaju na osnovu vaših rutina. Aplikacije iz reda sa omiljenim stavkama se premeštaju na početni ekran."</string>
-    <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Lako pristupajte aplikacijama koje najčešće koristite direktno sa početnog ekrana. Predlozi se menjaju na osnovu upotrebe. Aplikacije iz donjeg reda se premeštaju u nov direktorijum."</string>
+    <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Lako pristupajte aplikacijama koje najčešće koristite direktno sa početnog ekrana. Predlozi se menjaju na osnovu upotrebe. Aplikacije iz donjeg reda se premeštaju u nov folder."</string>
     <string name="hotseat_edu_accept" msgid="1611544083278999837">"Prikazuj predloge aplikacija"</string>
     <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ne, hvala"</string>
     <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Podešavanja"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlozi aplikacija su omogućeni"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlozi aplikacija su onemogućeni"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđamo aplikaciju: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Obavezno prevucite od same leve ivice."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Obavezno prevucite od leve ivice do sredine ekrana i otpustite."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"To je sve! Sada probajte da prevučete od desne ivice."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Obavezno prevucite od same desne ivice."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Obavezno prevucite od desne ivice do sredine ekrana i otpustite."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Dovršili ste pokret za povratak. Sada saznajte kako da odete na početnu stranicu."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nikako ne prevlačite previše blizu dna ekrana."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Osetljivost pok. za nazad možete da promenite u Podešavanjima"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Prevucite da biste se vratili"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Da biste se vratili na poslednji ekran, prevucite od leve ili desne ivice do sredine ekrana."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Obavezno prevucite nagore od donje ivice ekrana."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Nikako ne stajte pre otpuštanja."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Obavezno prevucite pravo nagore."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Dovršili ste pokret za povratak na početnu stranicu. Sada saznajte kako da promenite aplikacije."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Prevucite da biste otišli na početnu stranicu"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Prevucite nagore od dna ekrana. Ovaj pokret vas uvek vodi na početni ekran."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Obavezno prevucite nagore od donje ivice ekrana."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Probajte da držite prozor duže pre otpuštanja."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Obavezno prevucite pravo nagore, pa zastanite."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Dovršili ste pokret za promenu aplikacija. Spremni ste za korišćenje telefona!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prevucite da biste promenili aplikacije"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Prevucite nagore od dna ekrana, zadržite, pa pustite."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"To je to"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Dalje"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Gotovo"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Podešavanja"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Probajte ponovo"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Svaka čast!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Vodič <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Deli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</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>
 </resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index b3f5e52..d31468c 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Падзяліць экран"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Замацаваць"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Адвольная форма"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Няма новых элементаў"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Прапановы праграм уключаны"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Прапановы праграм выключаны"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Праграма з падказкі: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Пераканайцеся, што вы не праводзіце пальцам занадта далёка ад левага вугла экрана."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Пераканайцеся, што праводзіце пальцам з левага вугла ў цэнтр экрана, а потым адпускаеце."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Гатова! Цяпер правядзіце пальцам ад правага краю."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Пераканайцеся, што вы не праводзіце пальцам занадта далёка ад правага вугла экрана."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Пераканайцеся, што праводзіце пальцам з правага вугла ў цэнтр экрана, а потым адпускаеце."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Вы навучыліся рабіць жэст вяртання. А зараз даведайцеся, як перайсці на галоўны экран."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Пераканайцеся, што вы не праводзіце пальцам занадта блізка да ніжняга краю экрана."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Каб змяніць адчувальнасць жэста вяртання, адкрыйце налады"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Правядзіце пальцам, каб вярнуцца"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Каб вярнуцца на папярэдні экран, правядзіце пальцам ад левага ці правага краю да цэнтра экрана."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Пераканайцеся, што праводзіце пальцам па экране знізу ўверх."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Пераканайцеся, што не затрымліваецеся перад адпусканнем."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Пераканайцеся, што праводзіце пальцам вертыкальна."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Вы навучыліся рабіць жэст пераходу на галоўны экран. А зараз даведайцеся, як пераключацца паміж праграмамі."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Правесці пальцам для пераходу на галоўны экран"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Правядзіце пальцам па экране знізу ўверх. Гэты жэст дазваляе вярнуцца на Галоўны экран."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Пераканайцеся, што праводзіце пальцам па экране знізу ўверх."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Перш чым адпусціць палец, паспрабуйце даўжэй утрымліваць акно націснутым."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Пераканайцеся, што праводзіце пальцам вертыкальна, а потым затрымліваеце яго."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Вы навучыліся рабіць жэст пераключэння паміж праграмамі. Цяпер вы можаце выкарыстоўваць тэлефон!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Правесці пальцам для пераключэння паміж праграмамі"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Правядзіце па экране знізу ўверх, утрымліваючы палец націснутым, потым адпусціце."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Гатова"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Далей"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Гатова"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Налады"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Паўтарыць спробу"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Выдатна!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Дапаможнік <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Абагуліць"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Здымак экрана"</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>
 </resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 5484bc9..d2554d5 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Разделен екран"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Фиксиране"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Свободна форма"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Няма скорошни елементи"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предложенията за приложения са активирани"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Функцията „Предложения за приложения“ е деактивирана"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвидено приложение: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Трябва да прекарате пръст от най-лявата част на екрана."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Трябва да прекарате пръст от левия край на екрана до средата, след което да вдигнете пръста си."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Това е всичко! Сега прекарайте пръст от десния край."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Трябва да прекарате пръст от най-дясната част на екрана."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Трябва да прекарате пръст от десния край на екрана до средата, след което да вдигнете пръста си."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Изпълнихте жеста за връщане назад. В следващия урок ще научите как се преминава към началния екран."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Не прекарвайте пръст твърде близо до долната част на екрана."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Променете чувств. на жеста за връщане назад от настройките"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Прекарване на пръст за връщане назад"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"За да се върнете на предишния екран, прекарайте пръст от левия или десния край на екрана до средата."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Трябва да прекарате пръст нагоре от долния край на екрана."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Не задържайте, преди да вдигнете пръста си."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Трябва да прекарате пръст право нагоре."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Изпълнихте жеста за преминаване към началния екран. В следващия урок ще научите как се превключва между приложенията."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Прекарване на пръст за преминаване към началния екран"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Прекарайте пръст нагоре от долната част на екрана. Този жест винаги ще ви отвежда до началния екран."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Трябва да прекарате пръст нагоре от долния край на екрана."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Задръжте прозореца по-дълго, преди да вдигнете пръста си."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Прекарайте пръст право нагоре, след което задръжте."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Изпълнихте жеста за превключване между приложенията. Вече можете да използвате телефона си!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Прекарване на пръст за превключване между приложенията"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Прекарайте пръст нагоре от долната част на екрана, задръжте и след това вдигнете пръста си."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Готово"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Напред"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Готово"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Настройки"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Опитайте отново"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Чудесно!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Урок <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Споделяне"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Екранна снимка"</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>
 </resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 9212a4b..48d2690 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"স্ক্রিন স্প্লিট করুন"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"পিন করুন"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ফ্রি-ফর্ম"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"কোনো সাম্প্রতিক আইটেম নেই"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"অ্যাপ সাজেশন চালু করা আছে"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"অ্যাপ সাজেশন বন্ধ করা আছে"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"একদম বাঁ প্রান্ত থেকে সোয়াইপ করছেন কিনা ভাল করে দেখে নিন।"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"আপনি বাঁ প্রান্ত থেকে স্ক্রিনের মাঝখান পর্যন্ত সোয়াইপ করছেন ও পরে আঙুল তুলে নিন। এটির খেয়াল রাখুন।"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"এটুকুই যথেষ্ট! এখন ডান প্রান্ত থেকে সোয়াইপের চেষ্টা করুন।"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"একদম ডান প্রান্ত থেকে সোয়াইপ করছেন কিনা ভাল করে দেখে নিন।"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"আপনি ডান প্রান্ত থেকে স্ক্রিনের মাঝখান পর্যন্ত সোয়াইপ করছেন ও পরে আঙুল তুলে নিন। এটি খেয়াল রাখুন।"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"আপনি ফিরে যাওয়ার জেসচার সম্পর্কে জেনেছেন। এরপর, হোম স্ক্রিনে কীভাবে যাবেন জেনে নিন।"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"স্ক্রিনের নিচের প্রান্তের খুব কাছে পর্যন্ত যাতে সোয়াইপ না করেন সেটি ভাল করে দেখে নিন।"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ফিরে যাওয়ার জেসচারের সেন্সিটিভিটি পরিবর্তন করতে, সেটিংসে যান"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"ফিরে যেতে সোয়াইপ করুন"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"শেষের স্ক্রিনে ফিরে যেতে, ডান বা বাঁ প্রান্ত থেকে স্ক্রিনের মাঝখান পর্যন্ত সোয়াইপ করুন।"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"স্ক্রিনের নিচের প্রান্ত থেকে আপনি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন।"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"আঙুল তুলে নেওয়ার আগে আপনি যাতে পজ না করেন সেটি ভাল করে দেখে নিন।"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"আপনি উপরের দিকে সোজাসুজি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন।"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"আপনি হোম স্ক্রিনে যাওয়ার জেসচার সম্পর্কে জেনেছেন। এরপর, একটি অ্যাপ থেকে অন্য অ্যাপে কীভাবে যাবেন জেনে নিন।"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"হোম স্ক্রিনে যেতে সোয়াইপ করুন"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"স্ক্রিনের নিচের প্রান্ত থেকে উপরের দিকে সোয়াইপ করুন। এটি করলে, আপনি সবসময় হোম স্ক্রিনে যেতে পারবেন।"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"স্ক্রিনের নিচের প্রান্ত থেকে আপনি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিন।"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"চেষ্টা করুন যাতে আঙুল সরিয়ে নেওয়ার আগে উইন্ডো কিছুক্ষণ প্রেস করে রাখা যায়।"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"আপনি উপরের দিকে সোজাসুজি সোয়াইপ করেছেন কিনা ভাল করে দেখে নিয়ে তারপর পজ করুন।"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"আপনি একটি অ্যাপ থেকে অন্য অ্যাপে যাওয়ার জেসচার সম্পর্কে জেনেছেন। আপনি এখন আপনার ফোন ব্যবহার করার জন্য প্রস্তুত!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"একটি অ্যাপ থেকে অন্য অ্যাপে যেতে সোয়াইপ করুন"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"স্ক্রিনের নিচ থেকে উপরের দিকে সোয়াইপ করে ধরে থাকুন, তারপর ছেড়ে দিন।"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"সব প্রস্তুত"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"পরবর্তী"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"হয়ে গেছে"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"সেটিংস"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"আবার চেষ্টা করুন"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"সাবাস!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"টিউটোরিয়াল <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"শেয়ার করুন"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"স্ক্রিনশট নিন"</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>
 </resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 6b513b7..18494a9 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Način rada podijeljenog ekrana"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodan oblik"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Prijedlozi aplikacija su omogućeni"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Prijedlozi aplikacija su onemogućeni"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Obratite pažnju da prevučete s krajnjeg lijevog ruba."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Obratite pažnju da prevučete s lijevog ruba prema sredini ekrana, a zatim pustite."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"To je to! Sada pokušajte prevući s desnog ruba."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Obratite pažnju da prevučete s krajnjeg desnog ruba."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Obratite pažnju da prevučete s desnog ruba prema sredini ekrana, a zatim pustite."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Završili ste pokret za vraćanje. Sljedeće naučite kako otvoriti Početni ekran."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Obratite pažnju da ne prevučete preblizu donjem dijelu ekrana."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Promijenite osjetljivost pokreta za povratak u Postavkama"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Prevucite da se vratite"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Da se vratite na posljednji ekran, prevucite s lijevog ili desnog ruba prema sredini ekrana."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Obratite pažnju da prevučete prema gore s donjeg ruba ekrana."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Obratite pažnju da ne zastanete prije puštanja."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Obratite pažnju da prevučete ravno prema gore."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Završili ste pokret za otvaranje Početnog ekrana. Sljedeće naučite kako prebacivati između aplikacija."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Prevucite da odete na početni ekran"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Prevucite s dna ekrana prema gore. Tim pokretom uvijek idete na Početni ekran."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Obratite pažnju da prevučete prema gore s donjeg ruba ekrana."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Pokušajte zadržati prozor duže prije puštanja."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Obratite pažnju da prevučete ravno prema gore, a zatim zastanite."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Završili ste pokret za prebacivanje između aplikacija. Spremni ste da koristite telefon!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prevucite da prebacujete između aplikacija"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Prevucite s dna ekrana prema gore, zadržite, a zatim pustite."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Sve je spremno"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Naprijed"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Gotovo"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Postavke"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Pokušaj ponovo"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Lijepo!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Vodič <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Dijeli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</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>
 </resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 4b0acca..7c417ef 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Pantalla dividida"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixa"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Format lliure"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No hi ha cap element recent"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Els suggeriments d\'aplicacions estan activats"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Els suggeriments d\'aplicacions estan desactivats"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicció d\'aplicació: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Assegura\'t de lliscar des de l\'extrem esquerre de la pantalla."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Assegura\'t de lliscar des de la vora esquerra cap al centre de la pantalla i deixa anar."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Ja està! Ara, prova de lliscar des de la vora dreta."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Assegura\'t de lliscar des de l\'extrem dret de la pantalla."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Assegura\'t de lliscar des de la vora dreta cap al centre de la pantalla i deixa anar."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Has completat el gest per anar enrere. Ara, descobreix com pots anar a la pantalla d\'inici."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Assegura\'t de no lliscar massa a prop de la part inferior de la pantalla."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Per canviar la sensibilitat del gest, ves a Configuració"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Llisca per anar enrere"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Per tornar a la darrera pantalla, llisca des de la vora esquerra o dreta cap al centre de la pantalla."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Assegura\'t de lliscar des de la vora inferior de la pantalla."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Assegura\'t de no aturar-te abans de deixar anar."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Assegura\'t de lliscar directament cap amunt."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Has completat el gest per anar a la pantalla d\'inici. Ara, descobreix com pots canviar d\'aplicació."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Llisca per anar a la pantalla d\'inici"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Llisca cap amunt des de la part inferior de la pantalla. Aquest gest et porta a la pantalla d\'inici."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Assegura\'t de lliscar des de la vora inferior de la pantalla."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prova de mantenir premuda la finestra durant més temps abans de deixar anar."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Assegura\'t de lliscar directament cap amunt i després aturar-te."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Has completat el gest per canviar d\'aplicació. Ja ho tens tot a punt per utilitzar el telèfon."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Llisca per canviar d\'aplicació"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Llisca cap amunt des de la part inferior de la pantalla, mantén premut i deixa anar."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Tot a punt"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Següent"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Fet"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Configuració"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Torna-ho a provar"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Molt bé!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Comparteix"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"L\'aplicació o la teva organització no permeten aquesta acció"</string>
+    <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Vols ometre el tutorial de navegació?"</string>
+    <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Pots trobar-ho 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>
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 2cff6eb..9d1b94e 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -19,8 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Rozdělená obrazovka"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"PIN"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"Připnout"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Neomezený režim"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Žádné nedávné položky"</string>
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavení využití aplikací"</string>
@@ -31,7 +30,7 @@
     <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">"Vaše předpovídané 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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Mějte nejpoužívanější aplikace k dispozici přímo na ploše. Návrhy se budou měnit v závislosti na sledech činností. Aplikace ve spodním řádku se přesunou na vaši plochu."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Návrhy aplikací jsou povoleny"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Návrhy aplikací jsou zakázány"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Předpokládaná aplikace: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Přejeďte prstem z úplného levého okraje obrazovky."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Přejeďte prstem z levého okraje doprostřed obrazovky a zdvihněte prst."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"A je to! Teď zkuste přejet prstem z pravého okraje."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Přejeďte prstem z úplného pravého okraje obrazovky."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Přejeďte prstem z pravého okraje doprostřed obrazovky a zdvihněte prst."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Dokončili jste gesto pro přechod zpět. Teď se naučíme přejít na plochu."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Dejte pozor, abyste prstem nepřejížděli moc blízko ke spodnímu okraji obrazovky."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Citlivost gesta pro přechod zpět můžete změnit v Nastavení"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Přejetím prstem se vrátíte zpět"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"K návratu na poslední obrazovku přejeďte prstem z levého nebo pravého okraje obrazovky doprostřed."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Přejeďte prstem nahoru z dolního okraje obrazovky."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Před zdvihnutím prstu nedělejte pauzu."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Přejeďte prstem přímo nahoru."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Dokončili jste gesto pro přechod na plochu. Teď se naučíme přepínat aplikace."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Přechod na plochu přejetím prstem"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Přejeďte prstem ze spodní části obrazovky nahoru. Tímto gestem se vždy dostanete na plochu."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Přejeďte prstem nahoru z dolního okraje obrazovky."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Zkuste podržet okno delší dobu, než ho uvolníte."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Přejeďte prstem přímo nahoru a pak udělejte pauzu."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Dokončili jste gesto pro přepínání aplikací. Jste připraveni používat telefon."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Přepínání aplikací přejetím prstem"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Přejeďte prstem nahoru z dolního okraje obrazovky, podržte obrazovku a potom prst uvolněte."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Vše je nastaveno"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Další"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Hotovo"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Nastavení"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Zkusit znovu"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Skvělé!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Výukový program <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Sdílet"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snímek 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>
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 2cd265c..42098dc 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Opdel skærm"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fastgør"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Frit format"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appforslag er aktiveret"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appforslag er deaktiveret"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App, du forventes at skulle bruge: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Stryg fra kanten yderst til venstre."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Stryg fra venstre kant mod midten af skærmen, og løft fingeren."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Det var det. Nu kan du prøve at stryge fra højre kant."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Stryg fra kanten yderst til højre."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Stryg fra højre kant mod midten af skærmen, og løft fingeren."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Du har fuldført bevægelsen for Gå tilbage. Som det næste kan du se, hvordan du går til startskærmen."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Undgå at stryge for tæt på bunden af skærmen."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Juster følsomheden for bevægelsen Gå tilbage i Indstillinger"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Stryg for at gå tilbage"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Stryg mod midten af skærmen fra venstre eller højre kant for at gå tilbage til den seneste skærm."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Stryg opad fra bunden af skærmen."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Undlad at holde fingeren stille, indtil du løfter fingeren."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Stryg lige opad."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Du har fuldført bevægelsen for Gå til start. Som det næste kan du se, hvordan du skifter app."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Stryg for at gå til startskærmen"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Stryg opad fra bunden af skærmen. Denne bevægelse åbner altid startskærmen."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Stryg opad fra bunden af skærmen."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prøv at holde fingeren nede på vinduet i længere tid, inden du løfter den."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Stryg lige opad, og hold derefter fingeren stille."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Du har fuldført bevægelsen for Skift app. Du er klar til at bruge din telefon."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Stryg for at skifte app"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Stryg opad fra bunden af skærmen, hold fingeren stille, og løft den."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Så er du klar"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Næste"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Luk"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Indstillinger"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Prøv igen"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Sådan!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Selvstudie <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Del"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index b51f04d..e7d9523 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Splitscreen"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixieren"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform-Modus"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Funktion \"App-Vorschläge\" aktiviert"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Funktion \"App-Vorschläge\" deaktiviert"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Vorgeschlagene App: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Wische vom äußersten linken Bildschirmrand."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Wische vom linken Bildschirmrand zur Bildschirmmitte und lass los."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Gut gemacht. Versuch es jetzt mal vom rechten Rand aus."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Wische vom äußersten rechten Bildschirmrand."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Wische vom rechten Bildschirmrand zur Bildschirmmitte und lass los."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Du hast die „Zurück“-Touch-Geste abgeschlossen. Nun lernst du, wie du zum Startbildschirm gelangst."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Wische nicht zu nah am unteren Bildschirmrand."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Du kannst die Empfindlichkeit von „Zurück“ in den Einstellungen ändern"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Zum Zurückgehen wischen"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Wenn du zum letzten Bildschirm zurückgehen möchtest, wische vom linken oder rechten Rand zur Mitte."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Wische vom unteren Bildschirmrand nach oben."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Achte darauf, nicht innezuhalten, bevor du loslässt."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Wische gerade nach oben."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Du hast die „Startbildschirm“-Touch-Geste abgeschlossen. Als Nächstes lernst du, wie du zwischen Apps wechseln kannst."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Zum Startbildschirm gehen"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Wenn du zum Startbildschirm gehen möchtest, wische einfach vom unteren Bildschirmrand nach oben."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Wische vom unteren Bildschirmrand nach oben."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Versuche, das Fenster länger festzuhalten, bevor du es loslässt."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Wische gerade nach oben und halte dann inne."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Du hast die „Apps wechseln“-Touch-Geste abgeschlossen. Nun bist du bereit, dein Smartphone zu verwenden!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Zwischen Apps wechseln"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Wische auf dem Bildschirm von unten nach oben, halte ihn gedrückt und lass ihn dann los."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Fertig"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Weiter"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Fertig"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Einstellungen"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Wiederholen"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Sehr gut!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Anleitung <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Teilen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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">"Das findest du später 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>
 </resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 015ec22..21c2984 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Διαχωρισμός οθόνης"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Καρφίτσωμα"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Ελεύθερη μορφή"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Οι προτεινόμενες εφαρμογές ενεργοποιήθηκαν"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Οι προτεινόμενες εφαρμογές είναι απενεργοποιημένες"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Εφαρμογή από πρόβλεψη: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Φροντίστε να σύρετε από το άκρο της αριστερής πλευράς."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Φροντίστε να σύρετε από το αριστερό άκρο προς το μέσο της οθόνης και απομακρύνετε το δάχτυλό σας."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Αυτό ήταν! Δοκιμάστε τώρα να σύρετε από το δεξί άκρο."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Φροντίστε να σύρετε από το άκρο της δεξιάς πλευράς."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Φροντίστε να σύρετε από το δεξί άκρο προς το μέσο της οθόνης και απομακρύνετε το δάχτυλό σας."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Ολοκληρώσατε την κίνηση επιστροφής. Στη συνέχεια, μάθετε πώς να μεταβείτε στην αρχική σελίδα."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Φροντίστε να μην σύρετε υπερβολικά κοντά στο κάτω μέρος της οθόνης."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Μεταβείτε στις Ρυθμίσεις για αλλαγή ευαισθ. κίνησης επιστρ."</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Σύρετε για επιστροφή"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Για να επιστρέψετε στην τελευτ. οθόνη, σύρετε από το αριστ. ή το δεξί άκρο προς το μέσο της οθόνης."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Φροντίστε να σύρετε προς τα επάνω από το κάτω άκρο της οθόνης."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Φροντίστε να μην κάνετε παύση προτού απομακρύνετε τα δάχτυλά σας."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Φροντίστε να σύρετε απευθείας προς τα επάνω."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη. Στη συνέχεια, μάθετε πώς να κάνετε εναλλαγή εφαρμογών."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Σύρετε για μετάβαση στην αρχική οθόνη"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Σύρετε προς τα πάνω από το κάτω μέρος της οθόνης. Αυτή η κίνηση σάς μεταφέρει πάντα στην αρχ. οθόνη."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Φροντίστε να σύρετε προς τα επάνω από το κάτω άκρο της οθόνης."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Δοκιμάστε να κρατήσετε περισσότερο το παράθυρο προτού απελευθερώσετε."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Φροντίστε να σύρετε απευθείας προς τα επάνω και έπειτα κάντε παύση."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Ολοκληρώσατε την κίνηση εναλλαγής εφαρμογών. Είστε έτοιμοι να χρησιμοποιήσετε το τηλέφωνό σας!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Σύρετε για εναλλαγή εφαρμογών"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Σύρετε προς τα πάνω από το κάτω μέρος της οθόνης σας, κρατήστε παρατεταμένα και έπειτα ελευθερώστε."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Όλα είναι έτοιμα"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Επόμενο"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Τέλος"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ρυθμίσεις"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Δοκιμάστε ξανά"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Ωραία!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Οδηγός <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Κοινοποίηση"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Στιγμιότυπο οθόνης"</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>
 </resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index fa60ca8..487d6cc 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Make sure that you swipe from the far-left edge."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Make sure that you swipe from the left edge to the middle of the screen and let go."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"That\'s it! Now try swiping from the right edge."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Make sure that you swipe from the far-right edge."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Make sure that you swipe from the right edge to the middle of the screen and let go."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"You completed the go back gesture. Next up, learn how to go Home."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure that you don\'t swipe too close to the bottom of the screen."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change sensitivity of the back gesture, go to Settings"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure that you swipe up from the bottom edge of the screen."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure that you don\'t pause before letting go."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure that you swipe straight up."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"You completed the go home gesture. Next up, learn how to switch apps."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the home screen."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure that you swipe up from the bottom edge of the screen."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure that you swipe straight up, then pause."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"You completed the switch apps gesture. You\'re ready to use your phone!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Swipe up from the bottom of your screen, hold, then release."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Next"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Done"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Try again"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Nice!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
index fa60ca8..487d6cc 100644
--- a/quickstep/res/values-en-rCA/strings.xml
+++ b/quickstep/res/values-en-rCA/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Make sure that you swipe from the far-left edge."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Make sure that you swipe from the left edge to the middle of the screen and let go."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"That\'s it! Now try swiping from the right edge."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Make sure that you swipe from the far-right edge."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Make sure that you swipe from the right edge to the middle of the screen and let go."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"You completed the go back gesture. Next up, learn how to go Home."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure that you don\'t swipe too close to the bottom of the screen."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change sensitivity of the back gesture, go to Settings"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure that you swipe up from the bottom edge of the screen."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure that you don\'t pause before letting go."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure that you swipe straight up."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"You completed the go home gesture. Next up, learn how to switch apps."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the home screen."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure that you swipe up from the bottom edge of the screen."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure that you swipe straight up, then pause."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"You completed the switch apps gesture. You\'re ready to use your phone!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Swipe up from the bottom of your screen, hold, then release."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Next"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Done"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Try again"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Nice!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index fa60ca8..487d6cc 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Make sure that you swipe from the far-left edge."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Make sure that you swipe from the left edge to the middle of the screen and let go."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"That\'s it! Now try swiping from the right edge."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Make sure that you swipe from the far-right edge."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Make sure that you swipe from the right edge to the middle of the screen and let go."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"You completed the go back gesture. Next up, learn how to go Home."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure that you don\'t swipe too close to the bottom of the screen."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change sensitivity of the back gesture, go to Settings"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure that you swipe up from the bottom edge of the screen."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure that you don\'t pause before letting go."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure that you swipe straight up."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"You completed the go home gesture. Next up, learn how to switch apps."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the home screen."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure that you swipe up from the bottom edge of the screen."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure that you swipe straight up, then pause."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"You completed the switch apps gesture. You\'re ready to use your phone!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Swipe up from the bottom of your screen, hold, then release."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Next"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Done"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Try again"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Nice!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index fa60ca8..487d6cc 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App suggestions enabled"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App suggestions are disabled"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicted app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Make sure that you swipe from the far-left edge."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Make sure that you swipe from the left edge to the middle of the screen and let go."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"That\'s it! Now try swiping from the right edge."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Make sure that you swipe from the far-right edge."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Make sure that you swipe from the right edge to the middle of the screen and let go."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"You completed the go back gesture. Next up, learn how to go Home."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Make sure that you don\'t swipe too close to the bottom of the screen."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"To change sensitivity of the back gesture, go to Settings"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe to go back"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"To go back to the last screen, swipe from the left or right edge to the middle of the screen."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Make sure that you swipe up from the bottom edge of the screen."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Make sure that you don\'t pause before letting go."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Make sure that you swipe straight up."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"You completed the go home gesture. Next up, learn how to switch apps."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe to go home"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe up from the bottom of your screen. This gesture always takes you to the home screen."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Make sure that you swipe up from the bottom edge of the screen."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Try holding the window for longer before releasing."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Make sure that you swipe straight up, then pause."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"You completed the switch apps gesture. You\'re ready to use your phone!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe to switch apps"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Swipe up from the bottom of your screen, hold, then release."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"All set"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Next"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Done"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Settings"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Try again"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Nice!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Share"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
index d7008ca..36df9d1 100644
--- a/quickstep/res/values-en-rXC/strings.xml
+++ b/quickstep/res/values-en-rXC/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‎Split screen‎‏‎‎‏‎"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎Pin‎‏‎‎‏‎"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‏‏‎Freeform‎‏‎‎‏‎"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎No recent items‎‏‎‎‏‎"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‎‏‎‎App suggestions enabled‎‏‎‎‏‎"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎App suggestions are disabled‎‏‎‎‏‎"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‏‏‎Predicted app: ‎‏‎‎‏‏‎<xliff:g id="TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎Make sure you swipe from the far-left edge.‎‏‎‎‏‎"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎Make sure you swipe from the left edge to the middle of the screen and let go.‎‏‎‎‏‎"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‎That\'s it! Now try swiping from the right edge.‎‏‎‎‏‎"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎Make sure you swipe from the far-right edge.‎‏‎‎‏‎"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎Make sure you swipe from the right edge to the middle of the screen and let go.‎‏‎‎‏‎"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎You completed the go back gesture. Next up, learn how to go Home.‎‏‎‎‏‎"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‎Make sure you don\'t swipe too close to the bottom of the screen.‎‏‎‎‏‎"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎To change the sensitivity of the back gesture, go to Settings‎‏‎‎‏‎"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‎Swipe to go back‎‏‎‎‏‎"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‏‎‏‎‏‏‎‏‎To go back to the last screen, swipe from the left or right edge to the middle of the screen.‎‏‎‎‏‎"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎Make sure you swipe up from the bottom edge of the screen.‎‏‎‎‏‎"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎Make sure you don\'t pause before letting go.‎‏‎‎‏‎"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎Make sure you swipe straight up.‎‏‎‎‏‎"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‎You completed the go Home gesture. Next up, learn how to switch apps.‎‏‎‎‏‎"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎Swipe to go home‎‏‎‎‏‎"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎Swipe up from the bottom of your screen. This gesture always takes you to the Home screen.‎‏‎‎‏‎"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎Make sure you swipe up from the bottom edge of the screen.‎‏‎‎‏‎"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎Try holding the window for longer before releasing.‎‏‎‎‏‎"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎Make sure you swipe straight up, then pause.‎‏‎‎‏‎"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎You completed the switch apps gesture. You\'re ready to use your phone!‎‏‎‎‏‎"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎Swipe to switch apps‎‏‎‎‏‎"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎Swipe up from the bottom of your screen, hold, then release.‎‏‎‎‏‎"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎All set‎‏‎‎‏‎"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎Next‎‏‎‎‏‎"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎Done‎‏‎‎‏‎"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎Settings‎‏‎‎‏‎"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎Try again‎‏‎‎‏‎"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎Nice!‎‏‎‎‏‎"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‏‏‎‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎Tutorial ‎‏‎‎‏‏‎<xliff:g id="CURRENT">%1$d</xliff:g>‎‏‎‎‏‏‏‎/‎‏‎‎‏‏‎<xliff:g id="TOTAL">%2$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="action_share" msgid="2648470652637092375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎Share‎‏‎‎‏‎"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎Screenshot‎‏‎‎‏‎"</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>
 </resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index d1ae0fe..478d1cd 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Pantalla dividida"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugerencias de apps habilitadas"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Las sugerencias de aplicaciones están inhabilitadas"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicción de app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Asegúrate de deslizar el dedo bien desde el borde izquierdo."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Asegúrate de deslizar el dedo del borde izquierdo hacia el centro de la pantalla y, luego, suéltalo."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Eso es todo. Ahora desliza el dedo desde el borde derecho."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Asegúrate de deslizar el dedo bien desde el borde derecho."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Asegúrate de deslizar el dedo del borde derecho hacia el centro de la pantalla y, luego, suéltalo."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Completaste el gesto \"Atrás\". A continuación, obtén información para ir a la página principal."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Asegúrate de no hacerlo muy cerca de la parte inferior de la pantalla."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Cambia sensibilidad de gesto \"Atrás\" en Configuración"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Desliza el dedo para volver"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Desliza el dedo desde el borde derecho o izquierdo para volver a la última pantalla."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Asegúrate de deslizar el dedo hacia arriba desde la borde inferior de la pantalla."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Asegúrate de no detenerte antes de soltarlo."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Asegúrate de deslizar el dedo derecho hacia arriba."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Completaste el gesto para ir a la página principal. A continuación, obtén información para cambiar de app."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Desliza el dedo para ir a la página principal"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Desliza el dedo hacia arriba desde la parte inferior de la pantalla. Este gesto te llevará siempre a la pantalla principal."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Asegúrate de deslizar el dedo hacia arriba desde la borde inferior de la pantalla."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prueba mantener presionada la ventana más tiempo antes de soltarla."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Asegúrate de deslizar el dedo derecho hacia arriba y, luego, detente."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Completaste el gesto para cambiar de app. El teléfono ya está listo para usar."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Desliza el dedo para cambiar de app"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Desliza el dedo hacia arriba desde la parte inferior de la pantalla, mantenlo presionado y, luego, suéltalo."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Listo"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Siguiente"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Listo"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Configuración"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Reintentar"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"¡Genial!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Instructivo <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</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>
 </resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index a2b930d..454bea7 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Dividir pantalla"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No hay nada reciente"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugerencias de aplicaciones habilitadas"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Las sugerencias de aplicaciones están inhabilitadas"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación sugerida: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Desliza el dedo desde el extremo izquierdo."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Desliza el dedo desde el borde izquierdo de la pantalla hasta el centro y levántalo."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Eso es todo. Ahora desliza el dedo desde el borde derecho."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Desliza el dedo desde el extremo derecho."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Desliza el dedo desde el borde derecho de la pantalla hasta el centro y levántalo."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Has completado el gesto para volver. Ahora, descubre cómo ir a la pantalla de inicio."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"No deslices el dedo demasiado cerca de la parte inferior de la pantalla."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Para cambiar la sensibilidad del gesto, ve a Ajustes"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Desliza el dedo para volver"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para volver a la última pantalla, desliza el dedo desde un lateral de la pantalla hasta el centro."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Desliza el dedo hacia arriba desde el borde inferior de la pantalla."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"No hagas ninguna pausa antes de levantar el dedo."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Desliza el dedo directamente hacia arriba."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Has completado el gesto para ir a la pantalla de inicio. Ahora, descubre cómo cambiar de aplicación."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Desliza el dedo para ir a la pantalla de inicio"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Desliza hacia arriba desde la parte inferior. Con este gesto, siempre irás a la pantalla de inicio."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Desliza el dedo hacia arriba desde el borde inferior de la pantalla."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prueba a mantener pulsada la ventana durante más tiempo antes de soltarla."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Desliza el dedo directamente hacia arriba y luego mantenlo pulsado."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Has completado el gesto para cambiar de aplicación. Ya puedes usar el teléfono."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Deslizar el dedo para cambiar de aplicación"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Desliza el dedo hacia arriba desde la parte inferior de la pantalla, mantenlo pulsado y levántalo."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Todo listo"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Siguiente"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Hecho"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ajustes"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Reintentar"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"¡Muy bien!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Hacer captura"</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>
 </resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 3425b33..b729934 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Jagatud ekraan"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Kinnita"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Vabavorm"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Hiljutisi üksusi pole"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Rakenduste soovitused on lubatud"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Rakenduste soovitused on keelatud"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ennustatud rakendus: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Pühkige kindlasti vasakpoolsest servast."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Pühkige ekraanikuva vasakust servast keskele ja eemaldage sõrm."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Ongi kõik! Nüüd proovige paremast servast pühkida."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Pühkige kindlasti parempoolsest servast."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Pühkige ekraanikuva paremast servast keskele ja eemaldage sõrm."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Tegite tagasiliikumise liigutuse. Järgmisena vaadake, kuidas minna avakuvale."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Veenduge, et te ei pühiks liiga ekraanikuva allosa lähedalt."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Tagasiliigutuse tundlikkuse muutmiseks avage menüü Seaded"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Tagasiliikumiseks pühkige"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Eelmisele ekraanikuvale naasmiseks pühkige vasakust või paremast servast ekraanikuva keskele."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pühkige kindlasti ekraanikuva alumisest servast üles."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Veenduge, et te enne vabastamist liigutust ei peataks."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pühkige kindlasti otse üles."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Tegite avakuvale minemise liigutuse. Järgmisena vaadake, kuidas rakenduste vahel vahetada."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Pühkimine avakuvale minemiseks"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Pühkige ekraani alaosast üles. See liigutus viib teid alati tagasi avakuvale."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pühkige kindlasti ekraanikuva alumisest servast üles."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Hoidke sõrme aknal pisut kauem, enne kui vabastate."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pühkige kindlasti otse üles, seejärel peatuge."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Tegite rakenduste vahel vahetamise liigutuse. Olete oma telefoni kasutamiseks valmis!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Rakenduste vahel vahetamiseks pühkimine"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Pühkige ekraanikuva alaosast üles, hoidke ja seejärel vabastage."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Valmis"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Järgmine"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Valmis"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Seaded"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Proovige uuesti"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Tubli töö!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Õpetus <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Jaga"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</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>
 </resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 550d3fb..f6d3f23 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Zatitu pantaila"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Ainguratu"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Modu librea"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ez dago azkenaldi honetako ezer"</string>
@@ -31,7 +30,7 @@
     <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">"Lagungarri izan dakizkizukeen aplikazioak"</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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Atzitu erraz aplikazio erabilienak hasierako pantailatik bertatik. Ohituren arabera aldatuko dira iradokizunak. Hasierako pantailara eramango dira beheko errenkadan dauden aplikazioak."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Gaituta daude aplikazioen iradokizunak"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Desgaituta daude aplikazioen iradokizunak"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Iragarritako aplikazioa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Ziurtatu hatza ezkerreko ertzetik pasatzen duzula."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Ziurtatu hatza pantailaren ezkerreko ertzetik erdialdera pasatzen eta askatzen duzula."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Hori da. Orain, pasatu hatza eskuineko ertzetik."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Ziurtatu hatza eskuineko ertzetik pasatzen duzula."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Ziurtatu hatza pantailaren eskuineko ertzetik erdialdera pasatzen eta askatzen duzula."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Ikasi duzu atzera egiteko keinua. Jarraian, ikasi hasierako pantailara joaten."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Ziurtatu hatza ez duzula pantailaren behealdetik gertuegi pasatzen."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Keinuaren sentikortasuna aldatzeko, joan ezarpenetara"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Pasatu hatza atzera egiteko"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Aurreko pantailara itzultzeko, pasatu hatza pantailaren ezkerreko edo eskuineko ertzetik erdialdera."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Ziurtatu hatza pantailaren beheko ertzetik gora pasatzen duzula."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Ziurtatu askatu aurretik ez duzula hatza gelditzen."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Ziurtatu hatza zuzen gora pasatzen duzula."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Ikasi duzu hasierako pantailara joateko keinua. Jarraian, ikasi aplikazioa aldatzen."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Pasatu hatza hasierako pantailara joateko"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Pasatu hatza pantailaren behealdetik gora. Keinu horrek hasierako pantailara eramango zaitu beti."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Ziurtatu hatza pantailaren beheko ertzetik gora pasatzen duzula."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Eduki sakatuta leihoa luzaroago hatza altxatu aurretik."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Ziurtatu hatza zuzen gora pasatzen duzula; ondoren, gelditu."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Ikasi duzu aplikazioa aldatzeko keinua. Prest zaude telefonoa erabiltzeko."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Pasatu hatza aplikazioa aldatzeko"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Pasatu hatza pantailaren behealdetik gora, eduki pantaila sakatuta eta altxatu hatza."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Dena prest"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Hurrengoa"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Eginda"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ezarpenak"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Saiatu berriro"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Ederki!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutoriala: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Partekatu"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Atera pantaila-argazki bat"</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">"Tutorial hau <xliff:g id="NAME">%1$s</xliff:g> aplikazioan aurki dezakezu geroago"</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>
 </resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 4d40115..8ca0f95 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"تقسیم صفحه"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"پین"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"بدون موارد اخیر"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"«پیشنهاد برنامه» فعال است"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"«پیشنهاد برنامه» غیرفعال است"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"برنامه پیش‌بینی‌شده: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"دقت کنید که از ابتدای لبه سمت چپ تند بکشید."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"دقت کنید که از لبه سمت چپ تند به وسط صفحه بکشید و رها کنید."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"تمام شد! اکنون از لبه سمت راست تند بکشید."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"دقت کنید که از ابتدای لبه سمت راست تند بکشید."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"دقت کنید که از لبه سمت راست تند به وسط صفحه بکشید و رها کنید."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"اشاره برگشتن را تکمیل کردید. مورد بعدی، با نحوه رفتن به «صفحه اصلی» آشنا شوید."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"دقت کنید که موقع تند کشیدن بیش‌از حد به پایین صفحه نزدیک نشوید."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"برای تغییر حساسیت اشاره برگشت، به «تنظیمات» بروید"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"تند بکشید تا به‌عقب برگردید"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"برای برگشتن به صفحه آخر، از لبه سمت چپ یا راست تند به وسط صفحه بکشید."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"دقت کنید که از لبه پایینی صفحه تند به بالا بکشید."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"دقت کنید که تا قبل‌از رها کردن، کشیدن را متوقف نکنید."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"دقت کنید که مستقیماً تند به بالا بکشید."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"اشاره رفتن به «صفحه اصلی» را تکمیل کردید. مورد بعدی، با نحوه جابه‌جا شدن بین برنامه‌ها آشنا شوید."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"تند کشیدن برای رفتن به صفحه اصلی"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"از پایین صفحه، تند به‌سمت بالا بکشید. این اشاره همیشه شما را به صفحه اصلی می‌برد."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"دقت کنید که از لبه پایینی صفحه تند به بالا بکشید."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"سعی کنید قبل‌از رها کردن، پنجره را برای مدت طولانی‌تری نگه دارید."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"دقت کنید که مستقیماً تند به بالا بکشید و سپس توقف کنید."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"اشاره جابه‌جا شدن بین برنامه‌ها را تکمیل کردید. آماده‌اید از تلفنتان استفاده کنید!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"تند کشیدن برای جابه‌جا شدن بین برنامه‌ها"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"از پایین صفحه‌نمایش تند به‌سمت بالا بکشید، نگه دارید، و سپس رها کنید."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"همه چیز آماده است"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"بعدی"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"تمام"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"تنظیمات"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"امتحان مجدد"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"عالی!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"آموزش گام‌به‌گام <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"هم‌رسانی"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"نماگرفت"</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>
 </resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 9bc3d49..9ce7e6b 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Jaettu näyttö"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Kiinnitä"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Vapaamuotoinen"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ei viimeaikaisia kohteita"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sovellusehdotukset käytössä"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sovellusehdotukset on poistettu käytöstä"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ennakoitu sovellus: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Muista pyyhkäistä aivan vasemmasta reunasta."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Pyyhkäise näytön vasemmasta reunasta keskelle ja päästä irti."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Valmista tuli! Kokeile nyt pyyhkäistä oikeasta reunasta."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Muista pyyhkäistä aivan oikeasta reunasta."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Pyyhkäise näytön oikeasta reunasta keskelle ja päästä irti."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Olet oppinut Takaisin-eleen. Opettele seuraavaksi siirtymään aloitusnäytölle."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Varo, ettet pyyhkäise liian lähellä alareunaa."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Voit muuttaa Takaisin-eleen herkkyyttä asetuksista"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Siirry takaisin pyyhkäisemällä"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Voit palata edelliseen näkymään pyyhkäisemällä näytön vasemmasta tai oikeasta reunasta keskelle."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pyyhkäise ylös näytön alareunasta."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Varo keskeyttämästä ennen kuin päästät irti."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Muista pyyhkäistä suoraan ylöspäin."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Olet oppinut aloitusnäytölle palaamiseleen. Opettele seuraavaksi vaihtamaan sovellusta."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Siirry aloitusnäytölle pyyhkäisemällä"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Pyyhkäise ylös näytön alareunasta. Tämä ele vie sinut aina aloitusnäytölle."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pyyhkäise ylös näytön alareunasta."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Kokeile pitää ikkunaa painettuna pidempään ennen kuin päästät irti."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Muista pyyhkäistä suoraan ylöspäin ja keskeytä sitten."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Olet oppinut sovellusten vaihtamiseleen. Olet valmis käyttämään puhelintasi."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Vaihda sovellusta pyyhkäisemällä"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Pyyhkäise ylöspäin näytön alareunasta ja päästä irti."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Valmista"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Seuraava"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Valmis"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Asetukset"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Yritä uudelleen"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Hienoa!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Ohje <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Jaa"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</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>
 </resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 46096f5..da73849 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Écran divisé"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Forme libre"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Les suggestions d\'applications sont activées"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Les suggestions d\'applications sont désactivées"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Application prédite : <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Assurez-vous de balayer l\'écran à partir de l\'extrémité gauche."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Assurez-vous de balayer l\'écran de l\'extrémité gauche jusqu\'au centre, puis de lever le doigt."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"C\'est tout! Maintenant, essayez à partir de la droite."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Assurez-vous de balayer l\'écran à partir de l\'extrémité droite."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Assurez-vous de balayer l\'écran de l\'extrémité droite jusqu\'au centre, puis de lever le doigt."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Vous avez terminé le geste de retour. Ensuite, vous apprendrez comment revenir à l\'écran d\'accueil."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Assurez-vous de ne pas balayer trop près du bas de l\'écran."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Modifiez la sensibilité du geste de retour dans Paramètres"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Balayez l\'écran pour revenir en arrière"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Pour revenir à l\'écran précédent, balayez l\'écran de l\'extrémité gauche ou droite vers le centre"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Assurez-vous de balayer l\'écran à partir de l\'extrémité inférieure vers le haut."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Assurez-vous de ne pas interrompre le geste avant de lever le doigt."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Assurez-vous de balayer l\'écran en ligne droite vers le haut."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Vous avez terminé le geste de retour à l\'écran d\'accueil. Ensuite, vous apprendrez comment basculer entre les applications."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Balayer pour revenir à l\'écran d\'accueil"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Balayez l\'écran du bas vers le haut. Ce geste vous ramène toujours à l\'écran d\'accueil."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Assurez-vous de balayer l\'écran à partir de l\'extrémité inférieure vers le haut."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Essayez de tenir la fenêtre plus longtemps avant de relâcher."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Assurez-vous de balayer l\'écran vers le haut, puis de faire une pause."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Vous avez terminé le geste de basculement entre les applications. Vous êtes prêt à utiliser votre téléphone!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Balayer pour basculer entre les applications"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Balayez l\'écran de bas en haut, maintenez le doigt en place, puis relâchez-le."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Terminé"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Suivant"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Terminé"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Paramètres"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Réessayer"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bien!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Étape <xliff:g id="CURRENT">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g> du tutoriel"</string>
     <string name="action_share" msgid="2648470652637092375">"Partager"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"L\'application ou votre organisation n\'autorise pas cette action"</string>
+    <string name="skip_tutorial_dialog_title" msgid="2725643161260038458">"Ignorer le tutoriel sur la navigation?"</string>
+    <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"Vous trouverez le tutoriel dans l\'application <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="gesture_tutorial_action_button_label_cancel" msgid="3809842569351264108">"Annuler"</string>
+    <string name="gesture_tutorial_action_button_label_skip" msgid="394452764989751960">"Ignorer"</string>
 </resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 306d533..b6abea7 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Écran partagé"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Format libre"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Suggestions d\'applications activées"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Les suggestions d\'applications sont désactivées"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Application prédite : <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Veillez à balayer l\'écran depuis le bord gauche."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Veillez à bien balayer l\'écran depuis le bord gauche jusqu\'au centre avant de relever le doigt."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Et voilà ! Balayez maintenant l\'écran depuis le côté droit."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Veillez à balayer l\'écran depuis le bord droit."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Veillez à bien balayer l\'écran depuis le bord droit jusqu\'au centre avant de relever le doigt."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Vous avez appris le geste pour revenir en arrière. Apprenez ensuite à revenir à l\'écran d\'accueil."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Veillez à ne pas balayer l\'écran trop près du bas."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Modifiez la sensibilité du geste retour dans les paramètres"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Balayez l\'écran pour revenir en arrière"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Pour revenir à l\'écran précédent, balayez l\'écran depuis le bord droit ou gauche jusqu\'au centre."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Veillez à balayer l\'écran du bas vers le haut."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Veillez à ne pas marquer de pause dans votre geste avant de relever le doigt."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Veillez à balayer l\'écran vers le haut."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Vous avez appris le geste pour revenir à l\'écran d\'accueil. Apprenez ensuite à passer d\'une appli à l\'autre."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Balayer pour revenir à l\'écran d\'accueil"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Balayez l\'écran de bas en haut. Ce geste vous ramènera toujours à l\'écran d\'accueil."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Veillez à balayer l\'écran du bas vers le haut."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Essayez d\'appuyer plus longtemps sur la fenêtre avant de relever le doigt."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Veillez à balayer l\'écran vers le haut et à marquer une pause dans votre geste."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Vous avez appris le geste pour passer d\'une appli à l\'autre. Vous êtes prêt à utiliser votre téléphone."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Balayer pour passer d\'une appli à l\'autre"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Balayez l\'écran du bas vers le haut, appuyez de manière prolongée, puis relevez le doigt."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Vous avez terminé"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Suivant"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"OK"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Paramètres"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Réessayez"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bravo !"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutoriel <xliff:g id="CURRENT">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Partager"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</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>
 </resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index f3dc1ed..9310d71 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Pantalla dividida"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Forma libre"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Non hai elementos recentes"</string>
@@ -31,7 +30,7 @@
     <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">"As túas aplicacións preditas"</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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accede facilmente desde a pantalla de inicio ás aplicacións que máis usas. As suxestións irán cambiando en función das túas rutinas. As aplicacións da fila inferior pasarán á pantalla de inicio."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"As suxestións de aplicacións están activadas"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"As suxestións de aplicacións están desactivadas"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación predita: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Asegúrate de pasar o dedo desde o bordo esquerdo máis afastado."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Asegúrate de pasar o dedo desde o bordo esquerdo ata o medio da pantalla e avanza."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Xa está! Proba a pasar o dedo desde o bordo dereito."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Asegúrate de pasar o dedo desde o bordo dereito máis afastado."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Asegúrate de pasar o dedo desde o bordo dereito ata o medio da pantalla e avanza."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Completaches o xesto de volver. O próximo é aprender a ir ao inicio."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Asegúrate de non pasar o dedo demasiado preto da parte inferior da pantalla."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Podes cambiar a sensibilidade do xesto en Configuración"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Pasar o dedo para volver"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para volver á última pantalla, pasa o dedo cara ao medio desde o bordo dereito ou esquerdo."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Asegúrate de pasar o dedo cara arriba desde o bordo inferior da pantalla."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Asegúrate de non facer unha pausa antes de avanzar."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Asegúrate de pasar o dedo cara arriba cun movemento vertical."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Completaches o xesto de ir ao inicio. O próximo é aprender a cambiar de aplicación."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Pasar o dedo para ir ao inicio"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Pasa o dedo cara arriba desde a parte inferior da pantalla. Ao facelo, irás á pantalla de inicio."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Asegúrate de pasar o dedo cara arriba desde o bordo inferior da pantalla."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Proba a manter premida a pantalla máis tempo antes de soltala."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Asegúrate de pasar o dedo cara arriba cun movemento vertical. Despois, fai unha pausa."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Completaches o xesto para cambiar de aplicación. Xa podes usar o teléfono!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Pasar o dedo para cambiar de aplicación"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Pasa o dedo cara arriba desde a parte inferior da pantalla, mantena premida e sepárao."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Todo listo"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Seguinte"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Feito"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Configuración"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Téntao de novo"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Excelente!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Titorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Facer captura"</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>
 </resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index b1c40b9..496d33b 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"સ્ક્રીનને વિભાજિત કરો"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"પિન કરો"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ફ્રિફોર્મ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"તાજેતરની કોઈ આઇટમ નથી"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ઍપના સુઝાવો ચાલુ છે"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ઍપના સુઝાવો બંધ છે"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"પૂર્વાનુમાનિત ઍપ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ખાતરી કરો કે તમે એકદમ દૂરની ડાબી કિનારીએથી સ્વાઇપ કરો છો."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ખાતરી કરો કે તમે ડાબી કિનારીએથી સ્ક્રીનના મધ્ય ભાગ સુધી સ્વાઇપ કરો છે અને આંગળી ઊંચકી લો છો."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"બસ થઈ ગયું! હવે જમણી કિનારીએથી સ્વાઇપ કરવાનો પ્રયાસ કરો."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ખાતરી કરો કે તમે એકદમ દૂરની જમણી કિનારીએથી સ્વાઇપ કરો છો."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ખાતરી કરો કે તમે જમણી કિનારીએથી સ્ક્રીનના મધ્ય ભાગ સુધી સ્વાઇપ કરો છે અને આંગળી ઊંચકી લો છો."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"તમે પાછા જવાનો સંકેત પૂર્ણ કર્યો છે. હવે પછી, હોમ સ્ક્રીન પર જવાની રીત વિશે જાણો."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ખાતરી કરો કે તમારાથી સ્ક્રીનની એકદમ નીચેની કિનારીની ખૂબ નજીક સુધી સ્વાઇપ ન થઈ જાય."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"પાછા જવાના સંકેતની સંવેદિતા બદલવા માટે, સેટિંગમાં જાઓ"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"પાછળ જવા માટે સ્વાઇપ કરો"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"છેલ્લી સ્ક્રીન પર પાછા જવા, ડાબી કે જમણી કિનારીએથી સ્ક્રીનના મધ્ય ભાગ સુધી સ્વાઇપ કરો."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ખાતરી કરો કે તમે સ્ક્રીનની નીચેની કિનારીએથી ઉપરની તરફ સ્વાઇપ કરો છો."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ખાતરી કરો કે તમે આંગળી ઊંચકી લેતા પહેલાં સ્વાઇપ કરવાનું થોભાવતા નથી."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ખાતરી કરો કે તમે સીધું ઉપરની તરફ સ્વાઇપ કરો છો."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"તમે હોમ સ્ક્રીન પર પાછા જવાનો સંકેત પૂર્ણ કર્યો છે. હવે પછી, ઍપ સ્વિચ કરવાની રીત વિશે જાણો."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"હોમ સ્ક્રીન પર જવા માટે સ્વાઇપ કરો"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"તમારી સ્ક્રીનના નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરો. આ સંકેત તમને હંમેશાં હોમ સ્ક્રીન પર લઈ જાય છે."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ખાતરી કરો કે તમે સ્ક્રીનની નીચેની કિનારીએથી ઉપરની તરફ સ્વાઇપ કરો છો."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"તમારી આંગળી ઊંચકતા પહેલાં તેને વિન્ડો પર થોડી વધારે વાર માટે દબાવી રાખવાનો પ્રયાસ કરો."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ખાતરી કરો કે તમે સીધું ઉપર સ્વાઇપ કરો છો, પછી થોભી જાઓ છો."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"તમે ઍપ સ્વિચ કરવાનો સંકેત પૂર્ણ કર્યો છે. તમે તમારા ફોનનો ઉપયોગ કરવા માટે તૈયાર છો!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ઍપ સ્વિચ કરવા સ્વાઇપ કરો"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"તમારી સ્ક્રીનના નીચેના ભાગથી ઉપરની તરફ સ્વાઇપ કરીને, થોડીવાર દબાવી રાખો, પછી છોડી દો."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"બધું સેટ થઈ ગયું"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"આગળ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"થઈ ગયું"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"સેટિંગ"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ફરી પ્રયાસ કરો"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"સરસ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ટ્યૂટૉરિઅલ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"શેર કરો"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"સ્ક્રીનશૉટ"</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>
 </resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index d98cc77..a7711fe 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"स्क्रीन को दो हिस्सों में बाँटना (स्प्लिट स्क्रीन)"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करना"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"फ़्रीफ़ॉर्म"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"हाल ही में इस्तेमाल किया गया कोई ऐप्लिकेशन नहीं है"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"सुझाए गए ऐप्लिकेशन की सुविधा चालू है"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"सुझाए गए ऐप्लिकेशन की सुविधा बंद है"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"सुझाया गया ऐप्लिकेशन: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"देख लें कि आप स्क्रीन की बाईं तरफ़ के बिल्कुल किनारे से स्वाइप कर रहे हों."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"देख लें कि आप स्क्रीन के बाएं किनारे से बीचों-बीच तक स्वाइप कर रहे हों और फिर अपनी उंगली उठा लें."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"हो गया! अब, दाएं किनारे से स्वाइप करने की कोशिश करें."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"देख लें कि आप स्क्रीन की दाईं तरफ़ के बिल्कुल किनारे से स्वाइप कर रहे हों."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"देख लें कि आप स्क्रीन के दाएं किनारे से बीचों-बीच तक स्वाइप कर रहे हों और फिर अपनी उंगली उठा लें."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"आपने पीछे जाने के लिए इस्तेमाल होने वाले हाथ के जेस्चर (हाव-भाव) के बारे में जान लिया है. अब, होम स्क्रीन पर जाने का तरीका जानें."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"देखे लें कि आप स्क्रीन पर बिल्कुल नीचे तक स्वाइप न कर रहे हों."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"\'सेटिंग\' में जाकर, पीछे जाने के लिए इस्तेमाल होने वाले हाथ के जेस्चर (हाव-भाव) की संवेदनशीलता बदलें"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"पिछली स्क्रीन पर वापस जाने के लिए स्वाइप करें"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"पिछली स्क्रीन पर वापस जाने के लिए, स्क्रीन के बाएं या दाएं किनारे से बीचों-बीच तक स्वाइप करें."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"देख लें कि आप स्क्रीन के निचले किनारे से ऊपर की ओर स्वाइप कर रहे हों."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"देख लें कि आप स्क्रीन से अपनी उंगली उठाने से पहले, इसे कहीं न रोक रहे हों."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"देख लें कि आप ऊपर की ओर बिल्कुल सीधे स्वाइप कर रहे हों."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"आपने होम स्क्रीन पर जाने के लिए इस्तेमाल होने वाले हाथ के जेस्चर (हाव-भाव) के बारे में जान लिया है. अब, एक से दूसरे ऐप्लिकेशन पर जाने का तरीका जानें."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"होम स्क्रीन पर जाने के लिए स्वाइप करें"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"स्क्रीन पर नीचे से ऊपर की ओर स्वाइप करें. हाथ का यह जेस्चर आपको हमेशा होम स्क्रीन पर ले जाता है."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"देख लें कि आप स्क्रीन के निचले किनारे से ऊपर की ओर स्वाइप कर रहे हों."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"कोशिश करें कि स्क्रीन से उंगली उठाने से पहले, इसे कुछ देर स्क्रीन पर दबाकर रखें."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"देख लें कि आप स्क्रीन पर ऊपर की तरफ़, बिल्कुल सीधे स्वाइप कर रहे हों और फिर रुकें."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"आपने एक ऐप्लिकेशन से दूसरे पर जाने के लिए इस्तेमाल होने वाले हाथ के जेस्चर (हाव-भाव) के बारे में जान लिया है. आप अपना फ़ोन इस्तेमाल करने के लिए तैयार हैं!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"एक ऐप्लिकेशन से दूसरे पर जाने के लिए स्वाइप करें"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"अपनी स्क्रीन पर नीचे से ऊपर की तरफ़ स्वाइप करें, दबाकर रखें, फिर छोड़ दें."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"आप पूरी तरह तैयार हैं"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"आगे बढ़ें"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"हो गया"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"सेटिंग"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"फिर से कोशिश करें"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"बहुत बढ़िया!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ट्यूटोरियल <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"शेयर करें"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट"</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>
 </resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index a026ef8..d5e4b9d 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Podijeljeni zaslon"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Prikvači"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodni oblik"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlaganje apl. omogućeno"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlaganje apl. onemogućeno"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Pazite da prijeđete prstom od krajnjeg lijevog ruba."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Pazite da prijeđete prstom od lijevog ruba do sredine zaslona i podignite prst."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"To je to! Sad pokušajte prijeći prstom od desnog ruba."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Pazite da prijeđete prstom od krajnjeg desnog ruba."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Pazite da prijeđete prstom od desnog ruba do sredine zaslona i podignite prst."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Izvršili ste pokret za povratak. Sad saznajte kako otvoriti početni zaslon."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pazite da ne prijeđete prstom preblizu dnu zaslona."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Osjetljivost pokreta povratka promijenite u postavkama"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Prijeđite prstom da biste se vratili"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Za povratak na zadnji zaslon prijeđite prstom od lijevog ili desnog ruba do sredine zaslona."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pazite da prijeđete prstom prema gore od donjeg ruba zaslona."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pazite da ne zastanete prije podizanja prsta."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pazite da prijeđete prstom ravno prema gore."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Izvršili ste pokret za otvaranje početnog zaslona. Sad saznajte kako promijeniti aplikaciju."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Prijeđite prstom da biste otvorili početni zaslon"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Prijeđite prstom od dna zaslona prema gore. Tim pokretom uvijek će se otvoriti početni zaslon."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pazite da prijeđete prstom prema gore od donjeg ruba zaslona."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Pokušajte zadržati prozor dulje prije podizanja prsta."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pazite da prijeđete prstom ravno prema gore, a zatim zastanete."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Izvršili ste pokret za promjenu aplikacije. Spremni ste za upotrebu telefona!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prijeđite prstom da biste promijenili aplikaciju"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Prijeđite prstom od dna zaslona prema gore, zadržite pritisak pa podignite prst."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Sve je spremno"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Dalje"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Gotovo"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Postavke"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Pokušajte ponovo"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Odlično!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Vodič <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Podijeli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snimka zaslona"</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>
 </resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index f81355e..386ef3e 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Osztott képernyő"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Kitűzés"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Szabad forma"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nincsenek mostanában használt elemek"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Alkalmazásjavaslatok engedélyezve"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Alkalmazásjavaslatok letiltva"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Várható alkalmazás: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Csúsztasson a képernyő bal széléről."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Csúsztasson a képernyő bal széléről a képernyő közepéig, és engedje el a képernyőt."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Ennyi az egész! Most csúsztasson a jobb szélről."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Csúsztasson a képernyő jobb széléről."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Csúsztasson a képernyő jobb széléről a képernyő közepéig, és engedje el a képernyőt."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Teljesítette a visszalépési kézmozdulatot. Most megtanulhatja, hogyan léphet a kezdőképernyőre."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Ne csúsztasson túl közel a képernyő aljához."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"A vissza mozdulat érzékenysége a Beállításokban módosítható"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Csúsztasson a visszalépéshez"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Ha visszatérne a legutóbbi képernyőre, csúsztasson a képernyő közepére a bal vagy a jobb széléről."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Csúsztasson felfelé a képernyő aljától."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Ne álljon meg, mielőtt elengedi a képernyőt."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Csúsztasson egyenesen felfelé."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Teljesítette a kezdőképernyőre lépés kézmozdulatát. Most megtanulhatja, hogyan válthat az alkalmazások között."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Csúsztatás a kezdőképernyőre lépéshez"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Csúsztassa ujját felfelé a képernyő aljától. Ez a mozdulat mindig a kezdőképernyőre visz."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Csúsztasson felfelé a képernyő aljától."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Próbálja tovább lenyomva tartani az ablakot, mielőtt elengedi a képernyőt."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Csúsztasson egyenesen felfelé, majd várjon egy kicsit."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Teljesítette az alkalmazásváltás kézmozdulatát. Készen áll a telefonja használatára!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Alkalmazásváltás csúsztatással"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Csúsztasson felfelé a képernyő aljáról, tartsa lenyomva az ujját, majd emelje fel."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Minden kész"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Tovább"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Kész"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Beállítások"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Újra"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Remek!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Útmutató (<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="CURRENT">%1$d</xliff:g>.)"</string>
     <string name="action_share" msgid="2648470652637092375">"Megosztás"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Képernyőkép"</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>
 </resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 53988e1..3ff9d0d 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Տրոհել էկրանը"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Ամրացնել"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Կամայական ձև"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Վերջին տարրեր չկան"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"«Առաջարկվող հավելվածներ» գործառույթը միացված է"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"«Առաջարկվող հավելվածներ» գործառույթն անջատված է"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Առաջարկվող հավելված՝ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Համոզվեք, որ մատը սահեցնում եք էկրանի ձախ եզրից։"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Սահեցրեք մատն էկրանի ձախ եզրից դեպի կենտրոն և բաց թողեք։"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Ահա և վերջ։ Հիմա փորձեք սահեցնել մատն էկրանի աջ եզրից։"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Համոզվեք, որ մատը սահեցնում եք էկրանի աջ եզրից։"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Սահեցրեք մատն էկրանի աջ եզրից դեպի կենտրոն և բաց թողեք։"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Դուք սովորեցիք հետ գնալու ժեստը։ Այժմ սովորենք՝ ինչպես անցնել հիմնական էկրան։"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Համոզվեք, որ մատը չափազանց մոտ չեք սահեցնում էկրանին ներքևի հատվածին։"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Հետ գնալու ժեստի զգայունությունը փոփոխեք կարգավորումներում"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Սահեցրեք մատը՝ հետ գնալու համար"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Վերջին էկրան վերադառնալու համար էկրանի աջ կամ ձախ եզրից մատը սահեցրեք դեպի կենտրոն։"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Համոզվեք, որ մատն էկրանի ներքևի եզրից վերև եք սահեցնում։"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Համոզվեք, որ դադար չեք տալիս նախքան բաց թողնելը։"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Համոզվեք, որ մատն ուղիղ վերև եք սահեցնում։"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը։ Այժմ սովորենք՝ ինչպես անցնել մի հավելվածից մյուսին։"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Սահեցրեք մատը՝ հիմնական էկրան անցնելու համար"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Մատը սահեցրեք էկրանի ներքևից վերև։ Այս ժեստը բացում է հիմնական էկրանը։"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Համոզվեք, որ մատն էկրանի ներքևի եզրից վերև եք սահեցնում։"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Նախքան բաց թողնելը փորձեք հնարավորինս երկար պահել պատուհանը։"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Համոզվեք, որ մատն ուղիղ վերև եք սահեցնում, այնուհետև դադար տվեք։"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Դուք սովորեցիք մի հավելվածից մյուսին անցնելու ժեստը։ Այժմ հանգիստ կարող եք օգտագործել ձեր հեռախոսը։"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Մատը սահեցրեք՝ մյուս հավելվածին անցնելու համար"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Մատը սահեցրեք էկրանի ներքևից վերև, պահեք և բաց թողեք։"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Պատրաստ է"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Առաջ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Պատրաստ է"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Կարգավորումներ"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Նորից փորձեք"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Գերազանց է"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Ուղեցույց <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Կիսվել"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Սքրինշոթ անել"</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>
 </resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index d7437d0..941bdb7 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -19,8 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Layar terpisah"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"Pasang pin"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"Sematkan"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Format bebas"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Tidak ada item yang baru dibuka"</string>
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setelan penggunaan aplikasi"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Saran aplikasi diaktifkan"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Saran aplikasi dinonaktifkan"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplikasi yang diprediksi: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Pastikan Anda menggeser dari tepi ujung kiri."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Pastikan Anda menggeser dari tepi kiri ke tengah layar, lalu lepaskan."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Selesai. Sekarang, coba geser dari tepi kanan."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Pastikan Anda menggeser dari tepi ujung kanan."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Pastikan Anda menggeser dari tepi kanan ke tengah layar, lalu lepaskan."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Anda telah menyelesaikan gestur kembali. Selanjutnya, pelajari cara beralih ke Layar utama."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pastikan Anda tidak menggeser terlalu dekat ke bagian bawah layar."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Untuk mengubah sensitivitas gestur kembali, buka Setelan"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Geser untuk kembali"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Untuk kembali ke layar terakhir, geser dari tepi kiri atau kanan ke tengah layar."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pastikan Anda menggeser ke atas dari tepi bawah layar."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pastikan Anda tidak menjeda sebelum melepaskan."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pastikan Anda menggeser lurus ke atas."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Anda telah menyelesaikan gestur beralih ke Layar utama. Selanjutnya, pelajari cara beralih aplikasi."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Geser untuk beralih ke layar utama"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Geser ke atas dari bagian bawah layar. Gestur ini akan selalu membawa Anda ke Layar utama."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pastikan Anda menggeser ke atas dari tepi bawah layar."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Coba tahan jendela lebih lama sebelum melepaskan."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pastikan Anda menggeser lurus ke atas, lalu menjedanya."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Anda telah menyelesaikan gestur beralih aplikasi. Anda siap menggunakan ponsel Anda!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Geser untuk beralih aplikasi"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Geser ke atas dari bagian bawah layar, tahan, lalu lepaskan."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Semua siap"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Berikutnya"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Selesai"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Setelan"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Coba lagi"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bagus!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Bagikan"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 824fd7f..6042b47 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Skipta skjá"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Festa"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Frjálst snið"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Engin nýleg atriði"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Kveikt á tillögum að forritum"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Slökkt er á tillögðum forritum"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Tillaga að forriti: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Passaðu að strjúka frá jaðri vinstri brúnar."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Passaðu að strjúka frá vinstri brún skjásins að miðju hans og sleppa svo."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Það er komið! Prófaðu að strjúka frá vinstri brúninni."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Passaðu að strjúka frá jaðri hægri brúnar."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Passaðu að strjúka frá hægri brún skjásins að miðju hans og sleppa svo."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Þú laukst við að kynna þér bendinguna „til baka“. Næst skaltu læra hvernig þú ferð „heim“."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Passaðu að strjúka ekki of nálægt neðri brún skjásins."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Til að breyta næmi til baka-bendingar ferðu í stillingar"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Strjúktu til að fara til baka"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Til að fara til baka á síðasta skjá skaltu strjúka frá vinstri eða hægri brún að miðju skjásins."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Passaðu að strjúka upp frá neðri brún skjásins."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Passaðu að stoppa ekki áður en þú sleppir."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Passaðu að strjúka beint upp."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Þú laukst við að kynna þér bendinguna „heim“. Næst skaltu læra hvernig þú skiptir á milli forrita."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Strjúktu til að fara heim"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Strjúktu upp frá neðri hluta skjásins. Þetta flytur þig alltaf á heimaskjáinn."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Passaðu að strjúka upp frá neðri brún skjásins."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prófaðu að halda fingrinum lengur á glugganum áður en þú sleppir."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Passaðu að strjúka beint upp og stoppa svo."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Þú laukst við að kynna þér bendinguna „skipta um forrit“. Nú geturðu notað símann þinn!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Strjúktu til að skipta á milli forrita"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Strjúktu upp frá neðri hluta skjásins, haltu fingrinum á skjánum og slepptu svo."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Allt til reiðu"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Áfram"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Lokið"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Stillingar"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Reyna aftur"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Flott!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Leiðsögn <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Deila"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skjámynd"</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>
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 4038966..6c81d17 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Schermo diviso"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Blocca"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Forma libera"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nessun elemento recente"</string>
@@ -31,7 +30,7 @@
     <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">"Le app previste"</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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Accedi facilmente alle app più utilizzate direttamente dalla schermata Home. I suggerimenti varieranno in base alle tue routine. Le app nella riga inferiore verranno spostate più in alto sulla schermata Home."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"La funzionalità app suggerite è attiva"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"La funzionalità app suggerite è disattivata"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Assicurati di scorrere dall\'estremità del bordo sinistro."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Assicurati di scorrere dal bordo sinistro verso il centro dello schermo e solleva il dito."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"È tutto. Ora prova a scorrere dal bordo destro."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Assicurati di scorrere dall\'estremità del bordo destro."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Assicurati di scorrere dal bordo destro verso il centro dello schermo e solleva il dito."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Hai completato il gesto Indietro. Ora, impara come andare alla schermata Home."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Assicurati di non scorrere troppo vicino alla parte inferiore dello schermo."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Usa Impostazioni per cambiare sensibilità del gesto Indietro"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Scorri per tornare indietro"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Per tornare all\'ultima schermata, scorri dal bordo sinistro o destro verso il centro dello schermo."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Assicurati di scorrere verso l\'alto dal bordo inferiore dello schermo."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Assicurati di non fare pause prima di sollevare il dito."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Assicurati di scorrere verso l\'alto senza fermarti."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Hai completato il gesto Vai alla schermata Home. Ora, impara come passare da un\'app all\'altra."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Scorri per andare alla schermata Home"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Scorri verso l\'alto dalla parte inferiore dello schermo; così arrivi sempre alla schermata Home."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Assicurati di scorrere verso l\'alto dal bordo inferiore dello schermo."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prova a tenere premuta la finestra più a lungo prima di rilasciarla."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Assicurati di scorrere verso l\'alto senza fermarti, poi fai una pausa."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Hai completato il gesto Cambia app. Ora è tutto pronto per usare il telefono."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Scorri per passare da un\'app all\'altra"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Scorri verso l\'alto dalla parte inferiore dello schermo, tieni premuto e rilascia."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Fatto"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Avanti"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Fine"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Impostazioni"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Riprova"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bene!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Condividi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 81753e1..b77faeb 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"מסך מפוצל"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"הצמדה"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"מצב חופשי"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"אין פריטים אחרונים"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ההצעות לאפליקציות מופעלות"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ההצעות לאפליקציות מושבתות"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"האפליקציות החזויות: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"חשוב להקפיד להחליק מהקצה הימני."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"חשוב להקפיד להחליק מהקצה הימני למרכז המסך ואז לשחרר."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"זהו! עכשיו צריך לנסות להחליק מהקצה השמאלי."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"חשוב להקפיד להחליק מהקצה השמאלי."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"חשוב להקפיד להחליק מהקצה השמאלי למרכז המסך ואז לשחרר."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"השלמת את תנועת \'הקודם\'. בשלב הבא לומדים איך לעבור למסך הבית."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"חשוב להקפיד שלא להחליק קרוב מדי לתחתית המסך."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"כדי לשנות את מידת הרגישות של תנועת החזרה, יש לעבור להגדרות"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"יש להחליק כדי לחזור"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"כדי לחזור למסך הקודם, יש להחליק מהקצה השמאלי או הימני למרכז המסך."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"חשוב להקפיד להחליק למעלה מהקצה התחתון של המסך."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"חשוב לוודא שלא מחכים לפני שמשחררים."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"חשוב להקפיד להחליק ישר למעלה."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"השלמת את תנועת המעבר למסך הבית. בשלב הבא לומדים איך לעבור בין אפליקציות."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"יש להחליק כדי לעבור למסך הבית"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"החלקה למעלה מתחתית המסך תמיד תעביר אותך למסך הבית."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"חשוב להקפיד להחליק למעלה מהקצה התחתון של המסך."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"יש לנסות להחזיק את החלון לזמן רב יותר לפני שמשחררים."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"חשוב להקפיד להחליק ישר למעלה ואז להמתין."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"השלמת את תנועת המעבר בין האפליקציות. הטלפון מוכן לשימוש!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"יש להחליק כדי לעבור בין אפליקציות"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"יש להחליק למעלה מתחתית המסך, להחזיק ולאחר מכן לשחרר."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"הכול מוכן"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"הבא"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"סיום"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"הגדרות"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"אפשר לנסות שוב"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"איזה יופי!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"מדריך <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"שיתוף"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"צילום מסך"</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>
 </resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index d4d6572..1cbdf73 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"分割画面"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"フリーフォーム"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近のアイテムはありません"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"アプリの候補は有効です"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"アプリの候補は無効です"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"予測されたアプリ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"一番左端からスワイプしてください。"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"画面の左端から中央に向かってスワイプし、指を離してください。"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"そのとおりです。次に、右端からスワイプしてみましょう。"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"一番右端からスワイプしてください。"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"画面の右端から中央に向かってスワイプし、指を離してください。"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"「戻る」操作を完了しました。次は、ホームに戻る方法を覚えましょう。"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"スワイプする際は画面の下部に近づきすぎないようにしましょう。"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"「戻る」操作の感度を変更するには [設定] に移動します"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"スワイプで戻りましょう"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"直前の画面に戻るには、画面の左端または右端から中央に向かってスワイプします。"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"画面の下端から上にスワイプしてください。"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"指を離す前にいったん止めないでください。"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"まっすぐ上にスワイプしてください。"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"「ホームに戻る」操作を完了しました。次は、アプリを切り替える方法を覚えましょう。"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"スワイプでホームに戻る"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"画面を下から上にスワイプします。この操作でいつでもホーム画面に戻れます。"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"画面の下端から上にスワイプしてください。"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"指を離す前にウィンドウをもう少し長押ししてみましょう。"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"まっすぐ上にスワイプしてから、いったん指を止めてください。"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"「アプリを切り替える」操作を完了しました。スマートフォンをご利用いただけるようになりました。"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"スワイプでアプリを切り替え"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"画面を下から上にスワイプして長押しし、指を離します。"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"設定完了"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"次へ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"完了"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"設定"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"再試行"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"成功"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"チュートリアル <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"共有"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"スクリーンショット"</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>
 </resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 1d0a712..5dd818d 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"ეკრანის გაყოფა"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"ჩამაგრება"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"თავისუფალი ფორმა"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"აპის შეთავაზებები ჩართულია"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"აპის შეთავაზებები გათიშულია"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ნაწინასწარმეტყველები აპი: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"არ გადაფურცლოთ მარცხენა კიდის ბოლოდან."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"გადაფურცლეთ მარცხენა კიდიდან ეკრანის ცენტრისკენ და თითი აუშვით."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"სულ ეს არის! ახლა კი ცადეთ, გადაფურცლოთ მარჯვენა კიდიდან."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"არ გადაფურცლოთ მარჯვენა კიდის ბოლოდან."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"გადაფურცლეთ მარჯვენა კიდიდან ეკრანის ცენტრისკენ და თითი აუშვით."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"თქვენ შეასრულეთ უკან დაბრუნების ჟესტი. ახლა კი შევიტყოთ, თუ როგორ დავბრუნდეთ მთავარ გვერდზე."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"არ გადაფურცლოთ ეკრანის ბოლოსთან ახლოს."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"დაბრუნების ჟესტის მგრძნობელობის შესაცვლელად გადადით პარამეტრებზე"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"უკან დასაბრუნებლად გადაფურცლეთ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ბოლო ეკრანზე დასაბრუნებლად გადაფურცლეთ მარცხენა ან მარჯვენა კიდიდან ეკრანის ცენტრისკენ."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"არ დააპაუზოთ თითის აშვებამდე."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"გადაფურცლეთ ზემოთ."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"თქვენ შეასრულეთ მთავარ ეკრანზე დაბრუნების ჟესტი. ახლა კი შევიტყოთ, როგორ გადავრთოთ აპები."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"მთავარ გვერდზე გადასასვლელად გადაფურცლეთ"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ. ამ ჟესტს ყოველთვის მთავარი გვერდის ეკრანზე გადაყავხართ."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"უფრო ხანგრძლივად დააჭირეთ თითი ფანჯარას, რომ არ დაიხუროს."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"გადაფურცლეთ პირდაპირ ზემოთ და შემდეგ დააპაუზეთ."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"თქვენ შეასრულეთ აპების გადართვის ჟესტი. შეგიძლიათ ტელეფონით სარგებლობა!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"აპების გადასართავად გადაფურცლეთ"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ, დააყოვნეთ, შემდეგ თითი აუშვით."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"მზად არის"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"შემდეგ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"მზადაა"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"პარამეტრები"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ხელახლა ცდა"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"მშვენიერია!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"სახელმძღვანელო <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"გაზიარება"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ეკრანის ანაბეჭდი"</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>
 </resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index fb76660..554d10d 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Экранды бөлу"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Бекіту"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Еркін форма"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Соңғы элементтер жоқ"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"\"Ұсынылған қолданбалар\" функциясы қосулы."</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"\"Ұсынылған қолданбалар\" функциясы өшірулі."</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Болжалды қолданба: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Сол жақтағы ең шеткі нүктеден бастап сырғытыңыз."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Экранның сол жақ шетінен ортасына қарай сырғытыңыз да, жіберіңіз."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Дайын! Енді оң жақ шеттен сырғытып көріңіз."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Оң жақтағы ең шеткі нүктеден бастап сырғытыңыз."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Экранның оң жақ шетінен ортасына қарай сырғытыңыз да, жіберіңіз."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Артқа қайту қимылын аяқтадыңыз. Енді негізгі экранға өтуді үйреніңіз."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Саусағыңызбен сырғыту кезінде экранның төменгі жағына тым жақындамаңыз."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Артқа қайту қимылы сезгіштігін параметрлерден өзгертіңіз."</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Артқа қайту үшін сырғытыңыз"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Соңғы ашылған экранға оралу үшін экранның сол немесе оң жақ шетінен ортасына қарай сырғытыңыз."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Экранның төменгі шетінен жоғары қарай сырғытыңыз."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Жіберер алдында кідіріс жасамаңыз."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Тігінен жоғары қарай сырғытыңыз."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Негізгі экранға қайту қимылын аяқтадыңыз. Енді қолданбаларды ауыстыруды үйреніңіз."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Негізгі экранға өту үшін сырғытыңыз"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Экранның төменгі жағынан жоғары қарай сырғытыңыз. Сонда негізгі экран ашылады."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Экранның төменгі шетінен жоғары қарай сырғытыңыз."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Жіберер алдында терезені ұзағырақ ұстап тұруға тырысыңыз."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Тігінен жоғары қарай сырғытыңыз да, кідіріңіз."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Қолданбаларды ауыстыру қимылын аяқтадыңыз. Телефоныңызды пайдалануға дайынсыз!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Қолданбаларды ауыстыру үшін сырғытыңыз"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Экранның төменгі жағынан жоғары қарай сырғытып, ұстап тұрыңыз да, жіберіңіз."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Бәрі дайын"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Келесі"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Дайын"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Параметрлер"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Қайталау"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Жақсы!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Оқулық: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Бөлісу"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</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>
 </resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 3c3fccc..62858ba 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -19,8 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"មុខងារ​បំបែកអេក្រង់"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"ដៅ"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"ខ្ទាស់"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"មុខងារទម្រង់សេរី"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"មិនមានធាតុថ្មីៗទេ"</string>
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ការកំណត់​ការប្រើប្រាស់​កម្មវិធី"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"បានបើក​ការណែនាំ​កម្មវិធី"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"បានបិទ​ការណែនាំ​កម្មវិធី"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"កម្មវិធី​ដែលបាន​ព្យាករ៖ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ត្រូវប្រាកដថា​អ្នកអូសពី​ចុងគែម​ខាងឆ្វេង​។"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ត្រូវប្រាកដថា​អ្នកអូសពី​គែមខាងឆ្វេង​ទៅផ្នែកកណ្ដាល​នៃអេក្រង់ រួច​ដកដៃ​។"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"ត្រឹមត្រូវហើយ! ឥឡូវនេះ សូមសាកល្បង​អូសពីគែម​ខាងស្ដាំ។"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ត្រូវប្រាកដថា​អ្នកអូសពី​ចុងគែម​ខាងស្ដាំ​។"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ត្រូវប្រាកដថា​អ្នកអូសពី​គែមខាងស្ដាំ​ទៅផ្នែកកណ្ដាល​នៃអេក្រង់ រួច​ដកដៃ​។"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"អ្នក​បានបញ្ចប់​ចលនា​ថយក្រោយ​ហើយ។ បន្ទាប់​ទៀត សូមស្វែងយល់​ពីរបៀប​ចូលទៅកាន់​អេក្រង់ដើម​។"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ត្រូវប្រាកដថា​អ្នកមិនអូស​ទៅជិត​ផ្នែកខាងក្រោម​នៃអេក្រង់ពេក​។"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ដើម្បីប្ដូរកម្រិត​រំញោចនឹង​ចលនាថយក្រោយ សូមចូលទៅកាន់​ការកំណត់"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"អូស​ដើម្បី​ត្រឡប់​ទៅវិញ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ដើម្បី​ត្រឡប់ទៅ​អេក្រង់​ចុងក្រោយវិញ សូមអូស​ពីគែម​ខាងឆ្វេង ឬខាងស្ដាំ​ទៅផ្នែកកណ្ដាល​នៃអេក្រង់​។"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ត្រូវប្រាកដថា​អ្នកអូសឡើងលើ​ពីគែមខាងក្រោម​នៃអេក្រង់​។"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ត្រូវប្រាកដថា​អ្នកមិនផ្អាក មុនពេល​ដកដៃ​។"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ត្រូវប្រាកដថា​អ្នកអូស​ត្រង់ឡើងលើ​។"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"អ្នក​បានបញ្ចប់​ចលនា​ចូលទៅកាន់​អេក្រង់ដើម​ហើយ។ បន្ទាប់​ទៀត សូមស្វែងយល់​ពីរបៀប​ប្ដូរកម្មវិធី​។"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"អូស​ដើម្បីចូល​ទៅកាន់​អេក្រង់ដើម"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"អូស​ឡើងលើ​ពីផ្នែកខាងក្រោម​នៃ​អេក្រង់របស់អ្នក។ ចលនា​នេះ​នាំ​អ្នក​ទៅ​អេក្រង់ដើម​ជានិច្ច។"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ត្រូវប្រាកដថា​អ្នកអូសឡើងលើ​ពីគែមខាងក្រោម​នៃអេក្រង់​។"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"សាកល្បង​សង្កត់វិនដូ​ឱ្យបានយូរ​ជាងនេះ មុនពេល​ដកដៃ​។"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ត្រូវប្រាកដថា​អ្នកអូស​ត្រង់ឡើងលើ រួចផ្អាក។"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"អ្នក​បានបញ្ចប់​ចលនា​ប្ដូរកម្មវិធី​ហើយ។ អ្នក​អាចប្រើ​ទូរសព្ទ​របស់អ្នក​បានហើយ!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"អូស​ដើម្បីប្ដូរ​កម្មវិធី"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"អូសឡើងលើ​ពីផ្នែកខាងក្រោមនៃអេក្រង់​របស់អ្នក រួចសង្កត់​ឱ្យជាប់ បន្ទាប់មកដកដៃចេញ។"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"រួចហើយ"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"បន្ទាប់"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"រួចរាល់"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ការកំណត់"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ព្យាយាមម្ដងទៀត"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ល្អ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"មេរៀនទី <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"ចែករំលែក"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"រូបថតអេក្រង់"</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>
 </resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 6458d6e..8fdba69 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"ಪರದೆಯನ್ನು ಬೇರ್ಪಡಿಸಿ"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"ಪಿನ್ ಮಾಡಿ"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ಮುಕ್ತಸ್ವರೂಪ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
@@ -31,7 +30,7 @@
     <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="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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿಯೇ ನೀವು ಹೆಚ್ಚು ಬಳಸಿದ ಆ್ಯಪ್‌ಗಳನ್ನು ಸುಲಭವಾಗಿ ಪ್ರವೇಶಿಸಿ. ನಿಮ್ಮ ದಿನಚರಿಯನ್ನು ಆಧರಿಸಿ ಸಲಹೆಗಳು ಬದಲಾಗುತ್ತವೆ. ಕೆಳಭಾಗದ ಸಾಲಿನಲ್ಲಿನ ಆ್ಯಪ್‌ಗಳು ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ ಚಲಿಸುತ್ತವೆ."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ಶಿಫಾರಸು ಮಾಡಿದ ಆ್ಯಪ್: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ನೀವು ದೂರದ ಎಡ ಅಂಚಿನಿಂದ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ಎಡ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ ಮಧ್ಯಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ನಿಮ್ಮ ಬೆರಳನ್ನು ಮೇಲೆತ್ತಿ."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"ಅಷ್ಟೇ! ಈಗ ಬಲ ತುದಿಯಿಂದ ಸ್ವೈಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ನೀವು ದೂರದ ಬಲ ಅಂಚಿನಿಂದ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ಬಲ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ ಮಧ್ಯಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ನಿಮ್ಮ ಬೆರಳನ್ನು ಮೇಲೆತ್ತಿ."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"ನೀವು ಗೋ ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ. ಮುಂದೆ, ಹೋಮ್‌ಗೆ ಹೋಗುವುದು ಹೇಗೆ ಎಂದು ತಿಳಿಯಿರಿ."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ನೀವು ಸ್ಕ್ರೀನ್‌ನ ಕೆಳಭಾಗಕ್ಕೆ ಹೆಚ್ಚು ಹತ್ತಿರ ಸ್ವೈಪ್ ಮಾಡದಂತೆ ನೋಡಿಕೊಳ್ಳಿ."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್‌ನ ಸೂಕ್ಷ್ಮತೆ ಬದಲಾಯಿಸಲು, ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"ಹಿಂದಕ್ಕೆ ಹೋಗಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ಕೊನೆಯ ಸ್ಕ್ರೀನ್‌ಗೆ ಹಿಂತಿರುಗಲು, ಎಡ ಅಥವಾ ಬಲ ಅಂಚಿನಿಂದ ಸ್ಕ್ರೀನ್ ಮಧ್ಯಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ಸ್ಕ್ರೀನ್‌ನ ಕೆಳಗಿನ ಅಂಚಿನಿಂದ ನೀವು ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ವಿರಾಮಗೊಳಿಸದೆ ನಿಮ್ಮ ಬೆರಳನ್ನು ಸ್ಕ್ರೀನ್‌ನಿಂದ ಮೇಲೆತ್ತಿ."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ನೀವು ನೇರವಾಗಿ ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ. ಮುಂದೆ, ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಾಯಿಸುವುದು ಹೇಗೆ ಎಂದು ತಿಳಿಯಿರಿ."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ಗೆ ಹಿಂತಿರುಗಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ಸ್ಕ್ರೀನ್‌ನ ಕೆಳಗಿನಿಂದ ಮೇಲೆ ಸ್ವೈಪ್ ಮಾಡಿ. ಈ ಗೆಸ್ಚರ್ ಯಾವಾಗಲೂ ನಿಮ್ಮನ್ನು ಹೋಮ್‌ ಸ್ಕ್ರೀನ್‌ಗೆ ಕರೆದೊಯ್ಯುತ್ತದೆ."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ಸ್ಕ್ರೀನ್‌ನ ಕೆಳಗಿನ ಅಂಚಿನಿಂದ ನೀವು ಸ್ವೈಪ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ಬೆರಳನ್ನು ಮೇಲೆತ್ತುವ ಮೊದಲು ವಿಂಡೋವನ್ನು ಹೆಚ್ಚು ಸಮಯ ಹಿಡಿದಿಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ನೀವು ನೇರವಾಗಿ ಸ್ವೈಪ್ ಮಾಡಿ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ, ನಂತರ ವಿರಾಮಗೊಳಿಸಿ."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"ನೀವು ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಾಯಿಸುವ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ. ನಿಮ್ಮ ಫೋನ್ ಬಳಸಲು ನೀವು ಸಿದ್ಧರಿದ್ದೀರಿ!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ನ ಕೆಳಗಿನಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ, ಒತ್ತಿ ಹಿಡಿಯಿರಿ, ನಂತರ ಬಿಟ್ಟು ಬಿಡಿ."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ಸಂಪೂರ್ಣ ಸಿದ್ಧವಾಗಿದೆ"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"ಮುಂದೆ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"ಮುಗಿದಿದೆ"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ಚೆನ್ನಾಗಿದೆ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ಟ್ಯುಟೋರಿಯಲ್ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</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>
 </resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index f7051f5..fbbddd6 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"화면 분할"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"고정"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"자유 형식"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"최근 항목이 없습니다."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"앱 제안이 사용 설정됨"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"앱 제안이 사용 중지됨"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"예상 앱: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"왼쪽 가장자리 끝에서 스와이프하세요."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"왼쪽 가장자리에서 화면 중앙으로 스와이프한 후 손가락을 떼세요."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"좋습니다. 이번에는 오른쪽 가장자리에서 스와이프해 보세요."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"오른쪽 가장자리 끝에서 스와이프하세요."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"오른쪽 가장자리에서 화면 중앙으로 스와이프한 후 손가락을 떼세요."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"돌아가기 동작을 완료했습니다. 이번에는 홈으로 가는 방법을 알아보겠습니다."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"화면 하단에 지나치게 가까운 곳에서 스와이프하면 안 됩니다."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"돌아가기 동작의 민감도를 변경하려면 설정으로 이동하세요"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"스와이프하여 돌아가기"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"마지막 화면으로 돌아가려면 왼쪽 또는 오른쪽 가장자리에서 화면 중앙으로 스와이프하세요"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"화면 하단 가장자리에서 위로 스와이프하세요."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"손가락을 떼기 전에 멈추지 않아야 합니다."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"위로 똑바르게 스와이프하세요."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"홈으로 이동 동작을 완료했습니다. 이번에는 앱 전환 방법을 알아보겠습니다."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"스와이프하여 홈으로 이동"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"화면 하단에서 위로 스와이프합니다. 이 동작을 사용하면 언제든지 홈 화면으로 이동할 수 있습니다"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"화면 하단 가장자리에서 위로 스와이프하세요."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"창을 더 오래 누르고 있다가 손가락을 떼 보세요."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"위로 똑바르게 스와이프한 후 잠깐 멈추세요."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"앱 전환 동작을 완료했습니다. 휴대전화를 사용할 준비가 됐습니다."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"스와이프로 앱 전환"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"화면 하단에서 위로 스와이프하고 잠시 멈춘 다음 손가락을 떼세요"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"설정 완료"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"다음"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"완료"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"설정"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"다시 시도"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"잘하셨습니다"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"튜토리얼 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"공유"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"스크린샷"</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>
 </resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index bdc065e..bd50eed 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Экранды бөлүү"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Кадап коюу"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Эркин форма режими"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Акыркы колдонмолор жок"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Сунушталган колдонмолор функциясы иштетилди"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Сунушталган колдонмолор функциясы өчүрүлгөн"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Божомолдонгон колдонмо: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Экранды эң четинен солдон оңго карай сүрүңүз."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Экранды сол жагынан ортосуна карай сүрүп, манжаңызды алыңыз."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Бүттү! Эми экранды оң жагынан сүрүп көрүңүз."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Экранды эң четинен оңдой солго карай сүрүңүз."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Экранды оң жагынан ортосуна карай сүрүп, манжаңызды алыңыз."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"\"Артка\" жаңсоосу боюнча үйрөткүчтү бүтүрдүңүз. Эми башкы бетке кантип өтүүнү үйрөнүп алыңыз."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Манжаңызды экрандын ылдый жагына өтө жакындатпай сүрүңүз."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"\"Артка\" жаң-нун сезгичтигин өзгөртүү үчүн Жөндөөлөргө өтүңүз"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Артка кайтуу үчүн сүрүңүз"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Акыркы экранга кайтуу үчүн экранды сол же оң жагынан ортосуна карай сүрүңүз."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Экранды ылдыйдан өйдө карай сүрүңүз."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Манжаңызды алганга чейин токтотпоңуз."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Экранды өйдө карай сүрүңүз."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"\"Башкы бетке өтүү\" жаңсоосу боюнча үйрөткүчтү бүтүрдүңүз. Эми колдонмолорду которуштуруу ыкмасын үйрөнүп алыңыз."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Башкы бетке өтүү үчүн сүрүп коюңуз"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Экранды ылдый жагынан өйдө карай сүрүңүз. Бул жаңсоо сизди ар дайым Башкы экранга алып барат."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Экранды ылдыйдан өйдө карай сүрүңүз."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Манжаңызды алуудан мурун экранда узагыраак кармаңыз."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Экранды өйдө карай сүрүп, токтоп туруңуз."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"\"Колдонмолорду которуштуруу\" жаңсоосу боюнча үйрөткүчтү бүтүрдүңүз. Телефонуңузду колдоно берсеңиз болот."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Колдонмолорду которуштуруу үчүн сүрүңүз"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Экранды ылдыйдан өйдө карай сүрүп, бир аз коё бербей кармап туруңуз."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Дапдаяр!"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Кийинки"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Бүттү"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Жөндөөлөр"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Кайра аракет кылыңыз"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Сонун!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Үйрөткүч: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Бөлүшүү"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</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>
 </resources>
diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml
index c03eaa2..668aea2 100644
--- a/quickstep/res/values-land/dimens.xml
+++ b/quickstep/res/values-land/dimens.xml
@@ -15,5 +15,5 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="task_card_menu_horizontal_padding">24dp</dimen>
+    <dimen name="overview_task_margin">8dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values-land/styles.xml b/quickstep/res/values-land/styles.xml
deleted file mode 100644
index 0824b4f..0000000
--- a/quickstep/res/values-land/styles.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
--->
-<resources>
-    <!-- Task Menu layout styles. -->
-    <style name="TaskMenu">
-        <item name="android:orientation">horizontal</item>
-    </style>
-
-    <!-- Task Menu Option layout styles. -->
-    <style name="TaskMenu.Option">
-        <item name="android:layout_width">0dp</item>
-        <item name="android:layout_weight">1</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index b8c507b..e05b18e 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"ແບ່ງໜ້າຈໍ"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"ປັກໝຸດ"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ຮູບແບບອິດສະຫລະ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ເປີດການນຳໃຊ້ການແນະນຳແອັບແລ້ວ"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ປິດການນຳໃຊ້ການແນະນຳແອັບແລ້ວ"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ແອັບທີ່ຄາດເດົາໄວ້: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຊ້າຍສຸດ."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຊ້າຍໄປຫາທາງກາງຂອງຈໍແລ້ວປ່ອຍ."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"ແລ້ວໆ! ຕອນນີ້ໃຫ້ລອງປັດຈາກຂອບຂວາເບິ່ງ."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຂວາສຸດ."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ກະລຸນາກວດສອບວ່າທ່ານປັດຈາກຂອບຂວາໄປຫາທາງກາງຂອງຈໍແລ້ວປ່ອຍ."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"ທ່ານໃຊ້ທ່າທາງກັບຄືນສຳເລັດແລ້ວ. ຕໍ່ໄປ, ມາສຶກສາວິທີໄປໜ້າຫຼັກ."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ກະລຸນາກວດສອບວ່າທ່ານບໍ່ໄດ້ປັດໃກ້ກັບທາງລຸ່ມຂອງໜ້າຈໍເກີນໄປ."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ເພື່ອປ່ຽນຄວາມລະອຽດອ່ອນຂອງທ່າທາງກັບຄືນ, ໃຫ້ໄປຫາການຕັ້ງຄ່າ"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"ປັດເພື່ອກັບຄືນ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ເພື່ອກັບໄປໜ້າຈໍຫຼ້າສຸດ, ໃຫ້ປັດຈາກຂອບຊ້າຍ ຫຼື ຂວາໄປຫາທາງກາງຂອງໜ້າຈໍ."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຈາກຂອບລຸ່ມສຸດຂອງໜ້າຈໍ."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ກະລຸນາກວດສອບວ່າທ່ານບໍ່ຢຸດຊົ່ວຄາວກ່ອນປ່ອຍນິ້ວ."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນໄປຊື່ໆ."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"ທ່ານໃຊ້ທ່າທາງໄປໜ້າຫຼັກສຳເລັດແລ້ວ. ຕໍ່ໄປ, ມາສຶກສາວິທີສະຫຼັບແອັບ."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ປັດເພື່ອໄປໜ້າຫຼັກ"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ປັດຂຶ້ນມາຈາກລຸ່ມສຸດຂອງໜ້າຈໍທ່ານ. ທ່າທາງນີ້ຈະພາທ່ານໄປໂຮມສະກຣີນສະເໝີ."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຈາກຂອບລຸ່ມສຸດຂອງໜ້າຈໍ."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ລອງກົດໃສ່ໜ້າຈໍຄ້າງໄວ້ດົນຂຶ້ນກ່ອນປ່ອຍນິ້ວ."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ກະລຸນາກວດສອບວ່າທ່ານປັດຂຶ້ນຊື່ໆ, ຈາກນັ້ນຢຸດຊົ່ວຄາວ."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"ທ່ານໃຊ້ທ່າທາງສະຫຼັບແອັບສຳເລັດແລ້ວ. ທ່ານພ້ອມໃຊ້ໂທລະສັບຂອງທ່ານແລ້ວ!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ປັດເພື່ອສະຫຼັບແອັບ"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"ປັດຂຶ້ນຈາກລຸ່ມສຸດຂອງໜ້າຈໍທ່ານ, ຈາກນັ້ນປ່ອຍ."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ທຸກຢ່າງພ້ອມແລ້ວ"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"ຕໍ່ໄປ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"ແລ້ວໆ"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ການຕັ້ງຄ່າ"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ລອງໃໝ່"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ດີ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ການສອນການນຳໃຊ້ທີ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"ແບ່ງປັນ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ຮູບໜ້າຈໍ"</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>
 </resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index cd4310a..2f1db50 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Skaidyti ekraną"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Prisegti"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Laisva forma"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nėra jokių naujausių elementų"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Siūlomų programų funkcija įgalinta"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Siūlomų programų funkcija išjungta"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Numatoma programa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Turite perbraukti nuo kairiojo krašto."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Turite perbraukti nuo kairiojo krašto link ekrano vidurio ir pakelti pirštą."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Tai viskas! Dabar pabandykite perbr. nuo dešiniojo krašto."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Turite perbraukti nuo dešiniojo krašto."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Turite perbraukti nuo dešiniojo krašto link ekrano vidurio ir pakelti pirštą."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Atlikote grįžimo atgal gestą. Toliau sužinosite, kaip pereiti į pagrindinį ekraną."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nebraukite per arti ekrano apačios."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Norėd. pak. grįžimo gesto jautr., eikite į sk. „Nustatymai“"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Norėdami grįžti, perbraukite"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Norėdami grįžti į ankstesnį ekraną, perbr. nuo kairiojo arba dešinio krašto link ekrano vidurio."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Turite perbraukti aukštyn nuo apatinio ekrano krašto."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Nepristabdykite prieš pakeldami pirštą."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Turite tiesiai perbraukti aukštyn."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Atlikote perėjimo į pagrindinį ekraną gestą. Toliau sužinosite, kaip perjungti programas."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Perbraukite, kad pereitumėte į pagrindinį ekraną"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Perbraukite aukštyn nuo ekrano apačios. Atlikus šį gestą, visada nukreipiama į pagrindinį ekraną."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Turite perbraukti aukštyn nuo apatinio ekrano krašto."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Pabandykite palaikyti langą ilgiau prieš pakeldami pirštą."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Turite tiesiai perbraukti aukštyn, o tada pristabdyti."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Atlikote programų perjungimo gestą. Esate pasirengę naudoti telefoną!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Perbraukite, kad perjungtumėte programas"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Perbraukite aukštyn nuo ekrano apačios, palaikykite ir paleiskite."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Viskas nustatyta"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Kitas"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Atlikta"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Nustatymai"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Bandykite dar kartą"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Šaunu!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Mokymo programa: <xliff:g id="CURRENT">%1$d</xliff:g> iš <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Bendrinti"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekrano kopija"</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>
 </resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 65620c4..ce941bc 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Sadalīt ekrānu"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Piespraust"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Brīva forma"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nav nesenu vienumu."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Ieteicamās lietotnes ir iespējotas"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Ieteicamās lietotnes ir atspējotas"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Prognozētā lietotne: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Jāvelk no pašas kreisās malas."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Jāvelk no ekrāna kreisās malas uz vidu un jāatlaiž."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Tas arī viss! Tagad mēģiniet vilkt no labās malas."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Jāvelk no pašas labās malas."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Jāvelk no ekrāna labās malas uz vidu un jāatlaiž."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Jūs sekmīgi veicāt atgriešanās žestu. Tagad varat iemācīties, kā pāriet uz sākumu."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nevelciet pārāk tuvu ekrāna apakšdaļai."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Atgriešanās žesta jutīguma līmeni varat mainīt iestatījumos."</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Vilkšana, lai atgrieztos"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Lai atgrieztos iepriekšējā ekrānā, velciet no kreisās vai labās malas uz ekrāna vidu."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Jāvelk augšup no ekrāna apakšmalas."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pirms atlaišanas nepārtrauciet kustību."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Jāvelk tieši uz augšu."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu. Tagad varat iemācīties, kā pārslēgt lietotnes."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Vilkšana, lai pārietu uz sākumu"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Velciet augšup no ekrāna apakšdaļas. Ar šo žestu vienmēr varat atvērt sākuma ekrānu."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Jāvelk augšup no ekrāna apakšmalas."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Mēģiniet ilgāk turēt logu, pirms atlaižat."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Jāvelk tieši uz augšu un pēc tam jāaptur kustība."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Jūs sekmīgi veicāt lietotņu pārslēgšanas žestu. Esat gatavs izmantot tālruni!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Vilkšana, lai pārslēgtu lietotnes"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Velciet augšup no ekrāna apakšdaļas, turiet un pēc tam atlaidiet."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Gatavs"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Tālāk"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Gatavs"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Iestatījumi"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Mēģināt vēlreiz"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Lieliski!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"<xliff:g id="CURRENT">%1$d</xliff:g>. mācību darbība no <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Kopīgot"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Veikt ekrānuzņēmumu"</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>
 </resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 130e012..f9fd8b3 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -19,8 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Поделен екран"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"Прикачување"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"Закачи"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Нема неодамнешни ставки"</string>
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Поставки за користење на апликациите"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предлозите за апликации се овозможени"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Предлозите за апликации се оневозможени"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвидена апликација: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Повлечете од подалечниот лев агол."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Повлечете од левиот раб кон средината на екранот и пуштете."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Тоа е сѐ! Сега пробајте да повлечете од десниот раб."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Повлечете од подалечниот десен агол."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Повлечете од десниот раб кон средината на екранот и пуштете."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Завршивте со упатството за враќање назад. Следно, дознајте како да одите на почетната страница."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Не повлекувајте преблиску до долниот раб на екранот."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"За да ја промените чувствителноста, одете во „Поставки“"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Повлечете за да се вратите назад"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"За да се вратите на последниот екран, повлечете од левиот или десниот раб кон средината на екранот."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Повлечете нагоре од долниот раб на екранот."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Не правете пауза пред да пуштите."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Повлечете право нагоре."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Завршивте со упатството за почетната страница. Следно, дознајте како да се префрлате помеѓу апликации."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Повлечете за да одите на почетната страница"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Повлечете нагоре од долниот раб на екранот. Ова движење секогаш ќе ве одведе на почетниот екран."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Повлечете нагоре од долниот раб на екранот."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Задржете го прозорецот подолго пред да го пуштите."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Повлечете право нагоре, а потоа застанете."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Завршивте со упатството за префрлање помеѓу апликации. Спремни сте да го користите телефонот."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Повлечете за префрлање помеѓу апликации"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Повлечете нагоре од дното на екранот и задржете, па пуштете."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Готово"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Следно"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Готово"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Поставки"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Обиди се пак"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Одлично!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Упатство <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Сподели"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Слика од екранот"</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>
 </resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 8e1eec5..fc6bc82 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"സ്‌ക്രീൻ വിഭജിക്കുക"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"പിൻ ചെയ്യുക"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ഫ്രീഫോം"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ആപ്പ് നിർദ്ദേശങ്ങൾ പ്രവർത്തനക്ഷമമാക്കി"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ആപ്പ് നിർദ്ദേശങ്ങൾ പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"പ്രവചിച്ച ആപ്പ്: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ഇടത്തേയറ്റത്തെ അരികിൽ നിന്ന് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ഇടത്തേ അരികിൽ നിന്ന് സ്‌ക്രീനിന്റെ മധ്യഭാഗത്തേക്ക് സ്വെെപ്പ് ചെയ്‌ത് വിടുക."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"അത്ര മാത്രം! ഇനി വലത്തേ അരികിൽ നിന്ന് സ്വെെപ്പ് ചെയ്യൂ."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"വലത്തേയറ്റത്തെ അരികിൽ നിന്ന് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"വലത്തേ അരികിൽ നിന്ന് സ്‌ക്രീനിന്റെ മധ്യഭാഗത്തേക്ക് സ്വെെപ്പ് ചെയ്‌ത് വിടുക."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"മടങ്ങുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി. അടുത്തത്, ഹോമിലേക്ക് പോകുന്നതെങ്ങനെയെന്ന് മനസ്സിലാക്കുക."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"സ്‌ക്രീനിന്റെ ഏറ്റവും അടിഭാഗത്തേക്ക് സ്വെെപ്പ് ചെയ്യുന്നില്ലെന്ന് ഉറപ്പാക്കുക."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ബാക്ക്ജെസ്റ്ററിന്റെ സെൻസിറ്റിവിറ്റി മാറ്റാൻ ക്രമീകരണത്തിൽ പോകൂ"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"മടങ്ങാൻ സ്വെെപ്പ് ചെയ്യുക"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"മുൻ സ്‌ക്രീനിലേക്ക് മടങ്ങാൻ, ഇടതോ വലതോ അരികിൽ നിന്ന് സ്‌ക്രീനിന്റെ നടുവിലേക്ക് സ്വെെപ്പ് ചെയ്യുക."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"സ്‌ക്രീനിന്റെ താഴത്തെ അരികിൽ നിന്ന് മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"വിടുന്നതിന് മുമ്പ് നിങ്ങൾ താൽക്കാലികമായി നിർത്തുന്നില്ലെന്ന് ഉറപ്പാക്കുക."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"നേരെ മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പിക്കുക."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"ഹോമിലേക്ക് പോകുക ജെസ്‌ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി. അടുത്തത്, എങ്ങനെ ആപ്പുകൾ മാറാമെന്ന് മനസ്സിലാക്കുക."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ഹോമിലേക്ക് പോകാൻ സ്വെെപ്പ് ചെയ്യുക"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"സ്‌ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യൂ. ഈ ജെസ്ച്ചർ എപ്പോഴും ഹോം സ്‌ക്രീനിലേക്ക് നയിക്കുന്നു."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"സ്‌ക്രീനിന്റെ താഴത്തെ അരികിൽ നിന്ന് മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"റിലീസ് ചെയ്യുന്നതിന് മുമ്പ് വിൻഡോ കൂടുതൽ സമയം ഹോൾഡ് ചെയ്യാൻ ശ്രമിക്കുക."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"നേരെ മുകളിലേക്ക് സ്വെെപ്പ് ചെയ്യുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക, ശേഷം താൽക്കാലികമായി നിർത്തുക."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"ആപ്പുകൾ മാറുക ജെസ്‌ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി. ഫോൺ ഉപയോഗിക്കാൻ നിങ്ങൾ തയ്യാറാണ്!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ആപ്പുകൾ മാറാൻ സ്വെെപ്പ് ചെയ്യുക"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"സ്‌ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിച്ച ശേഷം വിടുക."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"എല്ലാം സജ്ജീകരിച്ചു"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"അടുത്തത്"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"പൂർത്തിയായി"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ക്രമീകരണം"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"വീണ്ടും ശ്രമിക്കുക"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"കൊള്ളാം!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ട്യൂട്ടോറിയൽ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"പങ്കിടുക"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"സ്ക്രീൻഷോട്ട്"</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>
 </resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index ba083ef..48513eb 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -19,8 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Дэлгэцийг хуваах"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"Тогтоох"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"Бэхлэх"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Чөлөөтэй хувьсах"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Сүүлийн үеийн зүйл алга"</string>
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Апп ашиглалтын тохиргоо"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Санал болгож буй аппуудыг идэвхжүүлсэн"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Санал болгож буй аппуудыг идэвхгүй болгосон"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Таамаглаж буй апп: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Та зүүн захын булангаас шударна уу."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Та дэлгэцийн зүүн булангаас дунд хэсэг хүртэл шударч, суллаарай."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Ингээд л боллоо! Одоо баруун булангаас шударч үзнэ үү."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Та баруун захын булангаас шударна уу."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Та дэлгэцийн баруун булангаас дунд хэсэг хүртэл шударч, суллаарай."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Та буцах зангааг гүйцэтгэлээ. Дараа нь Нүүр лүү хэрхэн очихыг мэдэж авна уу."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Та дэлгэцийн доод хэсэгтэй хэт ойр бүү шудраарай."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Буцах зангааны мэдрэгшлийг өөрчлөх бол Тохиргоо руу очно уу"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Буцахын тулд шудрах"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Сүүлийн дэлгэц рүү буцахын тулд дэлгэцийн зүүн эсвэл баруун булангаас дунд хэсэг рүү шударна уу."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Та дэлгэцийн доод булангаас дээш шударна уу."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Та суллахаасаа өмнө түр зогсоож болохгүй."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Та чигээрээ шударна уу."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Та Нүүр лүү очих зангааг гүйцэтгэлээ. Дараа нь аппуудыг хэрхэн сэлгэх талаар мэдэж авна уу."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Нүүр лүү очихын тулд шудрах"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Дэлгэцийнхээ доороос дээш шударна уу. Энэ зангаа таныг тогтмол Үндсэн нүүрэнд аваачна."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Та дэлгэцийн доод булангаас дээш шударна уу."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Суллахаасаа өмнө цонхыг илүү удаан дарж үзнэ үү."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Та чигээрээ шударч, дараа нь түр зогсооно уу."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Та аппуудыг сэлгэх зангааг гүйцэтгэлээ. Та утсаа ашиглахад бэлэн боллоо!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Аппуудыг сэлгэхийн тулд шудрах"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Дэлгэцийнхээ доод хэсгээс дээш шударч, удаан дараад суллана уу."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Бүгдийг тохируулсан"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Дараах"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Дууссан"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Тохиргоо"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Дахин оролдох"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Янзтай!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"<xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g> практик хичээл"</string>
     <string name="action_share" msgid="2648470652637092375">"Хуваалцах"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Дэлгэцийн агшин дарах"</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>
 </resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 4a5fddd..c576cf1 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"विभाजित स्क्रीन"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करा"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"फ्रीफॉर्म"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"कोणतेही अलीकडील आयटम नाहीत"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"अ‍ॅप सूचना सुरू केल्या आहेत"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"अ‍ॅप सूचना बंद केल्या आहेत"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"पूर्वानुमान केलेले अ‍ॅप: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"तुम्ही स्क्रीनच्या अगदी डाव्या कडेपासून स्‍वाइप करत आहात याची खात्री करा."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"तुम्ही स्क्रीनच्या डाव्या कडेपासून मध्यावर स्‍वाइप करून बोट उचलत आहात याची खात्री करा."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"पूर्ण झाले! आता उजव्या कोपऱ्यावरून स्वाइप करून पहा."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"तुम्ही स्क्रीनच्या अगदी उजव्या कडेपासून स्‍वाइप करत आहात याची खात्री करा."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"तुम्ही स्क्रीनच्या उजव्या कडेपासून मध्यावर स्‍वाइप करून बोट उचलत आहात याची खात्री करा."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"तुम्ही गो बॅक जेश्चर पूर्ण केले. आता, होम वर कसे जायचे ते जाणून घ्या."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"तुम्ही स्क्रीनच्या तळाच्या अगदी जवळून स्‍वाइप करत नाही याची खात्री करा."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"बॅक जेश्चरची संवेदनशीलता बदलण्यासाठी, सेटिंग्ज वर जा"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"परत जाण्यासाठी स्वाइप करा"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"मागील स्क्रीनवर परत जाण्यासाठी, स्क्रीनच्या डाव्या किंवा उजव्या कडेपासून मध्यावर स्‍वाइप करा."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"तुम्ही स्क्रीनच्या तळाच्या कडेपासून वर स्वाइप करत आहात याची खात्री करा."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"तुम्ही स्क्रीनवरून बोट उचलण्यापूर्वी ते थांबवत नाही याची खात्री करा."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"तुम्ही सरळ वर स्वाइप करत आहात याची खात्री करा."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"तुम्ही गो होम जेश्चर पूर्ण केले. आता, ॲप्स कशी स्विच करायची ते जाणून घ्या."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"होमवर जाण्यासाठी स्‍वाइप करा"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"तुमच्या स्क्रीनच्या तळाकडून वर स्वाइप करा. हे जेश्चर तुम्हाला नेहमी होम स्क्रीनवर घेऊन जाते."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"तुम्ही स्क्रीनच्या तळाच्या कडेपासून वर स्वाइप करत आहात याची खात्री करा."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"विंडोवरून बोट उचलण्यापूर्वी थोडा वेळ ते तेथेच धरून ठेवा."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"तुम्ही सरळ वर स्वाइप करून, त्यानंतर बोट थांबवत आहात याची खात्री करा."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"तुम्ही स्विच ॲप्स जेश्चर पूर्ण केले. तुम्ही तुमचा फोन वापरण्यासाठी तयार आहात!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"अ‍ॅप्स स्विच करण्यासाठी स्वाइप करा"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"तुमच्या स्क्रीनच्या तळाकडून वर स्वाइप करा, धरून ठेवा, त्यानंतर बोट उचला."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"सर्व तयार आहे"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"पुढील"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"पूर्ण झाले"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"सेटिंग्ज"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"पुन्हा प्रयत्न करा"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"छान!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ट्युटोरियल <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"शेअर करा"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट"</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>
 </resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 714831a..74b9d08 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Skrin pisah"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Semat"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Bentuk bebas"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Tiada item terbaharu"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Cadangan apl didayakan"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Cadangan apl dilumpuhkan"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Apl yang diramalkan: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Pastikan anda meleret dari hujung sebelah kiri."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Pastikan anda meleret dari tepi sebelah kiri ke tengah skrin dan lepaskan."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Itu sahaja! Sekarang, cuba leret dari tepi sebelah kanan."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Pastikan anda meleret dari hujung sebelah kanan."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Pastikan anda meleret dari tepi sebelah kanan ke tengah skrin dan lepaskan."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Anda telah melengkapkan gerak isyarat undur. Seterusnya, ketahui cara kembali ke Laman Utama."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pastikan anda tidak meleret terlalu dekat dengan bahagian bawah skrin."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Utk mengubah kepekaan gerak isyarat undur, pergi ke Tetapan"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Leret untuk kembali"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Untuk kembali ke skrin terakhir, leret dari tepi sebelah kiri atau kanan ke tengah skrin."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pastikan anda meleret ke atas dari tepi sebelah bawah skrin."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pastikan anda tidak menjeda sebelum melepaskan gerak isyarat tersebut."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pastikan anda meleret terus ke atas."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Anda telah melengkapkan gerak isyarat kembali ke Laman Utama. Seterusnya, ketahui cara menukar apl."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Leret untuk kembali ke laman utama"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Leret ke atas dari bahagian bawah skrin. Gerak isyarat ini sentiasa membawa anda ke Skrin utama."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pastikan anda meleret ke atas dari tepi sebelah bawah skrin."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Cuba tahan tetingkap untuk tempoh yang lebih lama sebelum melepaskan."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pastikan anda meleret ke atas, kemudian menjeda."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Anda telah melengkapkan gerak isyarat menukar apl. Telefon anda sudah bersedia untuk digunakan!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Leret untuk menukar apl"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Leret ke atas dari bahagian bawah skrin anda, tahan, kemudian lepaskan."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Selesai"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Seterusnya"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Selesai"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Tetapan"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Cuba lagi"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bagus!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Kongsi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Tangkapan skrin"</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>
 </resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index a18c1c4..919ad6e 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -19,8 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"ပင်ထိုးခြင်း"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"ပင်ထိုးရန်"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"အလွတ်ပုံစံ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"အက်ပ်အသုံးပြုမှု ဆက်တင်များ"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"အက်ပ်အကြံပြုချက်များ ဖွင့်ထားသည်"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"အက်ပ်အကြံပြုချက်များကို ပိတ်ထားသည်"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ကြိုတင်မှန်းဆထားသော အက်ပ်− <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ဘယ်ဘက်အစွန် ခပ်လှမ်းလှမ်းမှ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ဘယ်ဘက်အစွန်မှ ဖန်သားပြင်အလယ်သို့ ပွတ်ဆွဲပြီး လွှတ်လိုက်ကြောင်း သေချာပါစေ။"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"ရပါပြီ။ ညာဘက်အစွန်မှနေ၍ ယခု ပွတ်ဆွဲကြည့်ပါ။"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ညာဘက်အစွန် ခပ်လှမ်းလှမ်းမှ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ညာဘက်အစွန်မှ ဖန်သားပြင်အလယ်သို့ ပွတ်ဆွဲပြီး လွှတ်လိုက်ကြောင်း သေချာပါစေ။"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"နောက်ဆုတ်လက်ဟန် ရှင်းလင်းပို့ချချက် ပြီးပါပြီ။ နောက်အဆင့်တွင် ပင်မစာမျက်နှာသို့ သွားနည်းကို လေ့လာပါ။"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ဖန်သားပြင် အောက်ခြေနှင့် အလွန်နီးကပ်စွာ ပွတ်ဆွဲခြင်းမရှိကြောင်း သေချာပါစေ။"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"နောက်ဆုတ်လက်ဟန်၏ အာရုံခံစွမ်းကိုပြောင်းရန် ‘ဆက်တင်များ’ သို့ သွားပါ"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"နောက်ပြန်သွားရန် ပွတ်ဆွဲပါ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"နောက်ဆုံးဖန်သားပြင်သို့ ပြန်သွားရန် ဘယ်ဘက် (သို့) ညာဘက်အစွန်မှ ဖန်သားပြင်အလယ်သို့ ပွတ်ဆွဲပါ။"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ဖန်သားပြင် အောက်ခြေအစွန်မှ အပေါ်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"လက်မလွှတ်ခင် ခဏရပ်ခြင်းမရှိကြောင်း သေချာပါစေ။"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"အပေါ်တည့်တည့်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် ရှင်းလင်းပို့ချချက် ပြီးပါပြီ။ နောက်အဆင့်တွင် အက်ပ်များပြောင်းနည်းကို လေ့လာပါ။"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ပင်မစာမျက်နှာသို့သွားရန် ပွတ်ဆွဲပါ"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"သင့်ဖန်သားပြင် အောက်ခြေမှ အပေါ်သို့ပွတ်ဆွဲပါ။ ဤလက်ဟန်ဖြင့် ပင်မစာမျက်နှာသို့ အမြဲပြန်သွားနိုင်သည်။"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ဖန်သားပြင် အောက်ခြေအစွန်မှ အပေါ်သို့ ပွတ်ဆွဲကြောင်း သေချာပါစေ။"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"မလွှတ်ခင် ဝင်းဒိုးကို အချိန်ကြာကြာ ဖိထားကြည့်ပါ။"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"အပေါ်တည့်တည့်သို့ ပွတ်ဆွဲပြီးနောက် ခဏရပ်ကြောင်း သေချာပါစေ။"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"အက်ပ်များပြောင်းသည့် လက်ဟန် ရှင်းလင်းပို့ချချက် ပြီးပါပြီ။ သင့်ဖုန်းကို သုံးရန် အဆင်သင့်ဖြစ်ပါပြီ။"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"အက်ပ်များပြောင်းရန် ပွတ်ဆွဲပါ"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"သင့်ဖန်သားပြင် အောက်ခြေမှ အပေါ်သို့ ပွတ်ဆွဲပါ၊ ဖိထားပြီးနောက် လွှတ်လိုက်ပါ။"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"အားလုံးအဆင်သင့်ဖြစ်ပါပြီ"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"ရှေ့သို့"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"ပြီးပြီ"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ဆက်တင်များ"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ထပ်စမ်းကြည့်ရန်"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ကောင်းသည်။"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ရှင်းလင်းပို့ချချက် <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</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>
 </resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 161d1c4..6e7e4a3 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Delt skjerm"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fest"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nylige elementer"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appforslag er på"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appforslag er slått av"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Foreslått app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Sørg for at du sveiper fra kanten helt til venstre."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Sørg for at du sveiper fra den venstre kanten til midten av skjermen og slipper."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Så enkelt er det! Prøv nå å sveipe fra den høyre kanten."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Sørg for at du sveiper fra kanten helt til høyre."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Sørg for at du sveiper fra den høyre kanten til midten av skjermen og slipper."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Du har fullført bevegelsen for å gå tilbake. I neste trinn ser du hvordan du går til startskjermen."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Sørg for at du ikke sveiper for nær bunnen av skjermen."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Gå til Innstillinger for å endre tilbakebevegelsefølsomheten"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Sveip for å gå tilbake"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"For å gå tilbake til forrige skjerm, sveip fra venstre eller høyre kant til midten av skjermen."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Sørg for at du sveiper opp fra den nederste kanten av skjermen."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Sørg for at du ikke setter på pause før du slipper."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Sørg for at du sveiper rett opp."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Du har fullført bevegelsen for å gå til startskjermen. I neste trinn lærer du hvordan du bytter app."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Sveip for å gå til startskjermen"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Sveip opp fra bunnen av skjermen. Denne bevegelsen tar deg alltid til startskjermen."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Sørg for at du sveiper opp fra den nederste kanten av skjermen."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Prøv å holde vinduet lenger før du slipper."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Sørg for at du sveiper rett opp, og så stopper du."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Du har fullført bevegelsen for å bytte app. Du er klar til å bruke telefonen."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Sveip for å bytte app"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Sveip opp fra bunnen av skjermen, hold og slipp."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Ferdig"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Neste"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Ferdig"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Innstillinger"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Prøv igjen"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bra!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Veiledning <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Del"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skjermdump"</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>
 </resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 99a22f9..99ad814 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"स्क्रिन विभाजन गर्नुहोस्"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन गर्नुहोस्"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"फ्रिफर्म"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"हालसालैको कुनै पनि वस्तु छैन"</string>
@@ -29,9 +28,9 @@
     <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="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="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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"गृह स्क्रिनबाटै आफूले सबैभन्दा बढी प्रयोग गर्ने एप सजिलै चलाउनुहोस्। सिफारिस गरिने एपहरूको क्रम तपाईंले एप प्रयोग गर्ने समयतालिकाअनुसार बदलिने छ। फेदको पङ्क्तिमा रहेका एपहरू तपाईंको गृह स्क्रिनको सिरानमा सर्ने छन्।"</string>
@@ -46,7 +45,72 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"सिफारिस गरिएका एपहरू देखाउने सुविधा सक्षम पारिएका छन्"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"सिफारिस गरिएका एपहरू देखाउने सुविधा असक्षम पारिएको छ"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"पूर्वानुमान गरिएको एप: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <!-- no translation found for back_gesture_feedback_swipe_too_far_from_left_edge (340972404868601012) -->
+    <skip />
+    <!-- no translation found for back_gesture_feedback_cancelled_left_edge (6671316150388702530) -->
+    <skip />
+    <!-- no translation found for back_gesture_feedback_complete_left_edge (3220478647881674266) -->
+    <skip />
+    <!-- no translation found for back_gesture_feedback_swipe_too_far_from_right_edge (4306700023773832353) -->
+    <skip />
+    <!-- no translation found for back_gesture_feedback_cancelled_right_edge (4951916546256902552) -->
+    <skip />
+    <!-- no translation found for back_gesture_feedback_complete (7261221999760772210) -->
+    <skip />
+    <!-- no translation found for back_gesture_feedback_swipe_in_nav_bar (1148198467090405643) -->
+    <skip />
+    <!-- no translation found for back_gesture_tutorial_confirm_subtitle (5181305411668713250) -->
+    <skip />
+    <!-- no translation found for back_gesture_intro_title (19551256430224428) -->
+    <skip />
+    <!-- no translation found for back_gesture_intro_subtitle (7912576483031802797) -->
+    <skip />
+    <!-- no translation found for home_gesture_feedback_swipe_too_far_from_edge (1446774096007065298) -->
+    <skip />
+    <!-- no translation found for home_gesture_feedback_overview_detected (1557523944897393013) -->
+    <skip />
+    <!-- no translation found for home_gesture_feedback_wrong_swipe_direction (6993979358080825438) -->
+    <skip />
+    <!-- no translation found for home_gesture_feedback_complete (2324789183070815517) -->
+    <skip />
+    <!-- no translation found for home_gesture_intro_title (836590312858441830) -->
+    <skip />
+    <!-- no translation found for home_gesture_intro_subtitle (2632238748497975326) -->
+    <skip />
+    <!-- no translation found for overview_gesture_feedback_swipe_too_far_from_edge (3032757898111577225) -->
+    <skip />
+    <!-- no translation found for overview_gesture_feedback_home_detected (1411130969354020489) -->
+    <skip />
+    <!-- no translation found for overview_gesture_feedback_wrong_swipe_direction (6725820500906747925) -->
+    <skip />
+    <!-- no translation found for overview_gesture_feedback_complete (5477014491632199169) -->
+    <skip />
+    <!-- no translation found for overview_gesture_intro_title (2902054412868489378) -->
+    <skip />
+    <!-- no translation found for overview_gesture_intro_subtitle (1579517193845186042) -->
+    <skip />
+    <!-- no translation found for gesture_tutorial_confirm_title (6201516182040074092) -->
+    <skip />
+    <!-- no translation found for gesture_tutorial_action_button_label_next (2556263116424738762) -->
+    <skip />
+    <!-- no translation found for gesture_tutorial_action_button_label_done (671834508127014231) -->
+    <skip />
+    <!-- no translation found for gesture_tutorial_action_button_label_settings (2923621047916486604) -->
+    <skip />
+    <!-- no translation found for gesture_tutorial_try_again (65962545858556697) -->
+    <skip />
+    <!-- no translation found for gesture_tutorial_nice (2936275692616928280) -->
+    <skip />
+    <!-- no translation found for gesture_tutorial_step (1279786122817620968) -->
+    <skip />
     <string name="action_share" msgid="2648470652637092375">"सेयर गर्नुहोस्"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"स्क्रिनसट"</string>
     <string name="blocked_by_policy" msgid="2071401072261365546">"यो एप वा तपाईंको सङ्गठनले यो कारबाही गर्ने अनुमति दिँदैन"</string>
+    <!-- no translation found for skip_tutorial_dialog_title (2725643161260038458) -->
+    <skip />
+    <string name="skip_tutorial_dialog_subtitle" msgid="544063326241955662">"तपाईं पछि <xliff:g id="NAME">%1$s</xliff:g> एपमा गई यो ट्युटोरियल भेट्टाउन सक्नुहुन्छ"</string>
+    <!-- no translation found for gesture_tutorial_action_button_label_cancel (3809842569351264108) -->
+    <skip />
+    <!-- no translation found for gesture_tutorial_action_button_label_skip (394452764989751960) -->
+    <skip />
 </resources>
diff --git a/quickstep/res/values-night/styles.xml b/quickstep/res/values-night/styles.xml
new file mode 100644
index 0000000..1bd3f5d
--- /dev/null
+++ b/quickstep/res/values-night/styles.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.
+-->
+<resources>
+
+    <style name="AllSetTheme" parent="@android:style/Theme.DeviceDefault.NoActionBar">
+        <item name="android:navigationBarColor">@android:color/transparent</item>
+        <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>
+    </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 b814d39..be9329f 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Gesplitst scherm"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Vastzetten"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Vrije vorm"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Geen recente items"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App-suggesties zijn ingeschakeld"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App-suggesties zijn uitgeschakeld"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Voorspelde app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Swipe helemaal vanaf de linkerrand."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Swipe vanaf de linkerrand naar het midden van het scherm en laat los."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Dat is alles. Swipe nu vanaf de rechterrand."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Swipe helemaal vanaf de rechterrand."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Swipe vanaf de rechterrand naar het midden van het scherm en laat los."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Je hebt het gebaar Terug voltooid. Ontdek nu hoe je naar het startscherm gaat."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Swipe niet te dicht bij de onderkant van het scherm."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Open Instellingen om de gevoeligheid van Terug te wijzigen"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Swipe om terug te gaan"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Swipe vanaf de linker- of rechterrand naar het midden om terug te gaan naar het vorige scherm."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Swipe vanaf de onderrand van het scherm omhoog."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pauzeer niet voordat je loslaat."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Swipe recht omhoog."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Je hebt het gebaar Naar startscherm voltooid. Ontdek nu hoe je tussen apps schakelt."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Swipe om naar het startscherm te gaan"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swipe omhoog vanaf de onderkant van het scherm. Met dit gebaar ga je altijd naar het startscherm."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Swipe vanaf de onderrand van het scherm omhoog."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Houd het venster langer vast voordat je loslaat."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Swipe recht omhoog en pauzeer dan."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Je hebt het gebaar Schakelen tussen apps voltooid. Je kunt nu je telefoon gebruiken."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swipe om tussen apps te schakelen"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Swipe omhoog vanaf de onderkant van het scherm, houd vast en laat dan los."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Klaar"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Volgende"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Klaar"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Instellingen"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Opnieuw"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Dat gaat lekker."</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Delen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 1a63e11..8c36972 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"ସ୍କ୍ରୀନ୍‌କୁ ଭାଗ କରନ୍ତୁ"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"ପିନ୍‍"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ଫ୍ରିଫର୍ମ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକୁ ସକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକୁ ଅକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ପୂର୍ବାନୁମାନ କରାଯାଇଥିବା ଆପ୍: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ଆପଣ ଦୂର-ବାମ ଧାରରୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ଆପଣ ସ୍କ୍ରିନର ବାମ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ୍ କରି ଛାଡ଼ି ଦେଉଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"କେବଳ ଏତିକି! ବର୍ତ୍ତମାନ ଡାହାଣ ଧାରରୁ ସ୍ୱାଇପ୍ କରି ଦେଖନ୍ତୁ।"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ଆପଣ ଦୂର-ଡାହାଣ ଧାରରୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ଆପଣ ସ୍କ୍ରିନର ଡାହାଣ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ୍ କରି ଛାଡ଼ି ଦେଉଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"ଆପଣ \'ପଛକୁ ଫେରନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି। ତା\'ପରେ, ମୂଳପୃଷ୍ଠାକୁ କିପରି ଯିବେ ଜାଣନ୍ତୁ।"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ଆପଣ ସ୍କ୍ରିନର ତଳଭାଗର ଅତି ନିକଟରୁ ସ୍ୱାଇପ୍ କରୁନଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ପଛକୁ ଫେରିବା ଜେଶ୍ଚରର ସମ୍ବେଦନଶୀଳତା ବଦଳାଇବାକୁ ସେଟିଂସକୁ ଯାଆନ୍ତୁ"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"ପଛକୁ ଫେରିବା ପାଇଁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ପୂର୍ବବର୍ତ୍ତୀ ସ୍କ୍ରିନକୁ ଫେରିବା ପାଇଁ, ସ୍କ୍ରିନର ବାମ କିମ୍ବା ଡାହାଣ ଧାରରୁ ମଝିକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ।"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ଆପଣ ଛାଡ଼ିବା ପୂର୍ବରୁ ବିରତ କରୁନଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"ଆପଣ \'ମୂଳପୃଷ୍ଠାକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି। ତା\'ପରେ, ଆପଗୁଡ଼ିକୁ କିପରି ସ୍ୱିଚ୍ କରିବେ ଜାଣନ୍ତୁ।"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ମୂଳପୃଷ୍ଠାକୁ ଯିବା ପାଇଁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ଆପଣଙ୍କ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ। ଏହି ଜେଶ୍ଚର୍ ସର୍ବଦା ଆପଣଙ୍କୁ ମୂଳସ୍କ୍ରିନକୁ ନେଇଥାଏ।"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ଆପଣ ସ୍କ୍ରିନର ତଳ ଧାରରୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ୱିଣ୍ଡୋକୁ ରିଲିଜ୍ କରିବା ପୂର୍ବରୁ ଅଧିକ ସମୟ ଧରି ରଖିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ଆପଣ ସିଧା ଉପରକୁ ସ୍ୱାଇପ୍ କରି ତା\'ପରେ ବିରତ କରୁଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"ଆପଣ \'ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ୍ କରନ୍ତୁ\' ଜେଶ୍ଚର୍ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି। ଆପଣ ଆପଣଙ୍କ ଫୋନ୍ ବ୍ୟବହାର କରିବାକୁ ପ୍ରସ୍ତୁତ ଅଛନ୍ତି!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ଆପଗୁଡ଼ିକୁ ସ୍ୱିଚ୍ କରିବା ପାଇଁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"ଆପଣଙ୍କ ସ୍କ୍ରିନର ତଳୁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ, ଧରି ରଖନ୍ତୁ, ତା\'ପରେ ରିଲିଜ୍ କରନ୍ତୁ।"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ସବୁ ପ୍ରସ୍ତୁତ"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"ପରବର୍ତ୍ତୀ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"ହୋଇଗଲା"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ସେଟିଂସ୍"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ବଢ଼ିଆ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ଟ୍ୟୁଟୋରିଆଲ୍ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"ସେୟାର୍ କରନ୍ତୁ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ସ୍କ୍ରିନସଟ୍"</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>
 </resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 22fa519..227c060 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"ਪਿੰਨ ਕਰੋ"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"ਫ੍ਰੀਫਾਰਮ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ਐਪ ਸੁਝਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ਐਪ ਸੁਝਾਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"ਪੂਰਵ ਅਨੁਮਾਨਿਤ ਐਪ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਦੂਰ-ਖੱਬੇ ਕਿਨਾਰੇ ਤੋਂ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਖੱਬੇ ਕਿਨਾਰੇ ਤੋਂ ਸਕ੍ਰੀਨ ਦੇ ਵਿਚਕਾਰ ਤੱਕ ਸਵਾਈਪ ਕਰਦੇ ਹੋ ਅਤੇ ਛੱਡ ਦਿੰਦੇ ਹੋ।"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"ਹੋ ਗਿਆ! ਹੁਣ ਸੱਜੇ ਕਿਨਾਰੇ ਤੋਂ ਸਵਾਈਪ ਕਰਕੇ ਦੇੇਖੋ।"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਦੂਰ-ਸੱਜੇ ਕਿਨਾਰੇ ਤੋਂ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸੱਜੇ ਕਿਨਾਰੇ ਤੋਂ ਸਕ੍ਰੀਨ ਦੇ ਵਿਚਕਾਰ ਤੱਕ ਸਵਾਈਪ ਕਰਦੇ ਹੋ ਅਤੇ ਛੱਡ ਦਿੰਦੇ ਹੋ।"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"ਤੁਸੀਂ \'ਵਾਪਸ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ। ਅੱਗੇ, ਜਾਣੋ ਕਿ ਹੋਮ \'ਤੇ ਕਿਵੇਂ ਜਾਣਾ ਹੈ।"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਹਿੱਸੇ ਦੇ ਬਹੁਤ ਨੇੜੇ ਸਵਾਈਪ ਨਾ ਕਰੋ।"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ਪਿੱਛੇ ਜਾਣ ਦੇ ਸੰਕੇਤ ਦੀ ਸੰਵੇਦਨਸ਼ੀਲਤਾ ਬਦਲਣ ਲਈ, ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"ਪਿੱਛੇ ਜਾਣ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"ਪਿਛਲੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵਾਪਸ ਜਾਣ ਲਈ, ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਕਿਨਾਰੇ ਤੋਂ ਸਕ੍ਰੀਨ ਦੇ ਵਿਚਕਾਰ ਤੱਕ ਸਵਾਈਪ ਕਰੋ।"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਕਿਨਾਰੇ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ਪੱਕਾ ਕਰੋ ਕਿ ਸਕ੍ਰੀਨ ਨੂੰ ਛੱਡਣ ਤੋਂ ਪਹਿਲਾਂ ਰੁਕੋ ਨਾ।"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਿੱਧੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"ਤੁਸੀਂ ਹੋਮ \'ਤੇ ਜਾਓ ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ। ਅੱਗੇ, ਜਾਣੋ ਕਿ ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਿਵੇਂ ਕਰਨੀ ਹੈ।"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ। ਇਹ ਸੰਕੇਤ ਹਮੇਸ਼ਾਂ ਤੁਹਾਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲੈ ਜਾਂਦਾ ਹੈ।"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਕਿਨਾਰੇ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਦੇ ਹੋ।"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ਛੱਡਣ ਤੋਂ ਪਹਿਲਾਂ ਵਿੰਡੋ ਨੂੰ ਕੁਝ ਸਮੇਂ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ਇਹ ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਸਿੱਧੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਦੇ ਹੋ, ਫਿਰ ਰੋਕੋ।"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"ਤੁਸੀਂ ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ। ਤੁਸੀਂ ਆਪਣੇ ਫ਼ੋਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਤਿਆਰ ਹੋ!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਸਵਾਈਪ ਕਰੋ"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ, ਅਤੇ ਫਿਰ ਛੱਡੋ।"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"ਪੂਰੀ ਤਰ੍ਹਾਂ ਤਿਆਰ"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"ਅੱਗੇ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"ਹੋ ਗਿਆ"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ਸੈਟਿੰਗਾਂ"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ਵਧੀਆ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ਟਿਊਟੋਰੀਅਲ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"ਸਾਂਝਾ ਕਰੋ"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</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>
 </resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 33ea9b4..61d9b93 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Podziel ekran"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Przypnij"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Tryb dowolny"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Brak ostatnich elementów"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Włączono sugestie aplikacji"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugestie aplikacji są wyłączone"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Przewidywana aplikacja: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Pamiętaj, aby przesuwać palcem od samej lewej krawędzi."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Pamiętaj, aby przesuwać palcem od lewej krawędzi do środka ekranu, a potem podnieść palec."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Dobrze. Teraz przesuń palcem od prawej krawędzi."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Pamiętaj, aby przesuwać palcem od samej prawej krawędzi."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Pamiętaj, aby przesuwać palcem od prawej krawędzi do środka ekranu, a potem podnieść palec."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Gest przejścia wstecz został opanowany. Poćwicz teraz przechodzenie na ekran główny."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pamiętaj, aby nie przesuwać palcem zbyt blisko dolnej części ekranu."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Czułość gestu cofania możesz zmienić w Ustawieniach"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Przesuwanie palcem, aby przejść wstecz"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Aby wrócić do ostatniego ekranu, przesuń palcem od lewej lub prawej krawędzi do środka ekranu."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pamiętaj, aby przesuwać palcem od dolnej krawędzi ekranu."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pamiętaj, aby przed podniesieniem palca nie było przerwy."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pamiętaj, aby przesuwać palcem prosto do góry."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Gest przechodzenia na ekran główny został opanowany. Poćwicz teraz przełączanie aplikacji."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Przesuwanie palcem, aby przejść na ekran główny"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Przesuń palcem z dołu ekranu. Ten gest zawsze powoduje przejście na ekran główny."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pamiętaj, aby przesuwać palcem od dolnej krawędzi ekranu."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Przytrzymaj okno dłużej, zanim podniesiesz palec."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pamiętaj, aby przesuwać palcem prosto do góry, a potem przerwać ruch."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Gest przełączania aplikacji został opanowany. Możesz już używać telefonu."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Przesuwanie palcem, aby przełączać aplikacje"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Przesuń palcem od dołu ekranu, przytrzymaj i puść."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Wszystko gotowe"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Dalej"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Gotowe"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ustawienia"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Spróbuj ponownie"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Super!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Samouczek <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Udostępnij"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Zrzut 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>
 </resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index cff747f..0feead7 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ecrã dividido"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
@@ -30,8 +29,8 @@
     <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 aplicações"</string>
-    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"As suas aplicações previstas"</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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Aceda facilmente às suas apps mais utilizadas, diretamente no ecrã principal. As sugestões mudam em função das suas rotinas. As apps na última fila passam para o ecrã principal."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugestões de apps ativadas"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"As sugestões de apps estão desativadas"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Garanta que desliza rapidamente com o dedo a partir da extremidade esquerda."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Garanta que desliza rapidamente com o dedo do limite esquerdo até ao centro do ecrã e solta."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Já está! Agora deslize rapidamente do limite direito."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Garanta que desliza rapidamente com o dedo a partir da extremidade direita."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Garanta que desliza rapidamente com o dedo do limite direito até ao centro do ecrã e solta."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Concluiu o gesto para retroceder. A seguir, saiba como aceder à Página inicial."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Garanta que não desliza rapidamente com o dedo demasiado perto da parte inferior do ecrã."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Altere a sensibilidade do gesto para voltar nas Definições."</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Deslize rapidamente com o dedo para retroceder"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para voltar ao último ecrã, deslize rapidamente do limite esquerdo ou direito até ao centro do ecrã."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Garanta que desliza rapidamente com o dedo a partir do limite inferior do ecrã."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Garanta que não faz uma pausa antes de soltar."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Garanta que desliza rapidamente com o dedo para cima."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Concluiu o gesto para aceder à Página inicial. A seguir, saiba como alternar entre apps."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Deslize rapidamente com o dedo para aceder à Página inicial"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Deslize rapidamente para cima a partir da parte inferior. Este gesto abre sempre o ecrã principal."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Garanta que desliza rapidamente com o dedo a partir do limite inferior do ecrã."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Experimente premir a janela durante mais tempo antes de soltar."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Garanta que desliza rapidamente com o dedo para cima e, em seguida, faz uma pausa."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Concluiu o gesto para alternar entre apps. Já pode utilizar o seu telemóvel!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Deslize rapidamente com o dedo para alternar entre apps"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Deslize rapidamente para cima a partir da parte inferior do ecrã sem soltar e, em seguida, solte."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Está tudo pronto"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Seguinte"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Concluído"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Definições"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Tente novamente"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Boa!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Partilhar"</string>
-    <string name="action_screenshot" msgid="8171125848358142917">"Tirar captura de ecrã"</string>
+    <string name="action_screenshot" msgid="8171125848358142917">"Fazer captura de ecrã"</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>
 </resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 7c80a58..f533bbc 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Tela dividida"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"O recurso \"sugestões de apps\" está ativado"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"O recurso \"sugestões de apps\" está desativado"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App previsto: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Deslize da borda esquerda."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Deslize da borda esquerda até o meio da tela e solte."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Pronto! Agora tente deslizar da borda direita."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Deslize da borda direita."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Deslize da borda direita até o meio da tela e solte."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Você concluiu o gesto para voltar. A seguir, aprenda a acessar a tela inicial."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Não deslize perto demais da parte inferior da tela."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Mude a sensibilidade do gesto de voltar nas configurações"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Deslize para voltar"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para voltar à tela anterior, deslize da borda esquerda ou direita até o meio da tela."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Deslize da borda inferior da tela para cima."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Não pare antes de soltar."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Deslize para cima."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Você concluiu o gesto para acessar a tela inicial. A seguir, aprenda a trocar de app."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Deslizar para voltar à tela inicial"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Deslize de baixo para cima na tela. Esse gesto sempre leva você para a tela inicial."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Deslize da borda inferior da tela para cima."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Mantenha a janela pressionada por mais tempo antes de soltar."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Deslize para cima e pare."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Você concluiu o gesto para trocar de app. Você já pode usar o smartphone."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Deslizar para trocar de app"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Deslize de baixo para cima na tela, mantenha-a pressionada e depois solte."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Tudo pronto"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Próxima"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Concluído"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Configurações"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Tentar novamente"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Muito bem!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Compartilhar"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Capturar 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>
 </resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index e13ed18..37d2935 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ecran împărțit"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixați"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Formă liberă"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Niciun element recent"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugestiile de aplicații au fost activate"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugestiile de aplicații au fost dezactivate"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicația estimată: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Glisați dinspre marginea stângă îndepărtată."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Glisați dinspre marginea stângă spre mijlocul ecranului și eliberați."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Atât! Acum încercați să glisați dinspre marginea dreaptă."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Glisați dinspre marginea dreaptă îndepărtată."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Glisați dinspre marginea dreaptă spre mijlocul ecranului și eliberați."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Ați finalizat gestul „Înapoi”. În continuare, aflați cum să accesați pagina de pornire."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nu glisați prea aproape de partea de jos a ecranului."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Schimbați sensibilitatea gestului „Înapoi” accesând Setările"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Glisați pentru a reveni"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Pentru a reveni la ultimul ecran, glisați de la marginea stângă sau dreaptă spre mijlocul ecranului."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Glisați în sus dinspre marginea de jos a ecranului."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Nu întrerupeți gestul înainte de a elibera."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Glisați direct în sus."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Ați finalizat gestul „Pagina de pornire”. În continuare, aflați cum să comutați aplicațiile."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Glisați pentru a accesa pagina de pornire"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Glisați în sus din partea de jos a ecranului. Cu acest gest accesați întotdeauna ecranul de pornire."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Glisați în sus dinspre marginea de jos a ecranului."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Încercați să țineți fereastra mai mult înainte s-o eliberați."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Glisați direct în sus, apoi întrerupeți."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Ați finalizat gestul „schimbați aplicațiile”. Sunteți gata să vă folosiți telefonul!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Glisați pentru a comuta între aplicații"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Glisați în sus din partea de jos a ecranului, așteptați, apoi eliberați."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Gata"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Înainte"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Gata"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Setări"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Reîncercați"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bravo!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorialul <xliff:g id="CURRENT">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Distribuiți"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Captură de ecran"</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>
 </resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 736bb14..8d4c40a 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Разделить экран"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Закрепить"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Произвольная форма"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Здесь пока ничего нет."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Функция \"Рекомендуемые приложения\" включена."</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Функция \"Рекомендуемые приложения\" отключена."</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Рекомендуемое приложение: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Проведите слева направо от самого края экрана."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Проведите от левого края экрана до середины дисплея и отпустите палец."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Готово! Теперь попробуйте провести от правого края экрана."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Проведите справа налево от самого края экрана."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Проведите от правого края экрана до середины дисплея и отпустите палец."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Вы выполнили жест \"Назад\". Далее мы расскажем, как переходить на главный экран."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Проведите пальцем не слишком близко к нижнему краю экрана."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Уровень чувствительности можно изменить в настройках."</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Возврат к предыдущему экрану"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Чтобы вернуться к предыдущему экрану, проведите от левого или правого края дисплея к центру."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Проведите снизу вверх от самого края экрана."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Не приостанавливайтесь перед тем, как отпустить палец."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Проведите по экрану ровно вверх."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Вы выполнили жест \"На главный экран\". Далее мы расскажем, как переключаться между приложениями."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Переход на главный экран"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Проведите вверх от нижнего края дисплея. Этот жест открывает главный экран."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Проведите снизу вверх от самого края экрана."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Прежде чем отпускать палец, задержите его на дисплее подольше."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Проведите по экрану ровно вверх, а затем задержите палец в крайнем положении."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Вы выполнили жест \"Переключение между приложениями\". Теперь вы готовы начать работу с телефоном."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Переключение между приложениями"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Проведите вверх от нижнего края экрана, задержите палец в крайнем положении, а затем отпустите."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Готово"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Далее"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Готово"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Настройки"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Повторите попытку"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Поздравляем!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Руководство (шаг <xliff:g id="CURRENT">%1$d</xliff:g> из <xliff:g id="TOTAL">%2$d</xliff:g>)"</string>
     <string name="action_share" msgid="2648470652637092375">"Поделиться"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</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>
 </resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 7deb5f6..d526e13 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"බෙදුම් තිරය"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"අමුණන්න"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"මෑත අයිතම නැත"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"යෙදුම් යෝජනා සබලිතයි"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"යෙදුම් යෝජනා අබල කර ඇත"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"පුරෝකථනය කළ යෙදුම: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ඔබ ඈත වම් දාරයෙන් ස්වයිප් කරන බවට සහතික කර ගන්න."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ඔබ වම් දාරයේ සිට තිරයේ මැදට ස්වයිප් කර අත හරින බව සහතික කර ගන්න."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"එච්චරයි! දැන් දකුණු දාරයෙන් ස්වයිප් කිරීම උත්සාහ කරන්න."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ඔබ ඈත දකුණු දාරයෙන් ස්වයිප් කරන බවට සහතික කර ගන්න."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ඔබ දකුණු දාරයේ සිට තිරයේ මැදට ස්වයිප් කර අත හරින බව සහතික කර ගන්න."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"ඔබ ආපසු යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි. ඊළඟට, මුල් පිටුවට යන ආකාරය දැන ගන්න."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ඔබ තිරයේ පහළට ඉතාම සමීපව ස්වයිප් නොකරන බවට සහතික කර ගන්න."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"ආපසු ඉංගිතයෙහි සංවේදීතාව වෙනස් කිරීමට, සැකසීම් වෙත යන්න"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"ආපසු යාමට ස්වයිප් කරන්න"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"අවසාන තිරයට ආපසු යාමට, වම් හෝ දකුණු දාරයෙන් තිරයේ මැදට ස්වයිප් කරන්න."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ඔබ තිරයේ පහළ දාරයේ සිට ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"යාමට ඉඩ හැරීමට පෙර ඔබ විරාමයක් නොගන්නා බව සහතික කර ගන්න."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ඔබ කෙලින්ම ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි. ඊළඟට, යෙදුම් මාරු කරන ආකාරය දැන ගන්න."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"මුල් පිටුවට යාමට ස්වයිප් කරන්න"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ඔබගේ තිරයේ පහළින් උඩට ස්වයිප් කරන්න.මෙම ඉංගිතය සැම විටම ඔබව මුල් තිරයට ගෙන යයි."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ඔබ තිරයේ පහළ දාරයේ සිට ඉහළට ස්වයිප් කරන බව සහතික කර ගන්න."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"මුදා හැරීමට පෙර කවුළුව වැඩි වේලාවක් රඳවා තබා ගැනීමට උත්සාහ කරන්න."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ඔබ කෙලින්ම ඉහළට ස්වයිප් කර, අනතුරුව විරාම කරන බව සහතික කර ගන්න."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"ඔබ යෙදුම් මාරු කිරීමේ ඉංගිතය සම්පූර්ණ කළේය. ඔබ ඔබගේ දුරකථනය භාවිත කිරීමට සූදානම්ය!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"යෙදුම් මාරු කිරීමට ස්වයිප් කරන්න"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"ඔබගේ තිරයේ පහළින් උඩට ස්වයිප් කර, අල්ලාගෙන සිට, අනතුරුව මුදා හරින්න."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"සියල්ල සකසා ඇත"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"ඊළඟ"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"නිමයි"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"සැකසීම්"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"නැවත උත්සාහ කරන්න"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"කදිමයි!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"නිබන්ධනය <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"බෙදා ගන්න"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"තිර රුව"</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>
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 9c8f95f..24a4ac9 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Rozdeliť obrazovku"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Pripnúť"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Voľný režim"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Žiadne nedávne položky"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Návrhy aplikácií zapnuté"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Návrhy aplikácií vypnuté"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predpovedaná aplikácia: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Musite potiahnuť z úplne krajného ľavého okraja."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Musíte potiahnuť z ľavého okraja do stredu obrazovky a uvoľniť."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Hotovo! Teraz skúste potiahnuť z pravého okraja."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Musite potiahnuť z úplne krajného pravého okraja."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Musíte potiahnuť z pravého okraja do stredu obrazovky a uvoľniť."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Dokončili ste gesto na prejdenie späť. V ďalšom kroku sa naučíte, ako prejsť na plochu."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Nesmiete potiahnuť príliš blízko dolnej časti obrazovky."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Ak chcete zmeniť citlivosť gesta Späť, prejdite do Nastavení"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Prejdite späť potiahnutím"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Na poslednú obrazovku prejdete potiahnutím z ľavého alebo pravého okraja do stredu obrazovky."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Musíte potiahnuť nahor z dolného okraja obrazovky."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pred uvoľnením nesmiete zastať."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Musíte potiahnuť priamo hore."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Dokončili ste gesto na prechod na plochu. V ďalšom kroku sa naučíte, ako prepínať aplikácie."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Prechod na plochu potiahnutím"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Potiahnite nahor z dolného okraja obrazovky. Týmto gestom sa vždy vrátite na plochu."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Musíte potiahnuť nahor z dolného okraja obrazovky."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Skúste okno pred uvoľnením podržať dlhšie."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Musite potiahnuť priamo hore a potom zastať."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Dokončili ste gesto na prepnutie aplikácií. Môžete začať používať telefón."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Prepínanie aplikácií potiahnutím"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Potiahnite nahor z dolnej časti obrazovky, pridržte a uvoľnite."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Hotovo"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Ďalej"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Hotovo"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Nastavenia"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Skúste to znova"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Výborne!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Návod <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Zdieľať"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Snímka obrazovky"</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>
 </resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 5d45f6d..fdc8a6a 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Razdeljen zaslon"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Pripni"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Prosta oblika"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ni nedavnih elementov"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlogi aplikacij so omogočeni"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlogi aplikacij so onemogočeni"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predvidena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Pazite, da ne povlečete s skrajno levega roba."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Pazite, da povlečete z levega roba do sredine zaslona in dvignete prst."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"To je vse. Zdaj preizkusite vlečenje z levega roba."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Pazite, da ne povlečete s skrajno desnega roba."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Pazite, da povlečete z desnega roba do sredine zaslona in dvignete prst."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Izvedli ste potezo za nazaj. Zdaj se naučite, kako se pomaknete na začetni zaslon."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Pazite, da ne povlečete preblizu dna zaslona."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Občutljivost poteze za nazaj lahko spremenite v nastavitvah."</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Povlecite za vrnitev."</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Če se želite vrniti na zadnji zaslon, povlecite z levega ali desnega roba do sredine zaslona."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Pazite, da povlečete s spodnjega roba zaslona navzgor."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Pazite, da ne zaustavite prsta, preden ga dvignete."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Pazite, da povlečete naravnost navzgor."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Izvedli ste potezo za pomik na začetni zaslon. Zdaj se naučite preklapljanja med aplikacijami."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Vlečenje za pomik na začetni zaslon"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Z dna zaslona s prstom povlecite navzgor. S to potezo lahko vedno odprete začetni zaslon."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Pazite, da povlečete s spodnjega roba zaslona navzgor."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Poskusite okno pridržati dalj časa, preden ga izpustite."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Pazite, da povlečete naravnost navzgor in nato zaustavite prst."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Izvedli ste potezo za preklapljanje med aplikacijami. Pripravljeni ste na uporabo telefona."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Vlečenje za preklapljanje med aplikacijami"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Povlecite navzgor z dna zaslona, pridržite, nato izpustite."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Zdaj znate"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Naprej"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Končano"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Nastavitve"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Poskusite znova"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Odlično!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Vadnica <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Deli"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Posnetek 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>
 </resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index a144b92..36e0afc 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ekrani i ndarë"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Gozhdo"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Formë e lirë"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nuk ka asnjë artikull të fundit"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Aplikacionet e sugjeruara janë aktivizuar"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugjerimet e aplikacioneve janë çaktivizuar"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplikacioni i parashikuar: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Sigurohu që të rrëshqasësh shpejt nga skaji sa më majtas."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Sigurohu që të rrëshqasësh shpejt nga skaji i majtë drejt mesit të ekranit dhe lëshoje."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Kaq ishte! Tani provo të rrëshqasësh shpejt nga skaji i djathtë."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Sigurohu që të rrëshqasësh shpejt nga skaji sa më djathtas."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Sigurohu që të rrëshqasësh shpejt nga skaji i djathtë drejt mesit të ekranit dhe lëshoje."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"E ke përfunduar gjestin e kthimit prapa. Në vijim do të mësosh si të shkosh tek ekrani bazë."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Sigurohu që të mos rrëshqasësh shumë afër fundit të ekranit."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Për të ndryshuar ndjeshmërinë e gjestit të kthimit prapa, shko te \"Cilësimet\""</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Rrëshqit shpejt për t\'u kthyer prapa"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Për t\'u kthyer prapa tek ekrani i fundit, rrëshqit shpejt nga skaji i majtë ose i djathtë drejt mesit të ekranit"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Sigurohu që të rrëshqasësh shpejt lart nga skaji i poshtëm i ekranit."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Sigurohu që të mos ndalosh para se ta lëshosh."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Sigurohu që të rrëshqasësh shpejt drejt lart."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"E ke përfunduar gjestin e kalimit tek ekrani bazë. Në vijim do të mësosh se si t\'i ndërrosh aplikacionet."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Rrëshqit shpejt për të kaluar tek ekrani bazë"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Rrëshqit shpejt lart nga fundi i ekranit tënd. Ky gjest të dërgon gjithmonë tek ekrani bazë."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Sigurohu që të rrëshqasësh shpejt lart nga skaji i poshtëm i ekranit."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Provo ta mbash shtypur dritaren për një kohë më të gjatë para se ta lëshosh."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Sigurohu që të rrëshqasësh shpejt drejt lart dhe më pas ndalo."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"E ke përfunduar gjestin e ndërrimit të aplikacioneve. Je gati për të përdorur telefonin!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Rrëshqit shpejt për të ndërruar aplikacionet"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Rrëshqit shpejt lart nga fundi i ekranit, mbaje të shtypur dhe më pas lëshoje."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Plotësisht gati"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Para"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"U krye"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Cilësimet"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Provo përsëri"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bukur!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Udhëzuesi <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Ndaj"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Pamja e ekranit"</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>
 </resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index ae29bf5..8a462f9 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Подељени екран"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Закачи"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Слободни облик"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Нема недавних ставки"</string>
@@ -36,7 +35,7 @@
     <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Добијајте предлоге апликација у реду са омиљеним ставкама на почетном екрану"</string>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Лако приступајте апликацијама које најчешће користите директно са почетног екрана. Предлози се мењају на основу употребе. Апликације из доњег реда се премештају нагоре на почетни екран."</string>
     <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Лако приступајте апликацијама које најчешће користите директно са почетног екрана. Предлози се мењају на основу ваших рутина. Апликације из реда са омиљеним ставкама се премештају на почетни екран."</string>
-    <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Лако приступајте апликацијама које најчешће користите директно са почетног екрана. Предлози се мењају на основу употребе. Апликације из доњег реда се премештају у нов директоријум."</string>
+    <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Лако приступајте апликацијама које најчешће користите директно са почетног екрана. Предлози се мењају на основу употребе. Апликације из доњег реда се премештају у нов фолдер."</string>
     <string name="hotseat_edu_accept" msgid="1611544083278999837">"Приказуј предлоге апликација"</string>
     <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не, хвала"</string>
     <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Подешавања"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Предлози апликација су омогућени"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Предлози апликација су онемогућени"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Предвиђамо апликацију: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Обавезно превуците од саме леве ивице."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Обавезно превуците од леве ивице до средине екрана и отпустите."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"То је све! Сада пробајте да превучете од десне ивице."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Обавезно превуците од саме десне ивице."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Обавезно превуците од десне ивице до средине екрана и отпустите."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Довршили сте покрет за повратак. Сада сазнајте како да одете на почетну страницу."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Никако не превлачите превише близу дна екрана."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Осетљивост пок. за назад можете да промените у Подешавањима"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Превуците да бисте се вратили"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Да бисте се вратили на последњи екран, превуците од леве или десне ивице до средине екрана."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Обавезно превуците нагоре од доње ивице екрана."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Никако не стајте пре отпуштања."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Обавезно превуците право нагоре."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Довршили сте покрет за повратак на почетну страницу. Сада сазнајте како да промените апликације."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Превуците да бисте отишли на почетну страницу"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Превуците нагоре од дна екрана. Овај покрет вас увек води на почетни екран."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Обавезно превуците нагоре од доње ивице екрана."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Пробајте да држите прозор дуже пре отпуштања."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Обавезно превуците право нагоре, па застаните."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Довршили сте покрет за промену апликација. Спремни сте за коришћење телефона!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Превуците да бисте променили апликације"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Превуците нагоре од дна екрана, задржите, па пустите."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"То је то"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Даље"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Готово"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Подешавања"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Пробајте поново"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Свака част!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Водич <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Дели"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Снимак екрана"</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>
 </resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 8252fec..bf69850 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Delad skärm"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fäst"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Listan med de senaste åtgärderna är tom"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appförslag har aktiverats"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appförslag har inaktiverats"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Appförslag: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Se till att du sveper ända från vänsterkanten."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Se till att du sveper från den vänstra kanten till mitten av skärmen och sedan släpper."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Så där ja! Nu kan du testa att svepa från högerkanten."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Se till att du sveper ända från högerkanten."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Se till att du sveper från den högra kanten till mitten av skärmen och sedan släpper."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Du är klar med rörelsen för att gå tillbaka. Nu ska du få lära dig hur du öppnar startskärmen."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Se till att du inte sveper för nära skärmens nederkant."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Öppna inställningarna om du vill ändra rörelsens känslighet"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Svep för att återgå"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Återgå till den senaste skärmen genom att svepa från skärmens vänster- eller högerkant till mitten."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Se till att du sveper från nederkanten på skärmen."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Se till att du sveper i en jämn rörelse innan du släpper."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Se till att du sveper rakt uppåt."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Du är klar med rörelsen för att öppna startskärmen. Nu ska du få lära dig hur du byter mellan appar."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Svep för att öppna startskärmen"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Svep uppåt från skärmens nederkant. Du kan alltid återgå startskärmen med den här rörelsen."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Se till att du sveper från nederkanten på skärmen."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Testa att trycka längre på fönstret innan du släpper."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Se till att du sveper rakt uppåt och sedan pausar."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Du är klar med rörelsen för att byta mellan appar. Nu kan du börja använda telefonen!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Svep för att byta mellan appar"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Svep uppåt från skärmens nederkant. Håll fingret nedtryckt och släpp sedan."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Klart"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Nästa"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Klar"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Inställningar"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Försök igen"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Bra!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Självstudie <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Dela"</string>
-    <string name="action_screenshot" msgid="8171125848358142917">"Skärmdump"</string>
+    <string name="action_screenshot" msgid="8171125848358142917">"Skärmbild"</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 kan hitta 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>
 </resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 778d558..b761cae 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Gawa skrini"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Bandika"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Muundo huru"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Hakuna vipengee vya hivi karibuni"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Mapendekezo ya programu yamewashwa"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Umezima mapendekezo ya programu"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Programu iliyotabiriwa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Hakikisha unatelezesha kidole kuanzia ukingo wa mbali wa kushoto."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Hakikisha unatelezesha kidole kuanzia ukingo wa kushoto kuelekea katikati ya skrini na uachilie."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Ni hivyo tu! Sasa jaribu kutelezesha kidole kuanzia ukingo wa kuilia."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Hakikisha unatelezesha kidole kuanzia ukingo wa mbali wa kulia."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Hakikisha unatelezesha kidole kuanzia ukingo wa kulia kuelekea katikati ya skrini na uachilie."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Umekamilisha ishara ya kurudi nyuma. Inayofuata, jifunze jinsi ya kwenda kwenye Skrini ya kwanza."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Hakikisha hutelezeshi kidole karibu sana na sehemu ya chini ya skrini."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Kubadilisha hisi ya ishara ya nyuma, nenda kwenye Mipangilio"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Telezesha kidole ili urudi nyuma"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Ili urudi kwenye skrini iliyotangulia, telezesha kidole kuanzia ukingo wa kushoto au wa kulia kuelekea katikati ya skrini."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Hakikisha unatelezesha kidole juu kuanzia ukingo wa chini wa skrini."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Hakikisha kuwa husimamishi kabla ya kuachilia."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Hakikisha unatelezesha kidole kuelekea juu."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Umekamilisha ishara ya kwenda kwenye Skrini ya kwanza. Hatua inayofuata, jifunze jinsi ya kubadilisha programu."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Telezesha kidole ili uende kwenye skrini ya kwanza"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Telezesha kidole juu kuanzia chini ya skrini yako. Ishara hii kila wakati hukupeleka kwenye Skrini ya kwanza."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Hakikisha unatelezesha kidole juu kuanzia ukingo wa chini wa skrini."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Jaribu kushikilia dirisha kwa muda mrefu kabla ya kuachilia."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Hakikisha unatelezesha kidole kuelekea juu, kisha usimamishe."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Umekamilisha ishara ya kubadilisha programu. Uko tayari kutumia simu yako!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Telezesha kidole ili ubadilishe programu"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Telezesha kidole juu kuanzia sehemu ya chini ya skrini yako, ushikilie, kisha uachilie."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Kila kitu kiko tayari"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Endelea"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Nimemaliza"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Mipangilio"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Jaribu tena"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Safi!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Mafunzo ya <xliff:g id="CURRENT">%1$d</xliff:g> kati ya <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Shiriki"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Picha ya skrini"</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>
 </resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 5c1ef8f..089c95d 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"திரைப் பிரிப்பு"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"பின் செய்தல்"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"குறிப்பிட்ட வடிவமில்லாத பயன்முறை"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"சமீபத்தியவை எதுவுமில்லை"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ஆப்ஸ் பரிந்துரைகள் இயக்கப்பட்டுள்ளன"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ஆப்ஸ் பரிந்துரைகள் முடக்கப்பட்டுள்ளன"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"கணித்த ஆப்ஸ்: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"இடது ஓரத்தின் விளிம்பிலிருந்து ஸ்வைப் செய்கிறீர்கள் என்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"இடது ஓரத்திலிருந்து திரையின் மையப் பகுதிக்கு ஸ்வைப் செய்தபிறகு விடுவிப்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"அவ்வளவுதான்! இப்போது வலது ஓரத்திலிருந்து ஸ்வைப் செய்க."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"வலது ஓரத்தின் விளிம்பிலிருந்து ஸ்வைப் செய்கிறீர்கள் என்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"வலது ஓரத்திலிருந்து திரையின் மையப் பகுதிக்கு ஸ்வைப் செய்தபிறகு விடுவிப்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"பின்செல் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள். அடுத்து, முகப்புக்குச் செல்வது எப்படி என்பதை அறிக."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"திரையின் கீழ்பகுதிக்கு மிக நெருக்கமாக ஸ்வைப் செய்யவில்லை என்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"பின்செல் சைகையின் உணர்திறனை மாற்ற அமைப்புகளுக்குச் செல்க"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"பின்செல்ல ஸ்வைப் செய்யுங்கள்"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"முந்தைய திரைக்கு மீண்டும் செல்ல, இடது/வலது ஓரத்திலிருந்து திரையின் மையப் பகுதிக்கு ஸ்வைப் செய்க."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"திரையின் கீழ் ஓரத்திலிருந்து மேல்நோக்கி ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"விடுவிப்பதற்கு முன்பாக இடைநிறுத்தவில்லை என்பதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"மேல்நோக்கி நேராக ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"முகப்புக்குச் செல் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள். அடுத்து, ஆப்ஸுக்கிடையே மாறுவது எப்படி என்பதை அறிக."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"முகப்புக்குச் செல்ல ஸ்வைப் செய்யுங்கள்"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"திரையின் கீழிருந்து மேலாக ஸ்வைப் செய்க. இந்தச் சைகை எப்போதும் முகப்புத் திரைக்கு அழைத்துச் செல்லும்."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"திரையின் கீழ் ஓரத்திலிருந்து மேல்நோக்கி ஸ்வைப் செய்வதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"விடுவிப்பதற்கு முன்பாக நீண்டநேரம் சாளரத்தை அழுத்திப் பிடித்திருங்கள்."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"மேல்நோக்கி நேராக ஸ்வைப் செய்தபிறகு இடைநிறுத்துவதை உறுதிசெய்துகொள்ளுங்கள்."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"ஆப்ஸுக்கிடையே மாறும் சைகைப் பயிற்சியை முடித்துவிட்டீர்கள். உங்கள் மொபைலைப் பயன்படுத்தத் தயாராகிவிட்டீர்கள்!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ஆப்ஸுக்கிடையே மாற ஸ்வைப் செய்யுங்கள்"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"திரையின் கீழிலிருந்து மேலாக ஸ்வைப் செய்து, சில விநாடிகள் பிடித்திருந்து, பிறகு விரலை எடுங்கள்."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"எல்லாம் தயார்"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"அடுத்து"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"முடிந்தது"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"அமைப்புகள்"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"மீண்டும் முயல்க"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"அருமை!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"பயிற்சி <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"பகிர்"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ஸ்கிரீன்ஷாட்"</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>
 </resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index b381af9..c73c83e 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"స్క్రీన్‌ని విభజించు"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"పిన్ చేయి"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"సంప్రదాయేతర"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"యాప్ సలహాలు ఎనేబుల్ చేయబడ్డాయి"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"యాప్ సూచ‌న‌లు డిజేబుల్‌ చేయబడ్డాయి"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"సూచించబడిన యాప్: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"మీరు చాలా-ఎడమ అంచు నుండి స్వైప్ చేశారని నిర్ధారించుకోండి."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"మీరు ఎడమ అంచు నుండి స్క్రీన్ మధ్యలోకి స్వైప్ చేశారని నిర్ధారించుకోని, ఆపై మీ వేలిని ఎత్తండి."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"అంతే! ఇప్పుడు కుడి అంచు నుండి స్వైప్ చేయడానికి ట్రై చేయండి."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"మీరు చాలా-కుడి అంచు నుండి స్వైప్ చేశారని నిర్ధారించుకోండి."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"మీరు కుడి అంచు నుండి స్క్రీన్ మధ్యలోకి స్వైప్ చేశారని నిర్ధారించుకోని, ఆపై మీ వేలిని ఎత్తండి."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"మీరు తిరిగి వెళ్లే సంజ్ఞను పూర్తి చేశారు. తర్వాత, మొదటి ట్యాబ్‌కు ఎలా వెళ్లాలో తెలుసుకోండి."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"మీరు స్క్రీన్ దిగువకు చాలా దగ్గరగా స్వైప్ చేయలేదని నిర్ధారించుకోండి."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"వెనుక సంజ్ఞ సున్నితత్వం మార్చడానికి, సెట్టింగ్‌లకు వెళ్లండి"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"వెనుకకు వెళ్ళడం కోసం స్వైప్ చేయండి"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"మునుపటి స్క్రీన్‌కు తిరిగి వెళ్లడానికి, ఎడమ లేదా కుడి అంచు నుండి స్క్రీన్ మధ్యలోకి స్వైప్ చేయండి."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"మీరు స్క్రీన్ దిగువ అంచు నుండి పైకి స్వైప్ చేశారని నిర్ధారించుకోండి."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"బయలుదేరే ముందు మీరు పాజ్ చేయకుండా చూసుకోండి."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"మీరు నేరుగా పైకి స్వైప్ చేశారని నిర్ధారించుకోండి."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"మీరు మొదటి ట్యాబ్‌కు వెళ్లే సంజ్ఞను పూర్తి చేశారు. తర్వాత, యాప్‌ల మధ్య ఎలా మార్చాలో తెలుసుకోండి."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"వర్చువల్ హోమ్‌కి వెళ్లడానికి స్వైప్ చేయండి"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"మీ స్క్రీన్ కింది నుండి పైకి స్వైప్ చేయి. ఈ సంజ్ఞ ఎప్పుడూ మిమ్మల్ని మొదటి స్క్రీన్‌కు తీసుకెళ్తుంది."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"మీరు స్క్రీన్ దిగువ అంచు నుండి పైకి స్వైప్ చేశారని నిర్ధారించుకోండి."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"రిలీజ్ చేయడానికి ముందు విండోను ఎక్కువసేపు పట్టుకోడానికి ట్రై చేయండి."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"మీరు నేరుగా స్వైప్ చేశారని నిర్ధారించుకోండి, ఆపై పాజ్ చేయండి."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"మీరు \'యాప్‌ల మధ్య మార్పు\' సంజ్ఞను పూర్తి చేశారు. మీరు మీ ఫోన్‌ను ఉపయోగించడానికి సిద్ధంగా ఉన్నారు!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"యాప్‌ల మధ్య మార్చడం కోసం స్వైప్ చేయండి"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"మీ స్క్రీన్ కింది వైపు నుండి పైకి స్వైప్ చేసి, హోల్డ్ చేసి, తర్వాత రిలీజ్ చేయండి."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"అంతా సిద్ధంగా ఉంది"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"తర్వాత"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"పూర్తయింది"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"సెట్టింగ్‌లు"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"మళ్లీ ట్రై చేయండి"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"పనితీరు బాగుంది!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ట్యుటోరియల్ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"షేర్ చేయండి"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"స్క్రీన్‌షాట్"</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>
 </resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 42e0c58..d706108 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -19,8 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"แยกหน้าจอ"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"ตรึง"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"ปักหมุด"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"รูปแบบอิสระ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ไม่มีรายการล่าสุด"</string>
     <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"การตั้งค่าการใช้แอป"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"เปิดใช้คำแนะนำเกี่ยวกับแอปแล้ว"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ปิดใช้คำแนะนำเกี่ยวกับแอปอยู่"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"แอปที่คาดว่าจะใช้: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"ตรวจสอบว่าปัดจากขอบด้านซ้ายสุด"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"ตรวจสอบว่าปัดจากขอบด้านซ้ายไปตรงกลางหน้าจอ แล้วยกนิ้วขึ้น"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"เท่านี้ก็เรียบร้อย คราวนี้ให้ลองปัดจากขอบด้านขวา"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"ตรวจสอบว่าปัดจากขอบด้านขวาสุด"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"ตรวจสอบว่าปัดจากขอบด้านขวาไปตรงกลางหน้าจอ แล้วยกนิ้วขึ้น"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว ต่อไปดูวิธีไปที่หน้าแรก"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"ตรวจสอบว่าไม่ได้ปัดใกล้กับด้านล่างของหน้าจอมากเกินไป"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"เปลี่ยนความไวของท่าทางสัมผัสเพื่อย้อนกลับได้ที่การตั้งค่า"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"ปัดเพื่อย้อนกลับ"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"หากต้องการย้อนกลับไปที่หน้าจอล่าสุด ให้ปัดจากขอบด้านซ้ายหรือขวาไปตรงกลางหน้าจอ"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"ตรวจสอบว่าปัดขึ้นจากขอบด้านล่างของหน้าจอ"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"ตรวจสอบว่าไม่มีการหยุดชั่วคราวก่อนยกนิ้วขึ้น"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"ตรวจสอบว่าปัดขึ้นในแนวตรง"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว ต่อไปดูวิธีสลับแอป"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ปัดเพื่อไปที่หน้าแรก"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"ปัดขึ้นจากด้านล่างของหน้าจอ ท่าทางสัมผัสนี้จะนำคุณไปที่หน้าจอหลักเสมอ"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"ตรวจสอบว่าปัดขึ้นจากขอบด้านล่างของหน้าจอ"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"ลองแตะหน้าต่างค้างไว้นานขึ้นก่อนปล่อยนิ้ว"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"ตรวจสอบว่าปัดขึ้นในแนวตรง แล้วหยุดชั่วคราว"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"คุณทำท่าทางสัมผัสเพื่อสลับแอปเสร็จแล้ว คุณพร้อมใช้โทรศัพท์แล้ว"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ปัดเพื่อสลับแอป"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"ปัดขึ้นจากด้านล่างของหน้าจอค้างไว้ แล้วปล่อย"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"เรียบร้อย"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"ถัดไป"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"เสร็จสิ้น"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"การตั้งค่า"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"ลองอีกครั้ง"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"ดีมาก"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"บทแนะนำ <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"แชร์"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ภาพหน้าจอ"</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>
 </resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 54aa564..5056481 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Hatiin ang screen"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"I-pin"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Walang kamakailang item"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Naka-enable ang mga iminumungkahing app"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Naka-disable ang mga iminumungkahing app"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Hinulaang app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Tiyaking magsa-swipe ka mula sa pinakadulo sa kaliwa."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Tiyaking mag-swipe mula sa kaliwang gilid papunta sa gitna ng screen, saka iangat ang iyong daliri."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Iyon lang! Subukang mag-swipe mula sa kaliwang gilid."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Tiyaking magsa-swipe ka mula sa pinakadulo sa kanan."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Tiyaking mag-swipe mula sa kanang gilid papunta sa gitna ng screen, saka iangat ang iyong daliri."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Nakumpleto mo na ang galaw para bumalik. Susunod, alamin kung paano pumunta sa Home."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Tiyaking hindi ka magsa-swipe nang masyadong malapit sa ibaba ng screen."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Pumunta sa Settings para baguhin ang sensitivity ng pagbalik"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Mag-swipe para bumalik"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Para bumalik sa nakaraang screen, mag-swipe mula sa kaliwa o kanang gilid patungo sa gitna ng screen."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Tiyaking magsa-swipe ka pataas mula sa pinakaibaba ng screen."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Tiyaking hindi ka magpo-pause bago iangat ang iyong daliri."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Tiyaking magsa-swipe ka nang diretso pataas."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Nakumpleto mo na ang galaw para pumunta sa Home. Susunod, alamin kung paano magpalipat-lipat sa mga app."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Mag-swipe para pumunta sa home"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Mag-swipe pataas mula sa ibaba ng iyong screen. Dadalhin ka palagi ng galaw na ito sa Home screen."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Tiyaking magsa-swipe ka pataas mula sa pinakaibaba ng screen."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Subukang pindutin nang mas matagal ang window bago ito bitawan."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Tiyaking magsa-swipe ka nang diretso pataas, pagkatapos ay mag-pause."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Nakumpleto mo na ang galaw para magpalipat-lipat sa mga app. Handa ka nang gamitin ang iyong telepono!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Mag-swipe para lumipat ng app"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Mag-swipe pataas mula sa ibaba ng iyong screen, mag-hold, pagkatapos ay bitawan ito."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Handa na ang lahat"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Susunod"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Tapos na"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Mga Setting"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Subukan ulit"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Magaling!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Ibahagi"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</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>
 </resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 2f9ef31..a22247b 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Bölünmüş ekran"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Sabitle"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Serbest çalışma"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Yeni öğe yok"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Uygulama önerileri etkinleştirildi"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Uygulama önerileri devre dışı bırakıldı"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Tahmin edilen uygulama: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Ekranın en solundan kaydırdığınızdan emin olun."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Sol kenardan ekranın ortasına doğru kaydırıp bıraktığınızdan emin olun."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"İşte oldu. Şimdi sağ kenardan kaydırmayı deneyin."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Ekranın en sağından kaydırdığınızdan emin olun."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Sağ kenardan ekranın ortasına doğru kaydırıp bıraktığınızdan emin olun."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Geri dön hareketini tamamladınız. Sırada, Ana ekrana gitmeyi öğrenmek var."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Ekranın alt kısmına çok yakın bir şekilde kaydırmadığınızdan emin olun."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Geri hareketinin hassasiyetini değiştirmek için Ayarlar\'a gidin"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Geri dönmek için kaydırın"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Son ekrana geri gitmek için sol veya sağ kenardan ekranın ortasına doğru kaydırın."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Ekranın alt kenarından yukarı kaydırdığınızdan emin olun."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Bırakmadan önce parmağınızı duraklatmadığınızdan emin olun."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Düz bir şekilde yukarı kaydırdığınızdan emin olun."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Ana ekrana git hareketini tamamladınız. Sırada, uygulamalar arasında geçiş yapmayı öğrenmek var."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Ana ekrana gitmek için kaydırma"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Ekranın alt kısmından yukarıya doğru kaydırın. Bu hareket sizi her zaman Ana ekrana götürür."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Ekranın alt kenarından yukarı kaydırdığınızdan emin olun."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Bırakmadan önce pencereyi daha uzun süre tutmayı deneyin."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Düz bir şekilde yukarı kaydırıp ardından parmağınızı duraklattığınızdan emin olun."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Uygulamalar arasında geçiş yapma hareketini tamamladınız. Telefonunuzu kullanmaya hazırsınız."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Uygulamalar arasında geçiş yapmak için kaydırma"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Ekranınızın alt tarafından yukarı doğru kaydırın, tutun ve sonra bırakın."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Hepsi bu kadar"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Sonraki"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Bitti"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Ayarlar"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Tekrar deneyin"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Güzel!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Eğitici <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Paylaş"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekran görüntüsü"</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ğitici içeriği 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>
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 7f12d8d..c63cec4 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Розділити екран"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Закріпити"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Довільна форма"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Немає нещодавніх додатків"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Рекомендовані додатки ввімкнено"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Рекомендовані додатки вимкнено"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Передбачений додаток: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Проведіть пальцем саме від лівого краю екрана."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Проведіть пальцем від лівого краю до середини екрана й підніміть палець."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Це й усе! Тепер проведіть пальцем від правого краю."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Проведіть пальцем саме від правого краю екрана."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Проведіть пальцем від правого краю до середини екрана й підніміть палець."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Ви виконали жест \"Назад\". Далі дізнайтеся, як перейти на головний екран."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Не проводьте пальцем надто близько до нижнього краю екрана."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Щоб змінити чутливість жесту \"Назад\", відкрийте налаштування"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Щоб повернутися, проведіть пальцем по екрану"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Щоб перейти на попередній екран, проведіть пальцем від лівого чи правого краю до середини екрана."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Проведіть пальцем угору від нижнього краю екрана."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Не робіть паузу перед тим, як відірвати палець від екрана."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Проводьте пальцем вертикально вгору."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Ви виконали жест переходу на головний екран. Тепер дізнайтеся, як переходити між додатками."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Проведіть пальцем, щоб перейти на головний екран"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Проведіть пальцем по екрану знизу вгору. Цей жест завжди повертатиме вас на головний екран."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Проведіть пальцем угору від нижнього краю екрана."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Спробуйте втримувати вікно довше, перш ніж відпустити."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Проведіть пальцем вертикально вгору, а тоді зробіть паузу."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Ви виконали жест переходу в інший додаток. Усе готово! Почніть користуватися телефоном."</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Проведіть пальцем, щоб перейти в інший додаток"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Проведіть пальцем вгору від низу екрана, утримуйте палець на екрані, а потім відпустіть."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Усе готово!"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Далі"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Готово"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Налаштування"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Спробуйте ще"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Чудово!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Навчальний посібник <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Поділитися"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Знімок екрана"</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>
 </resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 09cd4ae..5ce95ad 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"اسپلٹ اسکرین وضع"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"پن کریں"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"فری فارم"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"کوئی حالیہ آئٹم نہیں"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"ایپ کی تجاویز فعال ہیں"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ایپ کی تجاویز غیر فعال ہیں"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"پیشن گوئی کردہ ایپ: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"اس بات کو یقینی بنائیں کہ آپ بائیں کنارے سے دور سے سوائپ کریں۔"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"یقینی طور پر بائیں کنارے سے اسکرین کے وسط تک سوائپ کریں اور پھر اپنی انگلی اٹھا لیں۔"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"بس اتنا ہی! اب دائیں کنارے سے سوائپ کرنا شروع کریں۔"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"اس بات کو یقینی بنائیں کہ آپ دائیں کنارے سے دور سے سوائپ کریں۔"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"یقینی طور پر دائیں کنارے سے اسکرین کے وسط تک سوائپ کریں اور پھر اپنی انگلی اٹھا لیں۔"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"آپ نے واپس جائیں اشارے کو مکمل کر لیا۔ اس کے بعد آپ ہوم پر جانے کا طریقہ جانیں۔"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے حصے سے زیادہ قریب سے سوائپ نہ کریں۔"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"پچھلے اشارے کی حساسیت تبدیل کرنے کے لیے ترتیبات پر جائیں"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"واپس جانے کے لیے سوائپ کریں"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"پچھلی اسکرین پر واپس جانے کے لیے بائیں یا دائیں کنارے سے اسکرین کے وسط تک سوائپ کریں۔"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے کنارے سے اوپر کی طرف سوائپ کریں۔"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"اس بات کو یقینی بنائیں کہ آپ اپنی انگلی اوپر اٹھانے سے پہلے موقوف نہ کریں۔"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"اس بات کو یقینی بنائیں کہ آپ سیدھا اوپر کی طرف سوائپ کریں۔"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا۔ اس کے بعد ایپس سوئچ کرنے کا طریقہ جانیں۔"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"ہوم پر جانے کے لیے سوائپ کریں"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"اپنی اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں۔ یہ اشارہ آپ کو ہمیشہ ہوم اسکرین پر لے جاتا ہے۔"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"اس بات کو یقینی بنائیں کہ آپ اسکرین کے نچلے کنارے سے اوپر کی طرف سوائپ کریں۔"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"انگلی اٹھانے سے پہلے ونڈو کو زیادہ دیر تک پکڑنے کی کوشش کریں۔"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"اس بات کو یقینی بنائیں کہ آپ سیدھا اوپر کی طرف سوائپ کریں، پھر موقوف کریں۔"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"آپ نے ایپ کو سوئچ کرنے کا اشارہ مکمل کر لیا۔ آپ اپنا فون استعمال کرنے کے لیے تیار ہیں!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"ایپس سوئچ کرنے کے لیے سوائپ کریں"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"اپنی اسکرین کے نچلے حصے سے اوپر کی طرف سوائپ کریں، پکڑ کر رکھیں، پھر چھوڑ دیں۔"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"سب ہو گیا"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"آگے"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"ہو گیا"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"ترتیبات"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"پھر کوشش کریں"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"عمدہ!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"ٹیوٹوریل <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"اشتراک کریں"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"اسکرین شاٹ"</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>
 </resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 737bd20..7272edd 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ekranni ikkiga ajratish"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Mahkamlash"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Erkin shakl"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Yaqinda ishlatilgan ilovalar yo‘q"</string>
@@ -31,7 +30,7 @@
     <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 ilovalaringiz"</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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Faol ishlatiladigan ilovalarga bosh ekrandan osongina kira olasiz. Tavsiyalar oxirgi faoliyatingiz asosida almashib boradi. Pastki qatordagi ilovalar bosh ekranga chiqadi."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Ilova tavsiyalari yoqildi"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Endi ilova takliflari chiqmaydi"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Taklif etilgan ilova: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Barmoqni chapdagi eng chekkadan suring."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Barmoqni ekranning chap chetidan oʻrtasigacha suring va ekrandan uzing."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Tamom! Endi oʻng chetidan surishga urining."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Barmoqni oʻngdagi eng chekkadan suring."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Barmoqni ekranning oʻng chetidan oʻrtasigacha suring va ekrandan uzing."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Ortga qaytish ishorasi darsini tamomladingiz. Endi Bosh sahifaga qaytishni oʻrganamiz."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Barmoqni ekran pastiga yaqin surmaslikka harakat qiling."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Orqaga ishorasi sezuvchanligi Sozlamalardan oʻzgartiriladi"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Ortga qaytish uchun suring"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Ortga qaytish uchun barmoqni ekranning yon chekkalaridan oʻrtasigacha suring."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Barmoqni ekranning pastki chetidan yuqoriga suring."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Barmoqni ekrandan pauzasiz qoʻyib uzing."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Barmoqni tik tepaga suring."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Bosh ekranni ochish ishorasi darsini tamomladingiz. Endi ilovalarni almashtirishni oʻrganamiz."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Svayp bilan bosh ekranni ochish"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Ekranning pastidan tepaga qarab suring. Bu ishora doim Bosh ekranni ochadi."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Barmoqni ekranning pastki chetidan yuqoriga suring."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Barmoqni uzishdan oldin oynani biroz bosib turing."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Avval tik tepaga surib, keyin pauza qiling."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Ilovalarni almashtirish darsini tamomladingiz. Endi telefondan foydalana olasiz!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Svayp bilan ilovalarni almashtirish"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Ekranning pastidan tepasiga suring, bosib turing va qoʻyib yuboring."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Tayyor"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Keyingisi"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Tayyor"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Sozlamalar"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Qayta urinish"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Yaxshi!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Darslik: <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Ulashish"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Skrinshot"</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>
 </resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 1d5fc42..fc3ef3f 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Chia đôi màn hình"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Ghim"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"Dạng tự do"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Không có mục gần đây nào"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Đã bật tính năng Ứng dụng đề xuất"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Tính năng Ứng dụng đề xuất bị tắt"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ứng dụng dự đoán: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Hãy vuốt từ mép ngoài cùng bên trái."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Hãy vuốt từ mép trái tới chính giữa màn hình rồi nhấc ngón tay."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Vậy là xong! Bây giờ, hãy thử vuốt từ mép phải."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Hãy vuốt từ mép ngoài cùng bên phải."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Hãy vuốt từ mép phải tới chính giữa màn hình rồi nhấc ngón tay."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Bạn đã thực hiện xong cử chỉ quay lại. Tiếp theo, hãy tìm hiểu cách chuyển đến Màn hình chính."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Hãy nhớ không được vuốt quá gần phần cuối màn hình."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Để thay đổi độ nhạy của cử chỉ quay lại, hãy vào mục Cài đặt"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Hãy vuốt để quay lại"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Để quay lại màn hình gần đây nhất, hãy vuốt từ mép trái hoặc mép phải tới chính giữa màn hình."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Hãy vuốt lên từ mép dưới cùng của màn hình."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Hãy nhớ không được tạm dừng trước khi nhấc ngón tay."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Hãy vuốt thẳng lên."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Bạn đã thực hiện xong cử chỉ chuyển đến Màn hình chính. Tiếp theo, hãy tìm hiểu cách chuyển đổi ứng dụng."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Vuốt để chuyển đến Màn hình chính"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Vuốt lên từ cuối màn hình. Cử chỉ này luôn đưa bạn đến Màn hình chính."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Hãy vuốt lên từ mép dưới cùng của màn hình."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Hãy thử giữ cửa sổ lâu hơn trước khi thả tay ra."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Hãy vuốt thẳng lên, sau đó tạm dừng."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Bạn đã thực hiện xong cử chỉ chuyển đổi ứng dụng. Bạn đã sẵn sàng sử dụng điện thoại!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Vuốt để chuyển đổi ứng dụng"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Vuốt lên từ cuối màn hình, giữ rồi thả tay ra."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Đã hoàn tất"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Tiếp theo"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Xong"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Cài đặt"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Thử lại"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Tuyệt vời!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Hướng dẫn <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</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="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>
 </resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index ed65978..fe5e553d 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"分屏"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"自由窗口"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"近期没有任何内容"</string>
@@ -31,7 +30,7 @@
     <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="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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"直接在主屏幕上轻松访问您最常用的应用。系统会根据您的日常安排提供不同的建议。最下面一排中的应用会向上移动到主屏幕中。"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"已启用应用建议"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"已停用应用建议"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"预测的应用:<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"请从最左侧边缘开始滑动。"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"请从左侧边缘滑动到屏幕中间位置后再松开手指。"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"大功告成!现在试着从右侧边缘开始滑动。"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"请从最右侧边缘开始滑动。"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"请从右侧边缘滑动到屏幕中间位置后再松开手指。"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"您完成了“返回”手势教程。接下来了解如何转到主屏幕。"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"滑动时,手的位置不要太靠近屏幕底部。"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"如要调节“返回”手势的灵敏度,请转到“设置”"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"滑动即可返回"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"如要返回上一个屏幕,请从左侧或右侧边缘滑动到屏幕中间位置。"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"请从屏幕底部边缘向上滑动。"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"在松开手指前请勿停下来。"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"请向上滑动。"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"您完成了“转到主屏幕”手势教程。接下来了解如何切换应用。"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"滑动即可转到主屏幕"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"从屏幕底部向上滑动。这个手势会一律将您转到主屏幕。"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"请从屏幕底部边缘向上滑动。"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"请尝试按住窗口较长时间,然后再松开手指。"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"请向上滑动,然后停住。"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"您完成了“切换应用”手势教程。您现在可以使用手机了!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"滑动即可切换应用"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"从屏幕底部向上滑动后按住,然后松开。"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"大功告成"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"继续"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"完成"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"设置"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"重试"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"很好!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"教程 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</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>
 </resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index d3fd7ee..5522a67 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"分割畫面"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"已啟用應用程式建議"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"已停用應用程式建議"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"預測應用程式:<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"請從螢幕左側邊緣滑動。"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"請從螢幕左側邊緣往中央滑動,然後放開手指。"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"大功告成!現在試試從螢幕右側邊緣滑動。"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"請從螢幕右側邊緣滑動。"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"請從螢幕右側邊緣往中央滑動,然後放開手指"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"您已完成「返回」手勢的教學課程。接下來一起瞭解如何返回主畫面。"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"滑動時,手的位置不要太接近螢幕底部。"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"如要變更「返回」手勢的敏感度,請前往「設定」"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"滑動即可返回"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"如要返回上一個畫面,請從螢幕左側或右側邊緣往中央滑動。"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"請從螢幕底部邊緣向上滑動。"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"放開手指前請勿停下來。"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"請向上滑動。"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"您已完成「返回主畫面」手勢的教學課程。接下來一起瞭解如何切換應用程式。"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"向上滑動即可返回主畫面。"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"從螢幕底部向上滑動。這個手勢在所有畫面下都可讓您返回主畫面。"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"請從螢幕底部邊緣向上滑動。"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"請嘗試按住視窗更長時間,然後再放開。"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"請向上滑動,然後停住。"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"您已完成「切換應用程式」手勢的教學課程。現在可以開始使用手機了!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"滑動即可切換應用程式"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"從螢幕底部向上滑動並按住,然後放開。"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"大功告成"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"繼續"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"完成"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"設定"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"再試一次"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"很好!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"教學課程 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</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>
 </resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 3545a16..20af2b4 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"分割畫面"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"應用程式建議功能已啟用"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"應用程式建議功能已停用"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"預測的應用程式:<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"請從螢幕左側邊緣滑動。"</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"請從螢幕左側邊緣往中央滑動,然後放開手指。"</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"大功告成!現在請試著從螢幕右側邊緣滑動。"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"請從螢幕右側邊緣滑動。"</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"請從螢幕右側邊緣往中央滑動,然後放開手指。"</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"你已完成「返回」手勢的教學課程。接著,一起來瞭解如何返回主畫面。"</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"滑動時,手的位置不要太接近螢幕底部。"</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"如要變更「返回」手勢的敏感度,請前往「設定」"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"滑動即可返回"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"如要返回上一個畫面,請從螢幕左側或右側邊緣往中央滑動。"</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"請從螢幕底部邊緣向上滑動。"</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"放開手指前請勿停下來。"</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"請向上滑動。"</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"你已完成「返回主畫面」手勢的教學課程。接著,一起來瞭解如何切換應用程式。"</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"使用滑動手勢返回主畫面"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"從螢幕底部向上滑動,即可返回主畫面。"</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"請從螢幕底部邊緣向上滑動。"</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"請按住視窗久一點,然後再放開。"</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"請向上滑動,然後停住。"</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"你已完成「切換應用程式」手勢的教學課程。現在可以開始使用手機了!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"使用滑動手勢切換應用程式"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"從螢幕底部向上滑動並按住,然後放開。"</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"大功告成"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"繼續"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"完成"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"設定"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"重試"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"很好!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"教學課程 <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"擷取螢幕畫面"</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>
 </resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 43ed289..2ffaecf 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -19,7 +19,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Hlukanisa isikrini"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Phina"</string>
     <string name="recent_task_option_freeform" msgid="48863056265284071">"I-Freeform"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Azikho izinto zakamuva"</string>
@@ -31,7 +30,7 @@
     <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">"Izinhlelo zakho zokusebenza eziqagelwe"</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>
     <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Finyelela kalula izinhlelo zakho zokusebenza ezisetshenziswa kakhulu khona kusikrini sasekhaya. Iziphakamiso zizoshintsha ngokususelwe kwimijikelezo yakho. Izinhlelo zokusebenza ezisemgqeni ongezansi zizoya phezulu kusikrini sakho sasekhaya."</string>
@@ -46,7 +45,40 @@
     <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Iziphakamiso zohlelo lokusebenza zinikwe amandla"</string>
     <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Iziphakamiso zohlelo lokusebenza zikhutshaziwe"</string>
     <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Uhlelo lokusebenza olubikezelwe: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" msgid="340972404868601012">"Qiniseka ukuthi uswayiphela ekudeni emngceleni ongakwesobunxele."</string>
+    <string name="back_gesture_feedback_cancelled_left_edge" msgid="6671316150388702530">"Qiniseka ukuthi uswayipha kusuka emngceleni okwesobunxele kuya phakathi kwesikrini bese uyadedela."</string>
+    <string name="back_gesture_feedback_complete_left_edge" msgid="3220478647881674266">"Yilokho nje! Manje zama ukuswayipha kusuka kwesokudla."</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" msgid="4306700023773832353">"Qiniseka ukuthi uswayiphela ekudeni emngceleni ongakwesokudla."</string>
+    <string name="back_gesture_feedback_cancelled_right_edge" msgid="4951916546256902552">"Qiniseka ukuthi uswayipha kusuka emngceleni okwesokudla kuya phakathi kwesikrini bese uyadedela."</string>
+    <string name="back_gesture_feedback_complete" msgid="7261221999760772210">"Ukuqedile ukuthinta kokubuyela emuva. Ngokulandelayo, funda indlela yokuya Ekhaya."</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar" msgid="1148198467090405643">"Qiniseka ukuba awuswayipheli eduze kakhulu naphansi kwesikrini."</string>
+    <string name="back_gesture_tutorial_confirm_subtitle" msgid="5181305411668713250">"Ukuze ushintshe ukuzwela kokuthinta emuva, iya Kumasethingi"</string>
+    <string name="back_gesture_intro_title" msgid="19551256430224428">"Swayipha ukuze uye emuva"</string>
+    <string name="back_gesture_intro_subtitle" msgid="7912576483031802797">"Ukuze ubuyele emuva esikrinini sokugcina, swapha kusuka emngceleni wesobunxele noma wesokudla kuya phakathi kwesikrini."</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge" msgid="1446774096007065298">"Qiniseka ukuthi uswayiphela phezulu kusuka emngceleni ophansi wesikrini."</string>
+    <string name="home_gesture_feedback_overview_detected" msgid="1557523944897393013">"Qiniseka ukuthi awumisi ngaphambi kokudedela."</string>
+    <string name="home_gesture_feedback_wrong_swipe_direction" msgid="6993979358080825438">"Qiniseka ukuthi uswayiphela ngqo phezulu."</string>
+    <string name="home_gesture_feedback_complete" msgid="2324789183070815517">"Ukuqedile ukuthinta kokuya Ekhaya. Ngokulandelayo, funda indlela yokushintsha ama-app."</string>
+    <string name="home_gesture_intro_title" msgid="836590312858441830">"Swayipha ukuze uye ekhaya"</string>
+    <string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Swayiphela phezulu kusuka phansi kwesikrini sakho.Lokhu kuthinta kuhlala kukusa esikrinini sasekhaya."</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="3032757898111577225">"Qiniseka ukuthi uswayiphela phezulu kusuka emngceleni ophansi wesikrini."</string>
+    <string name="overview_gesture_feedback_home_detected" msgid="1411130969354020489">"Zama ukubamba iwindi isikhashana ngaphambi kokulidedela."</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction" msgid="6725820500906747925">"Qiniseka ukuthi uswayiphela ngqo phezulu bese uyamisa."</string>
+    <string name="overview_gesture_feedback_complete" msgid="5477014491632199169">"Ukuqedile ukuthinta kokushintsha ama-app. Ukulungele ukusebenzisa ifoni yakho!"</string>
+    <string name="overview_gesture_intro_title" msgid="2902054412868489378">"Swayipha ukuze ushintshe ama-app"</string>
+    <string name="overview_gesture_intro_subtitle" msgid="1579517193845186042">"Swayiphela phezulu kusuka ngezansi kwesikrini sakho, ubambe, bese udedele."</string>
+    <string name="gesture_tutorial_confirm_title" msgid="6201516182040074092">"Konke kusethiwe"</string>
+    <string name="gesture_tutorial_action_button_label_next" msgid="2556263116424738762">"Okulandelayo"</string>
+    <string name="gesture_tutorial_action_button_label_done" msgid="671834508127014231">"Kwenziwe"</string>
+    <string name="gesture_tutorial_action_button_label_settings" msgid="2923621047916486604">"Amasethingi"</string>
+    <string name="gesture_tutorial_try_again" msgid="65962545858556697">"Zama futhi"</string>
+    <string name="gesture_tutorial_nice" msgid="2936275692616928280">"Kuhle!"</string>
+    <string name="gesture_tutorial_step" msgid="1279786122817620968">"Okokufundisa <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
     <string name="action_share" msgid="2648470652637092375">"Yabelana"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Isithombe-skrini"</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>
 </resources>
diff --git a/go/res/values/dimens.xml b/quickstep/res/values/attrs.xml
similarity index 74%
copy from go/res/values/dimens.xml
copy to quickstep/res/values/attrs.xml
index f1b1053..336fb57 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/values/attrs.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +14,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+    <declare-styleable name="AllSetLinkSpan">
+        <attr name="android:textSize"/>
+        <attr name="android:fontFamily"/>
+    </declare-styleable>
+</resources>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 40da136..167c7c3 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -16,4 +16,16 @@
 <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>
+
+    <color name="all_apps_label_text">#61000000</color>
+    <color name="all_apps_label_text_dark">#61FFFFFF</color>
+    <color name="all_apps_prediction_row_separator">#3c000000</color>
+    <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>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index a57112b..d67b23b 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -14,9 +14,8 @@
      limitations under the License.
 -->
 <resources>
-    <string name="task_overlay_factory_class" translatable="false"/>
-
     <string name="overscroll_plugin_factory_class" translatable="false" />
+    <string name="task_overlay_factory_class" translatable="false"/>
 
     <!-- Activities which block home gesture -->
     <string-array name="gesture_blocking_activities" translatable="false">
@@ -37,4 +36,6 @@
     <integer name="assistant_gesture_corner_deg_threshold">20</integer>
 
     <string name="wellbeing_provider_pkg" translatable="false"/>
+
+    <integer name="max_depth_blur_radius">23</integer>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 9d70316..6cc64e0 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -15,57 +15,73 @@
 -->
 
 <resources>
-
-    <dimen name="task_thumbnail_top_margin">24dp</dimen>
-    <dimen name="task_thumbnail_half_top_margin">12dp</dimen>
     <dimen name="task_thumbnail_icon_size">48dp</dimen>
+    <dimen name="task_thumbnail_icon_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 -->
+    <dimen name="task_corner_radius_override">-1dp</dimen>
+
+    <!--  Task Menu View  -->
+    <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="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>
 
     <!-- Overrideable in overlay that provides the Overview Actions. -->
-    <dimen name="overview_actions_height">66dp</dimen>
-    <dimen name="overview_actions_bottom_margin_gesture">16dp</dimen>
+    <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_horizontal_margin">16dp</dimen>
 
-    <dimen name="recents_page_spacing">10dp</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>
+
+    <!-- These speeds are in dp/s -->
+    <dimen name="max_task_dismiss_drag_velocity">2.25dp</dimen>
+    <dimen name="default_task_dismiss_drag_velocity">1.5dp</dimen>
+    <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>
-    <dimen name="overview_peek_distance">96dp</dimen>
 
     <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
              loading full resolution screenshots. -->
     <dimen name="recents_fast_fling_velocity">600dp</dimen>
 
-    <!-- These velocities are in dp / s -->
-    <dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
-    <dimen name="quickstep_fling_min_velocity">250dp</dimen>
-
     <!-- These speeds are in dp / ms -->
     <dimen name="motion_pause_detector_speed_very_slow">0.0285dp</dimen>
     <dimen name="motion_pause_detector_speed_slow">0.15dp</dimen>
     <dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
     <dimen name="motion_pause_detector_speed_fast">1.4dp</dimen>
     <dimen name="motion_pause_detector_min_displacement_from_app">36dp</dimen>
+    <dimen name="quickstep_fling_threshold_speed">0.5dp</dimen>
 
     <!-- Launcher app transition -->
-    <dimen name="content_trans_y">50dp</dimen>
-    <dimen name="workspace_trans_y">50dp</dimen>
+    <item name="content_scale" format="float" type="dimen">0.97</item>
     <dimen name="closing_window_trans_y">115dp</dimen>
 
     <dimen name="recents_empty_message_text_size">16sp</dimen>
     <dimen name="recents_empty_message_text_padding">16dp</dimen>
 
+    <dimen name="max_shadow_radius">0dp</dimen>
+
     <!-- Total space (start + end) between the task card and the edge of the screen
          in various configurations -->
-    <dimen name="task_card_vert_space">40dp</dimen>
-    <dimen name="task_card_menu_option_vertical_padding">8dp</dimen>
+    <dimen name="task_card_menu_option_vertical_padding">16dp</dimen>
+    <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_card_menu_horizontal_padding">0dp</dimen>
-    <dimen name="portrait_task_card_horz_space">136dp</dimen>
-    <dimen name="portrait_task_card_horz_space_big_overview">96dp</dimen>
-    <dimen name="portrait_modal_task_card_horz_space">60dp</dimen>
-    <dimen name="landscape_task_card_horz_space">200dp</dimen>
-    <dimen name="multi_window_task_card_horz_space">100dp</dimen>
+    <dimen name="task_menu_option_start_margin">12dp</dimen>
     <!-- Copied from framework resource:
        docked_stack_divider_thickness - 2 * docked_stack_divider_insets -->
     <dimen name="multi_window_task_divider_size">10dp</dimen>
@@ -76,6 +92,10 @@
     <dimen name="gestures_assistant_drag_threshold">55dp</dimen>
     <dimen name="gestures_assistant_fling_threshold">55dp</dimen>
 
+    <!-- One-Handed Mode -->
+    <!-- Threshold for draging distance to enable one-handed mode -->
+    <dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
+
     <!-- Distance to move elements when swiping up to go home from launcher -->
     <dimen name="home_pullback_distance">28dp</dimen>
 
@@ -93,9 +113,48 @@
     <dimen name="gesture_tutorial_feedback_margin_start_end">24dp</dimen>
     <dimen name="gesture_tutorial_button_margin_start_end">18dp</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>
+
     <!-- All Apps Education tutorial -->
     <dimen name="swipe_edu_padding">8dp</dimen>
     <dimen name="swipe_edu_circle_size">64dp</dimen>
     <dimen name="swipe_edu_width">80dp</dimen>
     <dimen name="swipe_edu_max_height">184dp</dimen>
+
+    <dimen name="chip_hint_border_width">1dp</dimen>
+    <dimen name="chip_hint_corner_radius">20dp</dimen>
+    <dimen name="chip_hint_outer_padding">20dp</dimen>
+    <dimen name="chip_hint_start_padding">10dp</dimen>
+    <dimen name="chip_hint_end_padding">12dp</dimen>
+    <dimen name="chip_hint_horizontal_margin">20dp</dimen>
+    <dimen name="chip_hint_vertical_offset">16dp</dimen>
+    <dimen name="chip_hint_elevation">2dp</dimen>
+    <dimen name="chip_icon_size">16dp</dimen>
+    <dimen name="chip_text_height">26dp</dimen>
+    <dimen name="chip_text_top_padding">4dp</dimen>
+    <dimen name="chip_text_start_padding">10dp</dimen>
+    <dimen name="chip_text_size">14sp</dimen>
+
+    <dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
+    <dimen name="all_apps_label_top_padding">16dp</dimen>
+    <dimen name="all_apps_label_bottom_padding">8dp</dimen>
+    <dimen name="all_apps_label_text_size">14sp</dimen>
+
+    <!-- Minimum distance to swipe to trigger accessibility gesture -->
+    <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
+
+    <!-- Taskbar -->
+    <dimen name="taskbar_size">60dp</dimen>
+    <dimen name="taskbar_icon_size">44dp</dimen>
+    <dimen name="taskbar_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>
 </resources>
diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/res/values/override.xml
similarity index 74%
rename from quickstep/recents_ui_overrides/res/values/override.xml
rename to quickstep/res/values/override.xml
index 6aa9619..705ec9d 100644
--- a/quickstep/recents_ui_overrides/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -17,16 +17,12 @@
 <!-- Class overrides for launcher with quickstep. -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-  <string name="app_transition_manager_class" translatable="false">com.android.launcher3.LauncherAppTransitionManagerImpl</string>
-
   <string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
 
   <string name="app_launch_tracker_class" translatable="false">com.android.launcher3.appprediction.PredictionAppTracker</string>
 
   <string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
 
-  <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherAppPredictionExtension</string>
+  <string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string>
 
-  <string name="prediction_model_class" translatable="false">com.android.launcher3.hybridhotseat.HotseatPredictionModel</string>
 </resources>
-
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 769d298..4aee2a9 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -22,8 +22,6 @@
     <string name="derived_app_name" translatable="false">Quickstep</string>
 
     <!-- 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>
     <!-- Title for an option to keep an app pinned to the screen until it is unpinned -->
     <string name="recent_task_option_pin">Pin</string>
     <!-- Title for an option to enter freeform mode for a given app -->
@@ -61,7 +59,7 @@
     <string name="all_apps_prediction_tip">Your predicted apps</string>
 
     <!-- Content description for a close button. [CHAR LIMIT=NONE] -->
-    <string  name="gesture_tutorial_close_button_content_description" translatable="false">Close</string>
+    <string name="gesture_tutorial_close_button_content_description" translatable="false">Close</string>
 
     <!-- Hotseat educational strings for users who don't qualify for migration -->
     <string name="hotseat_edu_title_migrate">Get app suggestions on the bottom row of your Home screen</string>
@@ -93,68 +91,100 @@
     <!-- content description for hotseat items -->
     <string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
 
-    <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
-    <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string>
-    <!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] -->
-    <string name="back_gesture_tutorial_engaged_subtitle_swipe_inward_right_edge" translatable="false">Start at the right edge and swipe toward the middle</string>
     <!-- Feedback shown during interactive parts of Back gesture tutorial for right edge when the gesture is too far from the edge. [CHAR LIMIT=100] -->
-    <string name="back_gesture_feedback_swipe_too_far_from_right_edge" translatable="false">Make sure you swipe from the far right edge</string>
+    <string name="back_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe from the far-right or far-left edge.</string>
     <!-- Feedback shown during interactive parts of Back gesture tutorial for right edge when the gesture is cancelled. [CHAR LIMIT=100] -->
-    <string name="back_gesture_feedback_cancelled_right_edge" translatable="false">Make sure you swipe straight to the left and let go</string>
-
-    <!-- Title shown during interactive part of Back gesture tutorial for left edge. [CHAR LIMIT=30] -->
-    <string name="back_gesture_tutorial_playground_title_swipe_inward_left_edge" translatable="false">Try the other side</string>
-    <!-- Subtitle shown during interactive parts of Back gesture tutorial for left edge. [CHAR LIMIT=60] -->
-    <string name="back_gesture_tutorial_engaged_subtitle_swipe_inward_left_edge" translatable="false">That\'s it! Now try swiping from the left edge.</string>
-    <!-- Feedback shown during interactive parts of Back gesture tutorial for left edge when the gesture is too far from the edge. [CHAR LIMIT=100] -->
-    <string name="back_gesture_feedback_swipe_too_far_from_left_edge" translatable="false">Make sure you swipe from the far left edge</string>
-    <!-- Feedback shown during interactive parts of Back gesture tutorial for left edge when the gesture is cancelled. [CHAR LIMIT=100] -->
-    <string name="back_gesture_feedback_cancelled_left_edge" translatable="false">Make sure you swipe straight to the right and let go</string>
-
+    <string name="back_gesture_feedback_cancelled">Make sure you swipe from the right or left edge to the middle of the screen and let go.</string>
+    <!-- Feedback shown after completing the back gesture step if the user is following the full gesture tutorial flow. [CHAR LIMIT=100] -->
+    <string name="back_gesture_feedback_complete_with_overview_follow_up">You learned how to swipe from the right to go back. Next up, learn how to switch apps.</string>
+    <!-- Feedback shown after completing the back gesture step if the user started this tutorial individually. [CHAR LIMIT=100] -->
+    <string name="back_gesture_feedback_complete_without_follow_up">You completed the go back gesture.</string>
     <!-- Feedback shown during interactive parts of Back gesture tutorial when the gesture is within the nav bar region. [CHAR LIMIT=100] -->
-    <string name="back_gesture_feedback_swipe_in_nav_bar" translatable="false">Make sure you don\'t swipe too close to the bottom of the screen</string>
+    <string name="back_gesture_feedback_swipe_in_nav_bar">Make sure you don\'t swipe too close to the bottom of the screen.</string>
     <!-- Subtitle shown on the confirmation screen after successful gesture. [CHAR LIMIT=60] -->
-    <string name="back_gesture_tutorial_confirm_subtitle" translatable="false">To change the sensitivity of the back gesture, go to Settings</string>
-
-    <!-- Title shown during interactive part of Home gesture tutorial. [CHAR LIMIT=30] -->
-    <string name="home_gesture_tutorial_playground_title" translatable="false">Tutorial: Go Home</string>
-    <!-- Subtitle shown during interactive parts of Home gesture tutorial. [CHAR LIMIT=60] -->
-    <string name="home_gesture_tutorial_playground_subtitle" translatable="false">Try swiping upward from the bottom edge of the screen</string>
+    <string name="back_gesture_tutorial_confirm_subtitle">To change the sensitivity of the back gesture, go to Settings</string>
     <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is started too far from the edge. [CHAR LIMIT=100] -->
-    <string name="home_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the bottom edge of the screen</string>
-    <!-- Feedback shown during interactive parts of Home gesture tutorial when the Overview gesture is detected. [CHAR LIMIT=100] -->
-    <string name="home_gesture_feedback_overview_detected" translatable="false">Make sure you don\'t pause before letting go</string>
-    <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
-    <string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string>
+    <!-- Introduction title for the Back gesture tutorial. [CHAR LIMIT=100] -->
+    <string name="back_gesture_intro_title">Swipe to go back</string>
+    <!-- Introduction subtitle for the Back gesture tutorial. [CHAR LIMIT=200] -->
+    <string name="back_gesture_intro_subtitle">To go back to the last screen, swipe from the left or right edge to the middle of the screen.</string>
 
-    <!-- Title shown during interactive part of Overview gesture tutorial. [CHAR LIMIT=30] -->
-    <string name="overview_gesture_tutorial_playground_title" translatable="false">Tutorial: Switch Apps</string>
-    <!-- Subtitle shown during interactive parts of Overview gesture tutorial. [CHAR LIMIT=60] -->
-    <string name="overview_gesture_tutorial_playground_subtitle" translatable="false">Swipe up from the bottom of the screen and hold</string>
+    <string name="home_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe up from the bottom edge of the screen.</string>
+    <!-- Feedback shown during interactive parts of Home gesture tutorial when the Overview gesture is detected. [CHAR LIMIT=100] -->
+    <string name="home_gesture_feedback_overview_detected">Make sure you don\'t pause before letting go.</string>
+    <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
+    <string name="home_gesture_feedback_wrong_swipe_direction">Make sure you swipe straight up.</string>
+    <!-- Feedback shown after completing the home gesture step if the user is following the full gesture tutorial flow. [CHAR LIMIT=100] -->
+    <string name="home_gesture_feedback_complete_with_follow_up">You completed the go Home gesture. Next up, learn how to go back.</string>
+    <!-- Feedback shown after completing the home gesture step if the user started this tutorial individually. [CHAR LIMIT=100] -->
+    <string name="home_gesture_feedback_complete_without_follow_up">You completed the go Home gesture.</string>
+    <!-- Introduction title for the Home gesture tutorial. [CHAR LIMIT=100] -->
+    <string name="home_gesture_intro_title">Swipe to go home</string>
+    <!-- Introduction subtitle for the Home gesture tutorial. [CHAR LIMIT=100] -->
+    <string name="home_gesture_intro_subtitle">Swipe up from the bottom of your screen. This gesture always takes you to the Home screen.</string>
+
     <!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is started too far from the edge. [CHAR LIMIT=100] -->
-    <string name="overview_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the bottom edge of the screen</string>
-    <!-- Feedback shown during interactive parts of Overview gesture tutorial when the Home gesture is detected. [CHAR LIMIT=100] -->
-    <string name="overview_gesture_feedback_home_detected" translatable="false">Try holding the window for longer before releasing</string>
+    <string name="overview_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe up from the bottom edge of the screen.</string>
+    <!-- Feedback shown during interactive parts of Overview gesture tutorial when the Home gesture is detected. The window refers to the current app's window during the gesture. [CHAR LIMIT=100] -->
+    <string name="overview_gesture_feedback_home_detected">Try holding the window for longer before releasing.</string>
     <!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
-    <string name="overview_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up and pause</string>
+    <string name="overview_gesture_feedback_wrong_swipe_direction">Make sure you swipe straight up, then pause.</string>
+    <!-- Feedback shown after completing the overview gesture step if the user is following the full gesture tutorial flow. [CHAR LIMIT=100] -->
+    <string name="overview_gesture_feedback_complete_with_follow_up">You learned how to use gestures. To turn off gestures, go to Settings.</string>
+    <!-- Feedback shown after completing the overview gesture step if the user started this tutorial individually. [CHAR LIMIT=100] -->
+    <string name="overview_gesture_feedback_complete_without_follow_up">You completed the switch apps gesture.</string>
+    <!-- Introduction title for the Overview gesture tutorial. [CHAR LIMIT=100] -->
+    <string name="overview_gesture_intro_title">Swipe to switch apps</string>
+    <!-- Introduction subtitle for the Overview gesture tutorial. [CHAR LIMIT=100] -->
+    <string name="overview_gesture_intro_subtitle">To switch between apps, swipe up from the bottom of your screen, hold, then release.</string>
 
     <!-- Title shown during interactive part of Assistant gesture tutorial. [CHAR LIMIT=30] -->
     <string name="assistant_gesture_tutorial_playground_title" translatable="false">Tutorial: Assistant</string>
     <!-- Subtitle shown during interactive parts of Assistant gesture tutorial. [CHAR LIMIT=60] -->
     <string name="assistant_gesture_tutorial_playground_subtitle" translatable="false">Try swiping diagonally from a bottom corner of the screen</string>
     <!-- Feedback shown during interactive parts of Assistant gesture tutorial when the gesture is started too far from the corner. [CHAR LIMIT=100] -->
-    <string name="assistant_gesture_feedback_swipe_too_far_from_corner" translatable="false">Make sure you swipe from a bottom corner of the screen</string>
+    <string name="assistant_gesture_feedback_swipe_too_far_from_corner" translatable="false">Make sure you swipe from a bottom corner of the screen.</string>
     <!-- Feedback shown during interactive parts of Assistant gesture tutorial when the gesture doesn't go diagonally enough. [CHAR LIMIT=100] -->
-    <string name="assistant_gesture_feedback_swipe_not_diagonal" translatable="false">Make sure you swipe diagonally</string>
+    <string name="assistant_gesture_feedback_swipe_not_diagonal" translatable="false">Make sure you swipe diagonally.</string>
     <!-- Feedback shown during interactive parts of Assistant gesture tutorial when the gesture doesn't go far enough. [CHAR LIMIT=100] -->
-    <string name="assistant_gesture_feedback_swipe_not_long_enough" translatable="false">Try swiping further</string>
+    <string name="assistant_gesture_feedback_swipe_not_long_enough" translatable="false">Try swiping further.</string>
+
+    <!-- Title shown in sandbox mode part of gesture tutorial. [CHAR LIMIT=30] -->
+    <string name="sandbox_mode_title" translatable="false">Sandbox Mode</string>
+    <!-- Subtitle shown in sandbox mode part of gesture tutorial. [CHAR LIMIT=60] -->
+    <string name="sandbox_mode_subtitle" translatable="false">Try any navigation gesture</string>
+    <!-- Feedback shown in sandbox mode when the back gesture is successfully issued. [CHAR LIMIT=60] -->
+    <string name="sandbox_mode_back_gesture_feedback_successful" translatable="false">Back gesture successful</string>
+    <!-- Feedback shown in sandbox mode when the assistant gesture is a successfully issued. [CHAR LIMIT=60] -->
+    <string name="sandbox_mode_assistant_gesture_feedback_successful" translatable="false">Assistant gesture successful</string>
+    <!-- Feedback shown in sandbox mode when the home gesture is a successfully issued. [CHAR LIMIT=60] -->
+    <string name="sandbox_mode_home_gesture_feedback_successful" translatable="false">Home gesture successful</string>
+    <!-- Feedback shown in sandbox mode when the overview gesture is a successfully issued. [CHAR LIMIT=60] -->
+    <string name="sandbox_mode_overview_gesture_feedback_successful" translatable="false">Overview gesture successful</string>
+    <!-- Feedback shown in sandbox mode when the back gesture swipe is too far from the edge. [CHAR LIMIT=60] -->
+    <string name="sandbox_mode_back_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the left/right edge of the screen</string>
 
     <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
-    <string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
-    <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] -->
-    <string name="gesture_tutorial_action_button_label_done" translatable="false">Done</string>
+    <string name="gesture_tutorial_confirm_title">All set</string>
+    <!-- Button text shown on a button on the feedback popup to complete the gesture tutorial. [CHAR LIMIT=14] -->
+    <string name="gesture_tutorial_action_button_label">Done</string>
     <!-- Button text shown on a button to go to Settings. [CHAR LIMIT=14] -->
-    <string name="gesture_tutorial_action_button_label_settings" translatable="false">Settings</string>
+    <string name="gesture_tutorial_action_button_label_settings">Settings</string>
+    <!-- Feedback title to try again. [CHAR LIMIT=30] -->
+    <string name="gesture_tutorial_try_again">Try again</string>
+    <!-- Feedback title for a successful gesture. [CHAR LIMIT=30] -->
+    <string name="gesture_tutorial_nice">Nice!</string>
+    <!-- Feedback subtext displaying the current step and the total number of steps for the tutorial. [CHAR LIMIT=30] -->
+    <string name="gesture_tutorial_step">Tutorial <xliff:g id="current">%1$d</xliff:g>/<xliff:g id="total">%2$d</xliff:g></string>
+
+    <!-- Title of "All Set" page [CHAR LIMIT=NONE] -->
+    <string name="allset_title">All set!</string>
+    <!-- Hint string at the bottom of "All Set" page [CHAR LIMIT=NONE] -->
+    <string name="allset_hint">Swipe up to go Home</string>
+    <!-- Description of "All Set" page [CHAR LIMIT=NONE] -->
+    <string name="allset_description">You\u2019re ready to start using your phone</string>
+    <!-- String linking to navigation settings on "All Set" page [CHAR LIMIT=NONE] -->
+    <string name="allset_navigation_settings"><annotation id="link">System navigation settings</annotation></string>
 
     <!-- ******* Overview ******* -->
     <!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
@@ -163,4 +193,14 @@
     <string name="action_screenshot">Screenshot</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>
-</resources>
\ No newline at end of file
+
+    <!-- ******* Skip tutorial dialog ******* -->
+    <!-- Title for the dialog that allows the user to skip the gesture navigation tutorial. [CHAR_LIMIT=40] -->
+    <string name="skip_tutorial_dialog_title">Skip navigation tutorial?</string>
+    <!-- Subtitle for the dialog that allows the user to skip the gesture navigation tutorial. This directs the user to where they can find the gesture tutorial again. [CHAR_LIMIT=100] -->
+    <string name="skip_tutorial_dialog_subtitle">You can find this later in the <xliff:g id="name">%1$s</xliff:g> app</string>
+    <!-- Button text shown on a button on the tutorial skip dialog to return to the tutorial. [CHAR LIMIT=14] -->
+    <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>
+</resources>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 8d054b4..07c448d 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -15,16 +15,6 @@
      limitations under the License.
 -->
 <resources>
-    <!-- Task Menu layout styles. -->
-    <style name="TaskMenu">
-        <item name="android:orientation">vertical</item>
-    </style>
-
-    <!-- Task Menu Option layout styles. -->
-    <style name="TaskMenu.Option">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">wrap_content</item>
-    </style>
 
     <style name="TextAppearance.GestureTutorial"
         parent="android:TextAppearance.Material.Body1" />
@@ -47,12 +37,53 @@
         <item name="android:textSize">21sp</item>
     </style>
 
-    <style name="TextAppearance.GestureTutorial.Feedback"
+    <style name="TextAppearance.GestureTutorial.Feedback.Title"
         parent="TextAppearance.GestureTutorial">
-        <item name="android:gravity">center</item>
+        <item name="android:gravity">start</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:fontFamily">google-sans-regular</item>
         <item name="android:letterSpacing">0.03</item>
-        <item name="android:textSize">21sp</item>
+        <item name="android:textSize">36sp</item>
+        <item name="android:lineHeight">44sp</item>
+    </style>
+
+    <style name="TextAppearance.GestureTutorial.Dialog.Title"
+        parent="TextAppearance.GestureTutorial.Feedback.Title">
+        <item name="android:gravity">center_horizontal</item>
+        <item name="android:fontFamily">google-sans</item>
+        <item name="android:lineHeight">32sp</item>
+        <item name="android:textSize">24sp</item>
+    </style>
+
+    <style name="TextAppearance.GestureTutorial.Feedback.Subtitle"
+        parent="TextAppearance.GestureTutorial">
+        <item name="android:gravity">start</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:fontFamily">google-sans-text</item>
+        <item name="android:letterSpacing">0.03</item>
+        <item name="android:textSize">18sp</item>
+        <item name="android:lineHeight">24sp</item>
+    </style>
+
+    <style name="TextAppearance.GestureTutorial.Dialog.Subtitle"
+        parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
+        <item name="android:gravity">center_horizontal</item>
+        <item name="android:fontFamily">google-sans-text</item>
+        <item name="android:letterSpacing">0.025</item>
+        <item name="android:lineHeight">20sp</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="TextAppearance.GestureTutorial.Feedback.Subtext"
+        parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/gesture_tutorial_primary_color</item>
+        <item name="android:gravity">center</item>
+    </style>
+
+    <style name="TextAppearance.GestureTutorial.Feedback.Subtext.Dark"
+        parent="TextAppearance.GestureTutorial.Feedback.Subtext">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.GestureTutorial.ButtonLabel"
@@ -64,11 +95,29 @@
         <item name="android:textAllCaps">false</item>
     </style>
 
+    <style name="TextAppearance.GestureTutorial.CancelButtonLabel"
+        parent="TextAppearance.GestureTutorial.ButtonLabel">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
     <style name="TextAppearance.GestureTutorial.TextButtonLabel"
         parent="TextAppearance.GestureTutorial.ButtonLabel">
         <item name="android:textColor">@color/gesture_tutorial_primary_color</item>
     </style>
 
+    <style name="TextAppearance.GestureTutorial.LinkText"
+        parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="AllSetTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:navigationBarColor">@android:color/transparent</item>
+        <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>
+    </style>
+
     <!--
       Can be applied to views to color things like ripples and list highlights the workspace text
       color.
@@ -81,8 +130,13 @@
         parent="@android:style/Widget.DeviceDefault.Button.Borderless">
         <item name="android:textColor">@color/overview_button</item>
         <item name="android:drawableTint">@color/overview_button</item>
-        <item name="android:tint">?attr/workspaceTextColor</item>
+        <item name="android:tint">?android:attr/textColorPrimary</item>
         <item name="android:drawablePadding">8dp</item>
         <item name="android:textAllCaps">false</item>
     </style>
+
+    <!-- Icon displayed on the taskbar -->
+    <style name="BaseIcon.Workspace.Taskbar" >
+        <item name="iconDisplay">taskbar</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/xml/indexable_launcher_prefs.xml b/quickstep/res/xml/indexable_launcher_prefs.xml
index c7e864f..b4740e5 100644
--- a/quickstep/res/xml/indexable_launcher_prefs.xml
+++ b/quickstep/res/xml/indexable_launcher_prefs.xml
@@ -26,7 +26,7 @@
         android:key="pref_allowRotation"
         android:title="@string/allow_rotation_title"
         android:summary="@string/allow_rotation_desc"
-        android:defaultValue="@bool/allow_rotation"
+        android:defaultValue="false"
         android:persistent="true" />
 
 </PreferenceScreen>
diff --git a/quickstep/res/xml/overview_file_provider_paths.xml b/quickstep/res/xml/overview_file_provider_paths.xml
index 14d7459..07e3236 100644
--- a/quickstep/res/xml/overview_file_provider_paths.xml
+++ b/quickstep/res/xml/overview_file_provider_paths.xml
@@ -2,4 +2,5 @@
 <paths xmlns:android="http://schemas.android.com/apk/res/android">
     <cache-path name="shared_images" path="/" />
     <files-path name="log_files" path="/" />
+    <root-path name="apps" path="/" />
 </paths>
\ 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
new file mode 100644
index 0000000..7c97b93
--- /dev/null
+++ b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -0,0 +1,301 @@
+/*
+ * 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/OrientationTouchTransformerTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index 22d205a..2b1b57c 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -17,40 +17,59 @@
 
 package com.android.quickstep;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
+import android.hardware.display.DisplayManager;
 import android.util.DisplayMetrics;
+import android.view.Display;
 import android.view.MotionEvent;
 import android.view.Surface;
 
 import com.android.launcher3.ResourceUtils;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
 
 @RunWith(RobolectricTestRunner.class)
 public class OrientationTouchTransformerTest {
-    private static final int SIZE_WIDTH = 1080;
-    private static final int SIZE_HEIGHT = 2280;
+    static class ScreenSize {
+        int mHeight;
+        int mWidth;
+
+        ScreenSize(int height, int width) {
+            mHeight = height;
+            mWidth = width;
+        }
+    }
+
+    private static final ScreenSize NORMAL_SCREEN_SIZE = new ScreenSize(2280, 1080);
+    private static final ScreenSize LARGE_SCREEN_SIZE = new ScreenSize(3280, 1080);
     private static final float DENSITY_DISPLAY_METRICS = 3.0f;
 
     private OrientationTouchTransformer mTouchTransformer;
 
     Resources mResources;
-    private DefaultDisplay.Info mInfo;
+    private DisplayController.Info mInfo;
 
 
     @Before
@@ -63,14 +82,16 @@
         DisplayMetrics mockDisplayMetrics = new DisplayMetrics();
         mockDisplayMetrics.density = DENSITY_DISPLAY_METRICS;
         when(mResources.getDisplayMetrics()).thenReturn(mockDisplayMetrics);
-        mInfo = createDisplayInfo(Surface.ROTATION_0);
+        mInfo = createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_0);
         mTouchTransformer = new OrientationTouchTransformer(mResources, NO_BUTTON, () -> 0);
     }
 
     @Test
     public void disabledMultipleRegions_shouldOverrideFirstRegion() {
-        float portraitRegionY = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
-        float landscapeRegionY = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+        float portraitRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+        float landscapeRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
 
         mTouchTransformer.createOrAddTouchRegion(mInfo);
         tapAndAssertTrue(100, portraitRegionY,
@@ -83,7 +104,8 @@
                 event -> mTouchTransformer.touchInAssistantRegion(event));
 
         // Override region
-        mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+        mTouchTransformer
+            .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
         tapAndAssertFalse(100, portraitRegionY,
                 event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
         tapAndAssertTrue(100, landscapeRegionY,
@@ -107,10 +129,13 @@
 
     @Test
     public void enableMultipleRegions_shouldOverrideFirstRegion() {
-        float portraitRegionY = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
-        float landscapeRegionY = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+        float portraitRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+        float landscapeRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
 
-        mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+        mTouchTransformer
+            .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
         tapAndAssertFalse(100, portraitRegionY,
                 event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
         tapAndAssertTrue(100, landscapeRegionY,
@@ -136,11 +161,14 @@
 
     @Test
     public void enableMultipleRegions_assistantTriggersInMostRecent() {
-        float portraitRegionY = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
-        float landscapeRegionY = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+        float portraitRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+        float landscapeRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
 
         mTouchTransformer.enableMultipleRegions(true, mInfo);
-        mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+        mTouchTransformer
+            .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
         mTouchTransformer.createOrAddTouchRegion(mInfo);
         tapAndAssertTrue(0, portraitRegionY,
                 event -> mTouchTransformer.touchInAssistantRegion(event));
@@ -150,12 +178,15 @@
 
     @Test
     public void enableMultipleRegions_assistantTriggersInCurrentOrientationAfterDisable() {
-        float portraitRegionY = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
-        float landscapeRegionY = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+        float portraitRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+        float landscapeRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
 
         mTouchTransformer.enableMultipleRegions(true, mInfo);
         mTouchTransformer.createOrAddTouchRegion(mInfo);
-        mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+        mTouchTransformer
+            .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
         mTouchTransformer.enableMultipleRegions(false, mInfo);
         tapAndAssertTrue(0, portraitRegionY,
                 event -> mTouchTransformer.touchInAssistantRegion(event));
@@ -164,6 +195,26 @@
     }
 
     @Test
+    public void assistantTriggersInCurrentScreenAfterScreenSizeChange() {
+        float smallerScreenPortraitRegionY =
+                generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+        float largerScreenPortraitRegionY =
+                generateTouchRegionHeight(LARGE_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+
+        mTouchTransformer.enableMultipleRegions(false,
+                createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_0));
+        tapAndAssertTrue(0, smallerScreenPortraitRegionY,
+                event -> mTouchTransformer.touchInAssistantRegion(event));
+
+        mTouchTransformer
+            .enableMultipleRegions(false, createDisplayInfo(LARGE_SCREEN_SIZE, Surface.ROTATION_0));
+        tapAndAssertTrue(0, largerScreenPortraitRegionY,
+                event -> mTouchTransformer.touchInAssistantRegion(event));
+        tapAndAssertFalse(0, smallerScreenPortraitRegionY,
+                event -> mTouchTransformer.touchInAssistantRegion(event));
+    }
+
+    @Test
     public void applyTransform_taskNotFrozen_notInRegion() {
         mTouchTransformer.createOrAddTouchRegion(mInfo);
         tapAndAssertFalse(100, 100,
@@ -182,7 +233,7 @@
     public void applyTransform_taskFrozen_noRotate_inRegion() {
         mTouchTransformer.createOrAddTouchRegion(mInfo);
         mTouchTransformer.enableMultipleRegions(true, mInfo);
-        float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
+        float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
         tapAndAssertTrue(100, y,
                 event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
     }
@@ -190,34 +241,34 @@
     @Test
     public void applyTransform_taskNotFrozen_noRotate_inDefaultRegion() {
         mTouchTransformer.createOrAddTouchRegion(mInfo);
-        float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
+        float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
         tapAndAssertTrue(100, y,
                 event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
     }
 
     @Test
     public void applyTransform_taskNotFrozen_90Rotate_inRegion() {
-        mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
-        float y = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+        mTouchTransformer
+            .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
+        float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
         tapAndAssertTrue(100, y,
                 event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
     }
 
     @Test
-    @Ignore("There's too much that goes into needing to mock a real motion event so the "
-            + "transforms in native code get applied correctly. Once that happens then maybe we can"
-            + " write slightly more complex unit tests")
-    public void applyTransform_taskNotFrozen_90Rotate_inTwoRegions() {
+    public void applyTransform_taskNotFrozen_90Rotate_withTwoRegions() {
         mTouchTransformer.createOrAddTouchRegion(mInfo);
         mTouchTransformer.enableMultipleRegions(true, mInfo);
-        mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+        mTouchTransformer
+            .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
         // Landscape point
-        float y1 = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+        float y1 = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
         MotionEvent inRegion1_down = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, y1);
         MotionEvent inRegion1_up = generateMotionEvent(MotionEvent.ACTION_UP, 10, y1);
         // Portrait point in landscape orientation axis
         MotionEvent inRegion2 = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, 10);
         mTouchTransformer.transform(inRegion1_down);
+        // no-op
         mTouchTransformer.transform(inRegion2);
         assertTrue(mTouchTransformer.touchInValidSwipeRegions(
                 inRegion1_down.getX(), inRegion1_down.getY()));
@@ -225,24 +276,49 @@
         assertFalse(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
 
         mTouchTransformer.transform(inRegion1_up);
+    }
 
-        // Set the new region with this MotionEvent.ACTION_DOWN
-        inRegion2 = generateAndTransformMotionEvent(MotionEvent.ACTION_DOWN, 10, 370);
+    @Test
+    public void applyTransform_90Rotate_inRotatedRegion() {
+        // Create regions for both 0 Rotation and 90 Rotation
+        mTouchTransformer.createOrAddTouchRegion(mInfo);
+        mTouchTransformer.enableMultipleRegions(true, mInfo);
+        mTouchTransformer
+                .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
+        // Portrait point in landscape orientation axis
+        float x1 = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0);
+        // bottom of screen, from landscape perspective right side of screen
+        MotionEvent inRegion2 = generateAndTransformMotionEvent(MotionEvent.ACTION_DOWN, x1, 370);
         assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
     }
 
-    private DefaultDisplay.Info createDisplayInfo(int rotation) {
-        Point p = new Point(SIZE_WIDTH, SIZE_HEIGHT);
+    private DisplayController.Info createDisplayInfo(ScreenSize screenSize, int rotation) {
+        Context context = RuntimeEnvironment.application;
+        Display display = spy(context.getSystemService(DisplayManager.class)
+                .getDisplay(DEFAULT_DISPLAY));
+
+        Point p = new Point(screenSize.mWidth, screenSize.mHeight);
         if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
-            p = new Point(SIZE_HEIGHT, SIZE_WIDTH);
+            p.set(screenSize.mHeight, screenSize.mWidth);
         }
-        return new DefaultDisplay.Info(0, rotation, 0, p, p, p, null);
+
+        doReturn(rotation).when(display).getRotation();
+        doAnswer(i -> {
+            ((Point) i.getArgument(0)).set(p.x, p.y);
+            return null;
+        }).when(display).getRealSize(any(Point.class));
+        doAnswer(i -> {
+            ((Point) i.getArgument(0)).set(p.x, p.y);
+            ((Point) i.getArgument(1)).set(p.x, p.y);
+            return null;
+        }).when(display).getCurrentSizeRange(any(Point.class), any(Point.class));
+        return new DisplayController.Info(context, display);
     }
 
-    private float generateTouchRegionHeight(int rotation) {
-        float height = SIZE_HEIGHT;
+    private float generateTouchRegionHeight(ScreenSize screenSize, int rotation) {
+        float height = screenSize.mHeight;
         if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
-            height = SIZE_WIDTH;
+            height = screenSize.mWidth;
         }
         return height - ResourceUtils.DEFAULT_NAVBAR_VALUE * DENSITY_DISPLAY_METRICS;
     }
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
index c148a4b..9df9ab1 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
@@ -37,6 +37,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
+@org.junit.Ignore
 public class RecentsActivityTest {
 
     @Test
@@ -60,9 +61,9 @@
 
         FallbackRecentsView frv = activity.getOverviewPanel();
 
-        RunningTaskInfo dummyTask = new RunningTaskInfo();
-        dummyTask.taskId = 22;
-        frv.showCurrentTask(dummyTask);
+        RunningTaskInfo placeholderTask = new RunningTaskInfo();
+        placeholderTask.taskId = 22;
+        frv.showCurrentTask(placeholderTask);
         doLayout(activity);
 
         ThumbnailData thumbnailData = new ThumbnailData();
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
new file mode 100644
index 0000000..656379f
--- /dev/null
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_90;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.Context;
+
+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)
+public class RecentsOrientedStateTest {
+
+    private RecentsOrientedState mR1, mR2;
+
+    @Before
+    public void setup() {
+        Context context = RuntimeEnvironment.application;
+        mR1 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { });
+        mR2 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { });
+        assertEquals(mR1.getStateId(), mR2.getStateId());
+    }
+
+    @Test
+    public void stateId_changesWithFlags() {
+        mR1.setGestureActive(true);
+        mR2.setGestureActive(false);
+        assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+        mR2.setGestureActive(true);
+        assertEquals(mR1.getStateId(), mR2.getStateId());
+    }
+
+    @Test
+    public void stateId_changesWithRecentsRotation() {
+        mR1.setRecentsRotation(ROTATION_90);
+        mR2.setRecentsRotation(ROTATION_180);
+        assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+        mR2.setRecentsRotation(ROTATION_90);
+        assertEquals(mR1.getStateId(), mR2.getStateId());
+    }
+
+    @Test
+    public void stateId_changesWithDisplayRotation() {
+        mR1.update(ROTATION_0, ROTATION_90);
+        mR2.update(ROTATION_0, ROTATION_180);
+        assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+        mR2.update(ROTATION_90, ROTATION_90);
+        assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+        mR2.update(ROTATION_90, ROTATION_0);
+        assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+        mR2.update(ROTATION_0, ROTATION_90);
+        assertEquals(mR1.getStateId(), mR2.getStateId());
+    }
+}
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index a31ba21..fd93d98 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -17,6 +17,8 @@
 
 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;
@@ -27,8 +29,9 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.shadows.LShadowDisplay;
-import com.android.launcher3.util.DefaultDisplay;
+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;
@@ -144,11 +147,11 @@
                     LauncherActivityInterface.INSTANCE);
             tvs.setDp(mDeviceProfile);
 
-            int launcherRotation = DefaultDisplay.INSTANCE.get(mContext).getInfo().rotation;
+            int launcherRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation;
             if (mAppRotation < 0) {
                 mAppRotation = launcherRotation;
             }
-            tvs.setLayoutRotation(launcherRotation, mAppRotation);
+            tvs.getOrientationState().update(launcherRotation, mAppRotation);
             if (mAppInsets == null) {
                 mAppInsets = new Rect(mLauncherInsets);
             }
@@ -162,7 +165,7 @@
         @Override
         public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
             SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
-            proxy.onBuildTargetParams(builder, null, this);
+            proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
             return new SurfaceParams[] {builder.build()};
         }
 
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 235df42..a44de79 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -19,43 +19,65 @@
 import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+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.Executors.UI_HELPER_EXECUTOR;
+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.Intent;
 import android.content.IntentSender;
-import android.content.SharedPreferences;
+import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.view.View;
+import android.window.SplashScreen;
+
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.hybridhotseat.HotseatPredictionController;
+import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.proxy.ProxyActivityStarter;
 import com.android.launcher3.proxy.StartActivityParams;
 import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
 import com.android.launcher3.statehandlers.DepthController;
 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.OnboardingPrefs;
+import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.OverviewCommandHelper;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.QuickstepOnboardingPrefs;
+import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.TouchInteractionService.TISBinder;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
-import com.android.quickstep.util.ShelfPeekAnim;
+import com.android.quickstep.util.SplitSelectStateController;
 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 java.util.List;
 import java.util.stream.Stream;
 
 /**
@@ -65,37 +87,76 @@
         implements NavigationModeChangeListener {
 
     private DepthController mDepthController = new DepthController(this);
+    private QuickstepTransitionManager mAppTransitionManager;
 
     /**
      * Reusable command for applying the back button alpha on the background thread.
      */
     public static final UiThreadHelper.AsyncCommand SET_BACK_BUTTON_ALPHA =
-            (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(
+            (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setNavBarButtonAlpha(
                     Float.intBitsToFloat(arg1), arg2 != 0);
 
-    private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
-
     private OverviewActionsView mActionsView;
-    protected HotseatPredictionController mHotseatPredictionController;
+
+    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;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
         addMultiWindowModeChangedListener(mDepthController);
     }
 
     @Override
     public void onDestroy() {
+        mAppTransitionManager.onActivityDestroyed();
+
         SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+
+
+        unbindService(mTisBinderConnection);
+        if (mTaskbarManager != null) {
+            mTaskbarManager.setLauncher(null);
+        }
         super.onDestroy();
     }
 
     @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        if (mOverviewCommandHelper != null) {
+            mOverviewCommandHelper.clearPendingCommands();
+        }
+    }
+
+    public QuickstepTransitionManager getAppTransitionManager() {
+        return mAppTransitionManager;
+    }
+
+    @Override
     public void onNavigationModeChanged(Mode newMode) {
         getDragLayer().recreateControllers();
-        if (mActionsView != null && isOverviewActionsEnabled()) {
+        if (mActionsView != null) {
             mActionsView.updateVerticalMargin(newMode);
         }
     }
@@ -124,9 +185,19 @@
     }
 
     @Override
-    protected void onUiChangedWhileSleeping() {
+    public void onUiChangedWhileSleeping() {
         // Remove the snapshot because the content view may have obvious changes.
-        ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this);
+        UI_HELPER_EXECUTOR.execute(
+                () -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this));
+    }
+
+    @Override
+    protected void onScreenOff() {
+        super.onScreenOff();
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            RecentsView recentsView = getOverviewPanel();
+            recentsView.finishRecentsAnimation(true /* toRecents */, null);
+        }
     }
 
     @Override
@@ -168,9 +239,14 @@
     }
 
     @Override
-    protected void handlePendingActivityRequest() {
-        super.handlePendingActivityRequest();
-        if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+    public void onStateSetEnd(LauncherState state) {
+        super.onStateSetEnd(state);
+        handlePendingActivityRequest();
+    }
+
+    private void handlePendingActivityRequest() {
+        if (mPendingActivityRequestCode != -1 && isInState(NORMAL)
+                && ((getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
             // Remove any active ProxyActivityStarter task and send RESULT_CANCELED to Launcher.
             onActivityResult(mPendingActivityRequestCode, RESULT_CANCELED, null);
             // ProxyActivityStarter is started with clear task to reset the task after which it
@@ -185,54 +261,84 @@
 
         SysUINavigationMode.INSTANCE.get(this).updateMode();
         mActionsView = findViewById(R.id.overview_actions_view);
-        ((RecentsView) getOverviewPanel()).init(mActionsView);
+        mSplitPlaceholderView = findViewById(R.id.split_placeholder);
+        RecentsView overviewPanel = (RecentsView) getOverviewPanel();
+        mSplitPlaceholderView.init(
+                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this))
+        );
+        overviewPanel.init(mActionsView, mSplitPlaceholderView);
+        mActionsView.setDp(getDeviceProfile());
+        mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
 
-        if (isOverviewActionsEnabled()) {
-            // Overview is above all other launcher elements, including qsb, so move it to the top.
-            getOverviewPanel().bringToFront();
-            mActionsView.bringToFront();
-            mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
-        }
+        mAppTransitionManager = new QuickstepTransitionManager(this);
+        mAppTransitionManager.registerRemoteAnimations();
+
+        bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0);
+
     }
 
-    private boolean isOverviewActionsEnabled() {
-        return FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this);
+    public void setTaskbarUIController(LauncherTaskbarUIController taskbarUIController) {
+        mTaskbarUIController = taskbarUIController;
     }
 
     public <T extends OverviewActionsView> T getActionsView() {
         return (T) mActionsView;
     }
 
-    @Override
-    protected void closeOpenViews(boolean animate) {
-        super.closeOpenViews(animate);
-        ActivityManagerWrapper.getInstance()
-                .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
+    public SplitPlaceholderView getSplitPlaceholderView() {
+        return mSplitPlaceholderView;
     }
 
     @Override
-    protected StateHandler<LauncherState>[] createStateHandlers() {
-        return new StateHandler[] {
-                getAllAppsController(),
-                getWorkspace(),
-                getDepthController(),
-                new RecentsViewStateController(this),
-                new BackButtonAlphaHandler(this)};
+    protected void closeOpenViews(boolean animate) {
+        super.closeOpenViews(animate);
+        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
+    }
+
+    @Override
+    protected void collectStateHandlers(List<StateHandler> out) {
+        super.collectStateHandlers(out);
+        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;
+    }
+
     @Override
-    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
-        return new QuickstepOnboardingPrefs(this, sharedPrefs);
+    public boolean supportsAdaptiveIconAnimation(View clickedView) {
+        return mAppTransitionManager.hasControlRemoteAppTransitionPermission()
+                && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
+    }
+
+    @Override
+    public DragOptions getDefaultWorkspaceDragOptions() {
+        if (mNextWorkspaceDragOptions != null) {
+            DragOptions options = mNextWorkspaceDragOptions;
+            mNextWorkspaceDragOptions = null;
+            return options;
+        }
+        return super.getDefaultWorkspaceDragOptions();
+    }
+
+    public void setNextWorkspaceDragOptions(DragOptions dragOptions) {
+        mNextWorkspaceDragOptions = dragOptions;
     }
 
     @Override
     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
-        QuickstepAppTransitionManagerImpl appTransitionManager =
-                (QuickstepAppTransitionManagerImpl) getAppTransitionManager();
+        QuickstepTransitionManager appTransitionManager = getAppTransitionManager();
         appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
             @Override
             public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
@@ -253,8 +359,16 @@
 
     @Override
     public float[] getNormalOverviewScaleAndOffset() {
-        return SysUINavigationMode.getMode(this) == Mode.NO_BUTTON
-                ? new float[] {1, 1} : new float[] {1.1f, 0};
+        return SysUINavigationMode.getMode(this).hasGestures
+                ? new float[] {1, 1} : new float[] {1.1f, NO_OFFSET};
+    }
+
+    @Override
+    public float getNormalTaskbarScale() {
+        if (mTaskbarUIController != null) {
+            return mTaskbarUIController.getTaskbarScaleOnHome();
+        }
+        return super.getNormalTaskbarScale();
     }
 
     @Override
@@ -273,6 +387,12 @@
             mDepthController.setActivityStarted(isStarted());
         }
 
+        if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
+            if (mTaskbarUIController != null) {
+                mTaskbarUIController.onLauncherResumedOrPaused(hasBeenResumed());
+            }
+        }
+
         super.onActivityFlagsChanged(changeBits);
     }
 
@@ -295,8 +415,10 @@
      */
     private void onLauncherStateOrFocusChanged() {
         boolean shouldBackButtonBeHidden = shouldBackButtonBeHidden(getStateManager().getState());
-        UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
-                shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
+        if (SysUINavigationMode.getMode(this) == TWO_BUTTONS) {
+            UiThreadHelper.setBackButtonAlphaAsync(this, SET_BACK_BUTTON_ALPHA,
+                    shouldBackButtonBeHidden ? 0f : 1f, true /* animate */);
+        }
         if (getDragLayer() != null) {
             getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
         }
@@ -313,19 +435,60 @@
 
     @Override
     public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
-        return Stream.concat(super.getSupportedShortcuts(),
-                Stream.of(WellbeingModel.SHORTCUT_FACTORY));
+        return Stream.concat(Stream.of(WellbeingModel.SHORTCUT_FACTORY),
+                super.getSupportedShortcuts());
     }
 
-    public ShelfPeekAnim getShelfPeekAnim() {
-        return mShelfPeekAnim;
+    @Override
+    public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+        ActivityOptionsWrapper activityOptions =
+                mAppTransitionManager.hasControlRemoteAppTransitionPermission()
+                        ? mAppTransitionManager.getActivityLaunchOptions(v)
+                        : super.getActivityLaunchOptions(v, item);
+        if (mLastTouchUpTime > 0) {
+            ActivityOptionsCompat.setLauncherSourceInfo(
+                    activityOptions.options, mLastTouchUpTime);
+        }
+        activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        addLaunchCookie(item, activityOptions.options);
+        return activityOptions;
     }
 
     /**
-     * Returns Prediction controller for hybrid hotseat
+     * Adds a new launch cookie for the activity launch of the given {@param info} if supported.
      */
-    public HotseatPredictionController getHotseatPredictionController() {
-        return mHotseatPredictionController;
+    public void addLaunchCookie(ItemInfo info, ActivityOptions opts) {
+        if (info == null) {
+            return;
+        }
+        switch (info.container) {
+            case LauncherSettings.Favorites.CONTAINER_DESKTOP:
+            case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
+                // Fall through and continue it's on the workspace (we don't support swiping back
+                // to other containers like all apps or the hotseat predictions (which can change)
+                break;
+            default:
+                if (info.container >= 0) {
+                    // Also allow swiping to folders
+                    break;
+                }
+                // Reset any existing launch cookies associated with the cookie
+                opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
+                return;
+        }
+        switch (info.itemType) {
+            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                // Fall through and continue if it's an app, shortcut, or widget
+                break;
+            default:
+                // Reset any existing launch cookies associated with the cookie
+                opts.setLaunchCookie(ObjectWrapper.wrap(NO_MATCHING_ID));
+                return;
+        }
+        opts.setLaunchCookie(ObjectWrapper.wrap(new Integer(info.id)));
     }
 
     public void setHintUserWillBeActive() {
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 02769c6..661053a 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -16,7 +16,9 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
 
 import android.animation.Animator;
@@ -28,45 +30,67 @@
 import android.os.Handler;
 
 import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
-@TargetApi(Build.VERSION_CODES.P)
-public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat,
-        WrappedAnimationRunnerImpl {
+import java.lang.ref.WeakReference;
 
-    private static final String TAG = "LauncherAnimationRunner";
+/**
+ * This class is needed to wrap any animation runner that is a part of the
+ * RemoteAnimationDefinition:
+ * - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
+ *   created, which in turn registers a new definition
+ * - When the definition is registered, window manager retains a strong binder reference to the
+ *   runner passed in
+ * - If the Launcher activity is recreated, the new definition registered will replace the old
+ *   reference in the system's activity record, but until the system server is GC'd, the binder
+ *   reference will still exist, which references the runner in the Launcher process, which
+ *   references the (old) Launcher activity through this class
+ *
+ * Instead we make the runner provided to the definition static only holding a weak reference to
+ * the runner implementation.  When this animation manager is destroyed, we remove the Launcher
+ * reference to the runner, leaving only the weak ref from the runner.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
+
+    private static final RemoteAnimationFactory DEFAULT_FACTORY =
+            (transit, appTargets, wallpaperTargets, nonAppTargets, result) ->
+                    result.setAnimation(null, null);
 
     private final Handler mHandler;
     private final boolean mStartAtFrontOfQueue;
+    private final WeakReference<RemoteAnimationFactory> mFactory;
+
     private AnimationResult mAnimationResult;
 
     /**
      * @param startAtFrontOfQueue If true, the animation start will be posted at the front of the
      *                            queue to minimize latency.
      */
-    public LauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue) {
+    public LauncherAnimationRunner(Handler handler, RemoteAnimationFactory factory,
+            boolean startAtFrontOfQueue) {
         mHandler = handler;
+        mFactory = new WeakReference<>(factory);
         mStartAtFrontOfQueue = startAtFrontOfQueue;
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
-    // Called only in R+ platform
+    // Called only in S+ platform
     @BinderThread
-    public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+    public void onAnimationStart(
+            int transit,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
+            Runnable runnable) {
         Runnable r = () -> {
             finishExistingAnimation();
-            mAnimationResult = new AnimationResult(() -> {
-                runnable.run();
-                mAnimationResult = null;
-            });
-            onCreateAnimation(appTargets, wallpaperTargets, mAnimationResult);
+            mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
+            getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
+                    mAnimationResult);
         };
         if (mStartAtFrontOfQueue) {
             postAtFrontOfQueueAsynchronously(mHandler, r);
@@ -75,6 +99,14 @@
         }
     }
 
+    // Called only in R platform
+    @BinderThread
+    public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+        onAnimationStart(0 /* transit */, appTargets, wallpaperTargets,
+                new RemoteAnimationTargetCompat[0], runnable);
+    }
+
     // Called only in Q platform
     @BinderThread
     @Deprecated
@@ -82,14 +114,11 @@
         onAnimationStart(appTargets, new RemoteAnimationTargetCompat[0], runnable);
     }
 
-    /**
-     * Called on the UI thread when the animation targets are received. The implementation must
-     * call {@link AnimationResult#setAnimation} with the target animation to be run.
-     */
-    @UiThread
-    public abstract void onCreateAnimation(
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result);
+
+    private RemoteAnimationFactory getFactory() {
+        RemoteAnimationFactory factory = mFactory.get();
+        return factory != null ? factory : DEFAULT_FACTORY;
+    }
 
     @UiThread
     private void finishExistingAnimation() {
@@ -105,42 +134,69 @@
     @BinderThread
     @Override
     public void onAnimationCancelled() {
-        postAsyncCallback(mHandler, this::finishExistingAnimation);
+        postAsyncCallback(mHandler, () -> {
+            finishExistingAnimation();
+            getFactory().onAnimationCancelled();
+        });
     }
 
     public static final class AnimationResult {
 
-        private final Runnable mFinishRunnable;
+        private final Runnable mSyncFinishRunnable;
+        private final Runnable mASyncFinishRunnable;
 
         private AnimatorSet mAnimator;
+        private Runnable mOnCompleteCallback;
         private boolean mFinished = false;
         private boolean mInitialized = false;
 
-        private AnimationResult(Runnable finishRunnable) {
-            mFinishRunnable = finishRunnable;
+        private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {
+            mSyncFinishRunnable = syncFinishRunnable;
+            mASyncFinishRunnable = asyncFinishRunnable;
         }
 
         @UiThread
         private void finish() {
             if (!mFinished) {
-                mFinishRunnable.run();
+                mSyncFinishRunnable.run();
+                UI_HELPER_EXECUTOR.execute(() -> {
+                    mASyncFinishRunnable.run();
+                    if (mOnCompleteCallback != null) {
+                        MAIN_EXECUTOR.execute(mOnCompleteCallback);
+                    }
+                });
                 mFinished = true;
             }
         }
 
         @UiThread
         public void setAnimation(AnimatorSet animation, Context context) {
+            setAnimation(animation, context, null, true);
+        }
+
+        /**
+         * Sets the animation to play for this app launch
+         * @param skipFirstFrame Iff true, we skip the first frame of the animation.
+         *                       We set to false when skipping first frame causes jank.
+         */
+        @UiThread
+        public void setAnimation(AnimatorSet animation, Context context,
+                @Nullable Runnable onCompleteCallback, boolean skipFirstFrame) {
             if (mInitialized) {
                 throw new IllegalStateException("Animation already initialized");
             }
             mInitialized = true;
             mAnimator = animation;
+            mOnCompleteCallback = onCompleteCallback;
             if (mAnimator == null) {
                 finish();
             } else if (mFinished) {
                 // Animation callback was already finished, skip the animation.
                 mAnimator.start();
                 mAnimator.end();
+                if (mOnCompleteCallback != null) {
+                    mOnCompleteCallback.run();
+                }
             } else {
                 // Start the animation
                 mAnimator.addListener(new AnimatorListenerAdapter() {
@@ -151,11 +207,37 @@
                 });
                 mAnimator.start();
 
-                // Because t=0 has the app icon in its original spot, we can skip the
-                // first frame and have the same movement one frame earlier.
-                mAnimator.setCurrentPlayTime(
-                        Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
+                if (skipFirstFrame) {
+                    // Because t=0 has the app icon in its original spot, we can skip the
+                    // first frame and have the same movement one frame earlier.
+                    mAnimator.setCurrentPlayTime(
+                            Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
+                }
             }
         }
     }
+
+    /**
+     * Used with LauncherAnimationRunner as an interface for the runner to call back to the
+     * implementation.
+     */
+    @FunctionalInterface
+    public interface RemoteAnimationFactory {
+
+        /**
+         * Called on the UI thread when the animation targets are received. The implementation must
+         * call {@link AnimationResult#setAnimation} with the target animation to be run.
+         */
+        void onCreateAnimation(int transit,
+                RemoteAnimationTargetCompat[] appTargets,
+                RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets,
+                LauncherAnimationRunner.AnimationResult result);
+
+        /**
+         * Called when the animation is cancelled. This can happen with or without
+         * the create being called.
+         */
+        default void onAnimationCancelled() { }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 7fb0d43..5fc79f0 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -46,8 +46,8 @@
     @Override
     public boolean handleInit(Launcher launcher, boolean alreadyOnHome) {
         if (mRemoteAnimationProvider != null) {
-            QuickstepAppTransitionManagerImpl appTransitionManager =
-                    (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
+            QuickstepTransitionManager appTransitionManager =
+                    ((BaseQuickstepLauncher) launcher).getAppTransitionManager();
 
             // Set a one-time animation provider. After the first call, this will get cleared.
             // TODO: Probably also check the intended target id.
diff --git a/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java
new file mode 100644
index 0000000..96559cb
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.uioverrides.PredictedAppIcon;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+
+import java.util.List;
+
+public class QuickstepAccessibilityDelegate extends LauncherAccessibilityDelegate {
+
+    public QuickstepAccessibilityDelegate(QuickstepLauncher launcher) {
+        super(launcher);
+        mActions.put(PIN_PREDICTION, new LauncherAction(
+                PIN_PREDICTION, R.string.pin_prediction, KeyEvent.KEYCODE_P));
+    }
+
+    @Override
+    protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
+        if (host instanceof PredictedAppIcon && !((PredictedAppIcon) host).isPinned()) {
+            out.add(new LauncherAction(PIN_PREDICTION, R.string.pin_prediction,
+                    KeyEvent.KEYCODE_P));
+        }
+        super.getSupportedActions(host, item, out);
+    }
+
+    @Override
+    protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
+        QuickstepLauncher launcher = (QuickstepLauncher) mLauncher;
+        if (action == PIN_PREDICTION) {
+            if (launcher.getHotseatPredictionController() == null) {
+                return false;
+            }
+            launcher.getHotseatPredictionController().pinPrediction(item);
+            return true;
+        }
+        return super.performAction(host, item, action, fromKeyboard);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
deleted file mode 100644
index 2d096d1..0000000
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ /dev/null
@@ -1,934 +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;
-
-import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
-import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
-import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
-import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
-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.postAsyncCallback;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
-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.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.statehandlers.DepthController.DEPTH;
-import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
-import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
-import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
-import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Pair;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.util.DynamicResource;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.launcher3.views.FloatingIconView;
-import com.android.quickstep.RemoteAnimationTargets;
-import com.android.quickstep.util.MultiValueUpdateListener;
-import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
-import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-/**
- * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
- * home and/or all-apps.  Not used for 3p launchers.
- */
-@TargetApi(Build.VERSION_CODES.O)
-@SuppressWarnings("unused")
-public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTransitionManager
-        implements OnDeviceProfileChangeListener {
-
-    private static final String TAG = "QuickstepTransition";
-
-    /** Duration of status bar animations. */
-    public static final int STATUS_BAR_TRANSITION_DURATION = 120;
-
-    /**
-     * Since our animations decelerate heavily when finishing, we want to start status bar animations
-     * x ms before the ending.
-     */
-    public static final int STATUS_BAR_TRANSITION_PRE_DELAY = 96;
-
-    private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
-            "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
-
-    private static final long APP_LAUNCH_DURATION = 450;
-    // Use a shorter duration for x or y translation to create a curve effect
-    private static final long APP_LAUNCH_CURVED_DURATION = 250;
-    private static final long APP_LAUNCH_ALPHA_DURATION = 50;
-    private static final long APP_LAUNCH_ALPHA_START_DELAY = 25;
-
-    // We scale the durations for the downward app launch animations (minus the scale animation).
-    private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f;
-    private static final long APP_LAUNCH_DOWN_DURATION =
-            (long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
-    private static final long APP_LAUNCH_DOWN_CURVED_DURATION =
-            (long) (APP_LAUNCH_CURVED_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
-    private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
-            (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
-
-    private static final long CROP_DURATION = 375;
-    private static final long RADIUS_DURATION = 375;
-
-    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;
-
-    protected static final int CONTENT_ALPHA_DURATION = 217;
-    protected static final int CONTENT_TRANSLATION_DURATION = 350;
-
-    // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
-    public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
-
-    protected final BaseQuickstepLauncher mLauncher;
-
-    private final DragLayer mDragLayer;
-    private final AlphaProperty mDragLayerAlpha;
-
-    final Handler mHandler;
-    private final boolean mIsRtl;
-
-    private final float mContentTransY;
-    private final float mWorkspaceTransY;
-    private final float mClosingWindowTransY;
-
-    private DeviceProfile mDeviceProfile;
-
-    private RemoteAnimationProvider mRemoteAnimationProvider;
-    // Strong refs to runners which are cleared when the launcher activity is destroyed
-    private WrappedAnimationRunnerImpl mWallpaperOpenRunner;
-    private WrappedAnimationRunnerImpl mAppLaunchRunner;
-    private WrappedAnimationRunnerImpl mKeyguardGoingAwayRunner;
-
-    private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationStart(Animator animation) {
-            mLauncher.addForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mLauncher.clearForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
-        }
-    };
-
-    public QuickstepAppTransitionManagerImpl(Context context) {
-        mLauncher = Launcher.cast(Launcher.getLauncher(context));
-        mDragLayer = mLauncher.getDragLayer();
-        mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
-        mHandler = new Handler(Looper.getMainLooper());
-        mIsRtl = Utilities.isRtl(mLauncher.getResources());
-        mDeviceProfile = mLauncher.getDeviceProfile();
-
-        Resources res = mLauncher.getResources();
-        mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
-        mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
-        mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
-
-        mLauncher.addOnDeviceProfileChangeListener(this);
-    }
-
-    @Override
-    public void onDeviceProfileChanged(DeviceProfile dp) {
-        mDeviceProfile = dp;
-    }
-
-    @Override
-    public boolean supportsAdaptiveIconAnimation() {
-        return hasControlRemoteAppTransitionPermission()
-                && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
-    }
-
-    /**
-     * @return ActivityOptions with remote animations that controls how the window of the opening
-     *         targets are displayed.
-     */
-    @Override
-    public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
-        if (hasControlRemoteAppTransitionPermission()) {
-            boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
-            mAppLaunchRunner = new AppLaunchAnimationRunner(mHandler, v);
-            RemoteAnimationRunnerCompat runner = new WrappedLauncherAnimationRunner<>(
-                    mAppLaunchRunner, true /* startAtFrontOfQueue */);
-
-            // Note that this duration is a guess as we do not know if the animation will be a
-            // recents launch or not for sure until we know the opening app targets.
-            long duration = fromRecents
-                    ? RECENTS_LAUNCH_DURATION
-                    : APP_LAUNCH_DURATION;
-
-            long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
-                    - STATUS_BAR_TRANSITION_PRE_DELAY;
-            return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
-                    runner, duration, statusBarTransitionDelay));
-        }
-        return super.getActivityLaunchOptions(launcher, v);
-    }
-
-    /**
-     * Whether the launch is a recents app transition and we should do a launch animation
-     * from the recents view. Note that if the remote animation targets are not provided, this
-     * may not always be correct as we may resolve the opening app to a task when the animation
-     * starts.
-     *
-     * @param v the view to launch from
-     * @param targets apps that are opening/closing
-     * @return true if the app is launching from recents, false if it most likely is not
-     */
-    protected abstract boolean isLaunchingFromRecents(@NonNull View v,
-            @Nullable RemoteAnimationTargetCompat[] targets);
-
-    /**
-     * Composes the animations for a launch from the recents list.
-     *
-     * @param anim the animator set to add to
-     * @param v the launching view
-     * @param appTargets the apps that are opening/closing
-     * @param launcherClosing true if the launcher app is closing
-     */
-    protected abstract void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing);
-
-    /**
-     * Compose the animations for a launch from the app icon.
-     *
-     * @param anim the animation to add to
-     * @param v the launching view with the icon
-     * @param appTargets the list of opening/closing apps
-     * @param launcherClosing true if launcher is closing
-     */
-    private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
-            @NonNull RemoteAnimationTargetCompat[] appTargets,
-            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
-            boolean launcherClosing) {
-        // Set the state animation first so that any state listeners are called
-        // before our internal listeners.
-        mLauncher.getStateManager().setCurrentAnimation(anim);
-
-        Rect windowTargetBounds = getWindowTargetBounds(appTargets);
-        boolean isAllOpeningTargetTrs = true;
-        for (int i = 0; i < appTargets.length; i++) {
-            RemoteAnimationTargetCompat target = appTargets[i];
-            if (target.mode == MODE_OPENING) {
-                isAllOpeningTargetTrs &= target.isTranslucent;
-            }
-            if (!isAllOpeningTargetTrs) break;
-        }
-        anim.play(getOpeningWindowAnimators(v, appTargets, wallpaperTargets, windowTargetBounds,
-                !isAllOpeningTargetTrs));
-        if (launcherClosing) {
-            Pair<AnimatorSet, Runnable> launcherContentAnimator =
-                    getLauncherContentAnimator(true /* isAppOpening */,
-                            new float[] {0, -mContentTransY});
-            anim.play(launcherContentAnimator.first);
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    launcherContentAnimator.second.run();
-                }
-            });
-        } else {
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    mLauncher.addOnResumeCallback(() ->
-                            ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
-                            mLauncher.getStateManager().getState().getDepth(mLauncher)).start());
-                }
-            });
-        }
-    }
-
-    /**
-     * Return the window bounds of the opening target.
-     * In multiwindow mode, we need to get the final size of the opening app window target to help
-     * figure out where the floating view should animate to.
-     */
-    private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] appTargets) {
-        Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
-        if (mLauncher.isInMultiWindowMode()) {
-            for (RemoteAnimationTargetCompat target : appTargets) {
-                if (target.mode == MODE_OPENING) {
-                    bounds.set(target.screenSpaceBounds);
-                    if (target.localBounds != null) {
-                        bounds.set(target.localBounds);
-                    } else {
-                        bounds.offsetTo(target.position.x, target.position.y);
-                    }
-                    return bounds;
-                }
-            }
-        }
-        return bounds;
-    }
-
-    public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider,
-            CancellationSignal cancellationSignal) {
-        mRemoteAnimationProvider = animationProvider;
-        cancellationSignal.setOnCancelListener(() -> {
-            if (animationProvider == mRemoteAnimationProvider) {
-                mRemoteAnimationProvider = null;
-            }
-        });
-    }
-
-    /**
-     * Content is everything on screen except the background and the floating view (if any).
-     *
-     * @param isAppOpening True when this is called when an app is opening.
-     *                     False when this is called when an app is closing.
-     * @param trans Array that contains the start and end translation values for the content.
-     */
-    private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
-            float[] trans) {
-        AnimatorSet launcherAnimator = new AnimatorSet();
-        Runnable endListener;
-
-        float[] alphas = isAppOpening
-                ? new float[] {1, 0}
-                : new float[] {0, 1};
-
-        if (mLauncher.isInState(ALL_APPS)) {
-            // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
-            final View appsView = mLauncher.getAppsView();
-            final float startAlpha = appsView.getAlpha();
-            final float startY = appsView.getTranslationY();
-            appsView.setAlpha(alphas[0]);
-            appsView.setTranslationY(trans[0]);
-
-            ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
-            alpha.setDuration(CONTENT_ALPHA_DURATION);
-            alpha.setInterpolator(LINEAR);
-            appsView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            alpha.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    appsView.setLayerType(View.LAYER_TYPE_NONE, null);
-                }
-            });
-            ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, trans);
-            transY.setInterpolator(AGGRESSIVE_EASE);
-            transY.setDuration(CONTENT_TRANSLATION_DURATION);
-
-            launcherAnimator.play(alpha);
-            launcherAnimator.play(transY);
-
-            endListener = () -> {
-                appsView.setAlpha(startAlpha);
-                appsView.setTranslationY(startY);
-                appsView.setLayerType(View.LAYER_TYPE_NONE, null);
-            };
-        } else if (mLauncher.isInState(OVERVIEW)) {
-            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
-            launcherAnimator.play(ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
-                    allAppsController.getProgress(), ALL_APPS_PROGRESS_OFF_SCREEN));
-            endListener = composeViewContentAnimator(launcherAnimator, alphas, trans);
-        } else {
-            mDragLayerAlpha.setValue(alphas[0]);
-            ObjectAnimator alpha =
-                    ObjectAnimator.ofFloat(mDragLayerAlpha, MultiValueAlpha.VALUE, alphas);
-            alpha.setDuration(CONTENT_ALPHA_DURATION);
-            alpha.setInterpolator(LINEAR);
-            launcherAnimator.play(alpha);
-
-            Workspace workspace = mLauncher.getWorkspace();
-            View currentPage = ((CellLayout) workspace.getChildAt(workspace.getCurrentPage()))
-                    .getShortcutsAndWidgets();
-            View hotseat = mLauncher.getHotseat();
-            View qsb = mLauncher.findViewById(R.id.search_container_all_apps);
-
-            currentPage.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            qsb.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
-            launcherAnimator.play(ObjectAnimator.ofFloat(currentPage, View.TRANSLATION_Y, trans));
-            launcherAnimator.play(ObjectAnimator.ofFloat(hotseat, View.TRANSLATION_Y, trans));
-            launcherAnimator.play(ObjectAnimator.ofFloat(qsb, View.TRANSLATION_Y, trans));
-
-            // Pause page indicator animations as they lead to layer trashing.
-            mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
-
-            endListener = () -> {
-                currentPage.setTranslationY(0);
-                hotseat.setTranslationY(0);
-                qsb.setTranslationY(0);
-
-                currentPage.setLayerType(View.LAYER_TYPE_NONE, null);
-                hotseat.setLayerType(View.LAYER_TYPE_NONE, null);
-                qsb.setLayerType(View.LAYER_TYPE_NONE, null);
-
-                mDragLayerAlpha.setValue(1f);
-                mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
-            };
-        }
-        return new Pair<>(launcherAnimator, endListener);
-    }
-
-    /**
-     * Compose recents view alpha and translation Y animation when launcher opens/closes apps.
-     *
-     * @param anim the animator set to add to
-     * @param alphas the alphas to animate to over time
-     * @param trans the translation Y values to animator to over time
-     * @return listener to run when the animation ends
-     */
-    protected abstract Runnable composeViewContentAnimator(@NonNull AnimatorSet anim,
-            float[] alphas, float[] trans);
-
-    /**
-     * @return Animator that controls the window of the opening targets from app icons.
-     */
-    private Animator getOpeningWindowAnimators(View v,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            Rect windowTargetBounds, boolean toggleVisibility) {
-        RectF bounds = new RectF();
-        FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
-                toggleVisibility, bounds, true /* isOpening */);
-        Rect crop = new Rect();
-        Matrix matrix = new Matrix();
-
-        RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
-                wallpaperTargets, MODE_OPENING);
-        SurfaceTransactionApplier surfaceApplier =
-                new SurfaceTransactionApplier(floatingView);
-        openingTargets.addReleaseCheck(surfaceApplier);
-
-        // Scale the app icon to take up the entire screen. This simplifies the math when
-        // animating the app window position / scale.
-        float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
-        float maxScaleX = smallestSize / bounds.width();
-        float maxScaleY = smallestSize / bounds.height();
-        float scale = Math.max(maxScaleX, maxScaleY);
-        float startScale = 1f;
-        if (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
-            Drawable dr = ((BubbleTextView) v).getIcon();
-            if (dr instanceof FastBitmapDrawable) {
-                startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
-            }
-        }
-        final float initialStartScale = startScale;
-
-        int[] dragLayerBounds = new int[2];
-        mDragLayer.getLocationOnScreen(dragLayerBounds);
-
-        // Animate the app icon to the center of the window bounds in screen coordinates.
-        float centerX = windowTargetBounds.centerX() - dragLayerBounds[0];
-        float centerY = windowTargetBounds.centerY() - dragLayerBounds[1];
-
-        float dX = centerX - bounds.centerX();
-        float dY = centerY - bounds.centerY();
-
-        boolean useUpwardAnimation = bounds.top > centerY
-                || Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx;
-        final long xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
-                : APP_LAUNCH_DOWN_DURATION;
-        final long yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
-                : APP_LAUNCH_DOWN_CURVED_DURATION;
-        final long alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
-                : APP_LAUNCH_ALPHA_DOWN_DURATION;
-
-        RectF targetBounds = new RectF(windowTargetBounds);
-        RectF iconBounds = new RectF();
-        RectF temp = new RectF();
-        Point tmpPos = new Point();
-
-        AnimatorSet animatorSet = new AnimatorSet();
-        ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
-        appAnimator.setDuration(APP_LAUNCH_DURATION);
-        appAnimator.setInterpolator(LINEAR);
-        appAnimator.addListener(floatingView);
-        appAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (v instanceof BubbleTextView) {
-                    ((BubbleTextView) v).setStayPressed(false);
-                }
-                openingTargets.release();
-            }
-        });
-
-        float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION;
-
-        final float startCrop;
-        final float endCrop;
-        if (mDeviceProfile.isVerticalBarLayout()) {
-            startCrop = windowTargetBounds.height();
-            endCrop = windowTargetBounds.width();
-        } else {
-            startCrop = windowTargetBounds.width();
-            endCrop = windowTargetBounds.height();
-        }
-
-        final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
-                ? startCrop / 2f : 0f;
-        final float windowRadius = mDeviceProfile.isMultiWindowMode
-                ? 0 : getWindowCornerRadius(mLauncher.getResources());
-        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
-            FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE);
-            FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE);
-            FloatProp mScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION,
-                    EXAGGERATED_EASE);
-            FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY,
-                    alphaDuration, LINEAR);
-            FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, CROP_DURATION,
-                    EXAGGERATED_EASE);
-            FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
-                    RADIUS_DURATION, EXAGGERATED_EASE);
-
-            @Override
-            public void onUpdate(float percent) {
-                // Calculate the size.
-                float width = bounds.width() * mScale.value;
-                float height = bounds.height() * mScale.value;
-
-                // Animate the crop so that it starts off as a square.
-                final int cropWidth;
-                final int cropHeight;
-                if (mDeviceProfile.isVerticalBarLayout()) {
-                    cropWidth = (int) mCroppedSize.value;
-                    cropHeight = windowTargetBounds.height();
-                } else {
-                    cropWidth = windowTargetBounds.width();
-                    cropHeight = (int) mCroppedSize.value;
-                }
-                crop.set(0, 0, cropWidth, cropHeight);
-
-                // Scale the size to match the crop.
-                float scaleX = width / cropWidth;
-                float scaleY = height / cropHeight;
-                float scale = Math.min(1f, Math.max(scaleX, scaleY));
-
-                float scaledCropWidth = cropWidth * scale;
-                float scaledCropHeight = cropHeight * scale;
-                float offsetX  = (scaledCropWidth - width) / 2;
-                float offsetY = (scaledCropHeight - height) / 2;
-
-                // Calculate the window position.
-                temp.set(bounds);
-                temp.offset(dragLayerBounds[0], dragLayerBounds[1]);
-                temp.offset(mDx.value, mDy.value);
-                Utilities.scaleRectFAboutCenter(temp, mScale.value);
-                float windowTransX0 = temp.left - offsetX;
-                float windowTransY0 = temp.top - offsetY;
-
-                // Calculate the icon position.
-                iconBounds.set(bounds);
-                iconBounds.offset(mDx.value, mDy.value);
-                Utilities.scaleRectFAboutCenter(iconBounds, mScale.value);
-                iconBounds.left -= offsetX;
-                iconBounds.top -= offsetY;
-                iconBounds.right += offsetX;
-                iconBounds.bottom += offsetY;
-
-                float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
-                float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
-                SurfaceParams[] params = new SurfaceParams[appTargets.length];
-                for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
-                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
-
-                    if (target.mode == MODE_OPENING) {
-                        matrix.setScale(scale, scale);
-                        matrix.postTranslate(windowTransX0, windowTransY0);
-
-                        floatingView.update(iconBounds, mIconAlpha.value, percent, 0f,
-                                mWindowRadius.value * scale, true /* isOpening */);
-                        builder.withMatrix(matrix)
-                                .withWindowCrop(crop)
-                                .withAlpha(1f - mIconAlpha.value)
-                                .withCornerRadius(mWindowRadius.value);
-                    } else {
-                        tmpPos.set(target.position.x, target.position.y);
-                        if (target.localBounds != null) {
-                            final Rect localBounds = target.localBounds;
-                            tmpPos.set(target.localBounds.left, target.localBounds.top);
-                        }
-
-                        matrix.setTranslate(tmpPos.x, tmpPos.y);
-                        final Rect crop = new Rect(target.screenSpaceBounds);
-                        crop.offsetTo(0, 0);
-                        builder.withMatrix(matrix)
-                                .withWindowCrop(crop)
-                                .withAlpha(1f);
-                    }
-                    params[i] = builder.build();
-                }
-                surfaceApplier.scheduleApply(params);
-            }
-        });
-
-        // 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;
-        DepthController depthController = mLauncher.getDepthController();
-        ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
-                BACKGROUND_APP.getDepth(mLauncher))
-                .setDuration(APP_LAUNCH_DURATION);
-        if (allowBlurringLauncher) {
-            depthController.setSurfaceToApp(RemoteAnimationProvider.findLowestOpaqueLayerTarget(
-                    appTargets, MODE_OPENING));
-            backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    depthController.setSurfaceToApp(null);
-                }
-            });
-        }
-
-        animatorSet.playTogether(appAnimator, backgroundRadiusAnim);
-        return animatorSet;
-    }
-
-    /**
-     * Registers remote animations used when closing apps to home screen.
-     */
-    @Override
-    public void registerRemoteAnimations() {
-        if (SEPARATE_RECENTS_ACTIVITY.get()) {
-            return;
-        }
-        if (hasControlRemoteAppTransitionPermission()) {
-            mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
-
-            RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
-            definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
-                    WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
-                    new RemoteAnimationAdapterCompat(
-                            new WrappedLauncherAnimationRunner<>(mWallpaperOpenRunner,
-                                    false /* startAtFrontOfQueue */),
-                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
-
-            if (KEYGUARD_ANIMATION.get()) {
-                mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
-                definition.addRemoteAnimation(
-                        WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
-                        new RemoteAnimationAdapterCompat(
-                                new WrappedLauncherAnimationRunner<>(mKeyguardGoingAwayRunner,
-                                        true /* startAtFrontOfQueue */),
-                                CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
-            }
-
-            new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
-        }
-    }
-
-    /**
-     * Unregisters all remote animations.
-     */
-    @Override
-    public void unregisterRemoteAnimations() {
-        if (SEPARATE_RECENTS_ACTIVITY.get()) {
-            return;
-        }
-        if (hasControlRemoteAppTransitionPermission()) {
-            new ActivityCompat(mLauncher).unregisterRemoteAnimations();
-
-            // Also clear strong references to the runners registered with the remote animation
-            // definition so we don't have to wait for the system gc
-            mWallpaperOpenRunner = null;
-            mAppLaunchRunner = null;
-            mKeyguardGoingAwayRunner = null;
-        }
-    }
-
-    private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
-        return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode);
-    }
-
-    /**
-     * @return Runner that plays when user goes to Launcher
-     *         ie. pressing home, swiping up from nav bar.
-     */
-    WrappedAnimationRunnerImpl createWallpaperOpenRunner(boolean fromUnlock) {
-        return new WallpaperOpenLauncherAnimationRunner(mHandler, fromUnlock);
-    }
-
-    /**
-     * Animator that controls the transformations of the windows when unlocking the device.
-     */
-    private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
-        SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
-        ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
-        unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
-        float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 :
-                QuickStepContract.getWindowCornerRadius(mLauncher.getResources());
-        unlockAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                SurfaceParams[] params = new SurfaceParams[appTargets.length];
-                for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
-                    params[i] = new SurfaceParams.Builder(target.leash)
-                            .withAlpha(1f)
-                            .withWindowCrop(target.screenSpaceBounds)
-                            .withCornerRadius(cornerRadius)
-                            .build();
-                }
-                surfaceApplier.scheduleApply(params);
-            }
-        });
-        return unlockAnimator;
-    }
-
-    /**
-     * Animator that controls the transformations of the windows the targets that are closing.
-     */
-    private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
-        SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
-        Matrix matrix = new Matrix();
-        Point tmpPos = new Point();
-        ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
-        int duration = CLOSING_TRANSITION_DURATION_MS;
-        float windowCornerRadius = mDeviceProfile.isMultiWindowMode
-                ? 0 : getWindowCornerRadius(mLauncher.getResources());
-        closingAnimator.setDuration(duration);
-        closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
-            FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
-            FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
-            FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
-
-            @Override
-            public void onUpdate(float percent) {
-                SurfaceParams[] params = new SurfaceParams[appTargets.length];
-                for (int i = appTargets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = appTargets[i];
-                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
-
-                    tmpPos.set(target.position.x, target.position.y);
-                    if (target.localBounds != null) {
-                        tmpPos.set(target.localBounds.left, target.localBounds.top);
-                    }
-
-                    if (target.mode == MODE_CLOSING) {
-                        matrix.setScale(mScale.value, mScale.value,
-                                target.screenSpaceBounds.centerX(),
-                                target.screenSpaceBounds.centerY());
-                        matrix.postTranslate(0, mDy.value);
-                        matrix.postTranslate(tmpPos.x, tmpPos.y);
-                        builder.withMatrix(matrix)
-                                .withAlpha(mAlpha.value)
-                                .withCornerRadius(windowCornerRadius);
-                    } else {
-                        matrix.setTranslate(tmpPos.x, tmpPos.y);
-                        builder.withMatrix(matrix)
-                                .withAlpha(1f);
-                    }
-                    final Rect crop = new Rect(target.screenSpaceBounds);
-                    crop.offsetTo(0, 0);
-                    params[i] = builder
-                            .withWindowCrop(crop)
-                            .build();
-                }
-                surfaceApplier.scheduleApply(params);
-            }
-        });
-
-        return closingAnimator;
-    }
-
-    private boolean hasControlRemoteAppTransitionPermission() {
-        return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
-    /**
-     * Remote animation runner for animation from the app to Launcher, including recents.
-     */
-    protected class WallpaperOpenLauncherAnimationRunner implements WrappedAnimationRunnerImpl {
-
-        private final Handler mHandler;
-        private final boolean mFromUnlock;
-
-        public WallpaperOpenLauncherAnimationRunner(Handler handler, boolean fromUnlock) {
-            mHandler = handler;
-            mFromUnlock = fromUnlock;
-        }
-
-        @Override
-        public Handler getHandler() {
-            return mHandler;
-        }
-
-        @Override
-        public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets,
-                LauncherAnimationRunner.AnimationResult result) {
-            if (mLauncher.isDestroyed()) {
-                AnimatorSet anim = new AnimatorSet();
-                anim.play(getClosingWindowAnimators(appTargets, wallpaperTargets));
-                result.setAnimation(anim, mLauncher.getApplicationContext());
-                return;
-            }
-
-            if (!mLauncher.hasBeenResumed()) {
-                // If launcher is not resumed, wait until new async-frame after resume
-                mLauncher.addOnResumeCallback(() ->
-                        postAsyncCallback(mHandler, () ->
-                                onCreateAnimation(appTargets, wallpaperTargets, result)));
-                return;
-            }
-
-            if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
-                mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
-                mLauncher.getStateManager().moveToRestState();
-            }
-
-            AnimatorSet anim = null;
-            RemoteAnimationProvider provider = mRemoteAnimationProvider;
-            if (provider != null) {
-                anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
-            }
-
-            if (anim == null) {
-                anim = new AnimatorSet();
-                anim.play(mFromUnlock
-                        ? getUnlockWindowAnimator(appTargets, wallpaperTargets)
-                        : getClosingWindowAnimators(appTargets, wallpaperTargets));
-
-                // 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
-                // contents of launcher unless we know that we are animating home as a result
-                // of the home button press with quickstep, which will result in launcher being
-                // started on touch down, prior to the animation home (and won't be in the
-                // 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()) {
-                    // Only register the content animation for cancellation when state changes
-                    mLauncher.getStateManager().setCurrentAnimation(anim);
-
-                    if (mLauncher.isInState(LauncherState.ALL_APPS)) {
-                        Pair<AnimatorSet, Runnable> contentAnimator =
-                                getLauncherContentAnimator(false /* isAppOpening */,
-                                        new float[] {-mContentTransY, 0});
-                        contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
-                        anim.play(contentAnimator.first);
-                        anim.addListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                contentAnimator.second.run();
-                            }
-                        });
-                    } else {
-                        float velocityPxPerS = DynamicResource.provider(mLauncher)
-                                .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
-                        anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false)
-                                .getAnimators());
-                    }
-                }
-            }
-
-            mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
-            result.setAnimation(anim, mLauncher);
-        }
-    }
-
-    /**
-     * Remote animation runner for animation to launch an app.
-     */
-    private class AppLaunchAnimationRunner implements WrappedAnimationRunnerImpl {
-
-        private final Handler mHandler;
-        private final View mV;
-
-        AppLaunchAnimationRunner(Handler handler, View v) {
-            mHandler = handler;
-            mV = v;
-        }
-
-        @Override
-        public Handler getHandler() {
-            return mHandler;
-        }
-
-        @Override
-        public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets,
-                LauncherAnimationRunner.AnimationResult result) {
-            AnimatorSet anim = new AnimatorSet();
-
-            boolean launcherClosing =
-                    launcherIsATargetWithMode(appTargets, MODE_CLOSING);
-
-            if (isLaunchingFromRecents(mV, appTargets)) {
-                composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
-                        launcherClosing);
-            } else {
-                composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
-                        launcherClosing);
-            }
-
-            if (launcherClosing) {
-                anim.addListener(mForceInvisibleListener);
-            }
-
-            result.setAnimation(anim, mLauncher);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
new file mode 100644
index 0000000..5bb76d6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -0,0 +1,1501 @@
+/*
+ * 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;
+
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+
+import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
+import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
+import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
+import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
+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.postAsyncCallback;
+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_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.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
+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;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Point;
+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.Looper;
+import android.os.SystemProperties;
+import android.util.Pair;
+import android.util.Size;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.ViewTreeObserver;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
+
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.FloatingIconView;
+import com.android.launcher3.views.ScrimView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.WorkspaceRevealAnim;
+import com.android.quickstep.views.FloatingWidgetView;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.ActivityCompat;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.BlurUtils;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
+import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
+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.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.wm.shell.startingsurface.IStartingWindowListener;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+/**
+ * Manages the opening and closing app transitions from Launcher
+ */
+public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
+
+    private static final String TAG = "QuickstepTransition";
+
+    private static final boolean ENABLE_SHELL_STARTING_SURFACE =
+            SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
+
+    /** Duration of status bar animations. */
+    public static final int STATUS_BAR_TRANSITION_DURATION = 120;
+
+    /**
+     * Since our animations decelerate heavily when finishing, we want to start status bar
+     * animations x ms before the ending.
+     */
+    public static final int STATUS_BAR_TRANSITION_PRE_DELAY = 96;
+
+    private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
+            "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
+
+    private static final long APP_LAUNCH_DURATION = 450;
+    // Use a shorter duration for x or y translation to create a curve effect
+    private static final long APP_LAUNCH_CURVED_DURATION = 250;
+    private static final long APP_LAUNCH_ALPHA_DURATION = 50;
+    private static final long APP_LAUNCH_ALPHA_START_DELAY = 25;
+
+    // We scale the durations for the downward app launch animations (minus the scale animation).
+    private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f;
+    private static final long APP_LAUNCH_DOWN_DURATION =
+            (long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
+    private static final long APP_LAUNCH_DOWN_CURVED_DURATION =
+            (long) (APP_LAUNCH_CURVED_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
+    private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
+            (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
+
+    public static final int ANIMATION_NAV_FADE_IN_DURATION = 266;
+    public static final int ANIMATION_NAV_FADE_OUT_DURATION = 133;
+    public static final long ANIMATION_DELAY_NAV_FADE_IN =
+            APP_LAUNCH_DURATION - ANIMATION_NAV_FADE_IN_DURATION;
+    public static final Interpolator NAV_FADE_IN_INTERPOLATOR =
+            new PathInterpolator(0f, 0f, 0f, 1f);
+    public static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
+            new PathInterpolator(0.2f, 0f, 1f, 1f);
+
+    private static final long CROP_DURATION = 375;
+    private static final long RADIUS_DURATION = 375;
+
+    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 CONTENT_ALPHA_DURATION = 217;
+    protected static final int CONTENT_SCALE_DURATION = 350;
+    protected static final int CONTENT_SCRIM_DURATION = 350;
+
+    private static final int MAX_NUM_TASKS = 5;
+
+    // Cross-fade duration between App Widget and App
+    private static final int WIDGET_CROSSFADE_DURATION_MILLIS = 125;
+
+    protected final BaseQuickstepLauncher mLauncher;
+
+    private final DragLayer mDragLayer;
+    private final AlphaProperty mDragLayerAlpha;
+
+    final Handler mHandler;
+
+    private final float mContentScale;
+    private final float mClosingWindowTransY;
+    private final float mMaxShadowRadius;
+
+    private final StartingWindowListener mStartingWindowListener = new StartingWindowListener();
+
+    private DeviceProfile mDeviceProfile;
+
+    private RemoteAnimationProvider mRemoteAnimationProvider;
+    // Strong refs to runners which are cleared when the launcher activity is destroyed
+    private RemoteAnimationFactory mWallpaperOpenRunner;
+    private RemoteAnimationFactory mAppLaunchRunner;
+    private RemoteAnimationFactory mKeyguardGoingAwayRunner;
+
+    private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
+    private RemoteTransitionCompat mLauncherOpenTransition;
+
+    private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mLauncher.addForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mLauncher.clearForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
+        }
+    };
+
+    // Pairs of window starting type and starting window background color for starting tasks
+    // Will never be larger than MAX_NUM_TASKS
+    private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
+
+    public QuickstepTransitionManager(Context context) {
+        mLauncher = Launcher.cast(Launcher.getLauncher(context));
+        mDragLayer = mLauncher.getDragLayer();
+        mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
+        mHandler = new Handler(Looper.getMainLooper());
+        mDeviceProfile = mLauncher.getDeviceProfile();
+
+        Resources res = mLauncher.getResources();
+        mContentScale = res.getFloat(R.dimen.content_scale);
+        mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
+        mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
+
+        mLauncher.addOnDeviceProfileChangeListener(this);
+
+        if (supportsSSplashScreen()) {
+            mTaskStartParams = new LinkedHashMap<Integer, Pair<Integer, Integer>>(MAX_NUM_TASKS) {
+                @Override
+                protected boolean removeEldestEntry(Entry<Integer, Pair<Integer, Integer>> entry) {
+                    return size() > MAX_NUM_TASKS;
+                }
+            };
+
+            mStartingWindowListener.setTransitionManager(this);
+            SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
+                    mStartingWindowListener);
+        }
+    }
+
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile dp) {
+        mDeviceProfile = dp;
+    }
+
+    /**
+     * @return ActivityOptions with remote animations that controls how the window of the opening
+     * targets are displayed.
+     */
+    public ActivityOptionsWrapper getActivityLaunchOptions(View v) {
+        boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
+        RunnableList onEndCallback = new RunnableList();
+        mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
+        RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(
+                mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
+
+        // Note that this duration is a guess as we do not know if the animation will be a
+        // recents launch or not for sure until we know the opening app targets.
+        long duration = fromRecents
+                ? RECENTS_LAUNCH_DURATION
+                : APP_LAUNCH_DURATION;
+
+        long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
+                - STATUS_BAR_TRANSITION_PRE_DELAY;
+        RemoteAnimationAdapterCompat adapterCompat =
+                new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay);
+        return new ActivityOptionsWrapper(
+                ActivityOptionsCompat.makeRemoteAnimation(adapterCompat), onEndCallback);
+    }
+
+    /**
+     * Whether the launch is a recents app transition and we should do a launch animation
+     * from the recents view. Note that if the remote animation targets are not provided, this
+     * may not always be correct as we may resolve the opening app to a task when the animation
+     * starts.
+     *
+     * @param v       the view to launch from
+     * @param targets apps that are opening/closing
+     * @return true if the app is launching from recents, false if it most likely is not
+     */
+    protected boolean isLaunchingFromRecents(@NonNull View v,
+            @Nullable RemoteAnimationTargetCompat[] targets) {
+        return mLauncher.getStateManager().getState().overviewUi
+                && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
+    }
+
+    /**
+     * Composes the animations for a launch from the recents list.
+     *
+     * @param anim            the animator set to add to
+     * @param v               the launching view
+     * @param appTargets      the apps that are opening/closing
+     * @param launcherClosing true if the launcher app is closing
+     */
+    protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+            @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing) {
+        TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
+                nonAppTargets, launcherClosing, mLauncher.getStateManager(),
+                mLauncher.getOverviewPanel(), mLauncher.getDepthController());
+    }
+
+    private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTargetCompat[] targets) {
+        boolean isAllOpeningTargetTrs = true;
+        for (int i = 0; i < targets.length; i++) {
+            RemoteAnimationTargetCompat target = targets[i];
+            if (target.mode == MODE_OPENING) {
+                isAllOpeningTargetTrs &= target.isTranslucent;
+            }
+            if (!isAllOpeningTargetTrs) break;
+        }
+        return isAllOpeningTargetTrs;
+    }
+
+    /**
+     * Compose the animations for a launch from the app icon.
+     *
+     * @param anim            the animation to add to
+     * @param v               the launching view with the icon
+     * @param appTargets      the list of opening/closing apps
+     * @param launcherClosing true if launcher is closing
+     */
+    private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+            @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+            boolean launcherClosing) {
+        // Set the state animation first so that any state listeners are called
+        // before our internal listeners.
+        mLauncher.getStateManager().setCurrentAnimation(anim);
+
+        final int rotationChange = getRotationChange(appTargets);
+        // Note: the targetBounds are relative to the launcher
+        int startDelay = getSingleFrameMs(mLauncher);
+        Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
+        Animator windowAnimator = getOpeningWindowAnimators(v, appTargets, wallpaperTargets,
+                nonAppTargets, windowTargetBounds, areAllTargetsTranslucent(appTargets),
+                rotationChange);
+        windowAnimator.setStartDelay(startDelay);
+        anim.play(windowAnimator);
+        if (launcherClosing) {
+            // Delay animation by a frame to avoid jank.
+            Pair<AnimatorSet, Runnable> launcherContentAnimator =
+                    getLauncherContentAnimator(true /* isAppOpening */, startDelay);
+            anim.play(launcherContentAnimator.first);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    launcherContentAnimator.second.run();
+                }
+            });
+        } else {
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mLauncher.addOnResumeCallback(() ->
+                            ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
+                                    mLauncher.getStateManager().getState().getDepth(
+                                            mLauncher)).start());
+                }
+            });
+        }
+    }
+
+    private void composeWidgetLaunchAnimator(
+            @NonNull AnimatorSet anim,
+            @NonNull LauncherAppWidgetHostView v,
+            @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets) {
+        mLauncher.getStateManager().setCurrentAnimation(anim);
+
+        Rect windowTargetBounds = getWindowTargetBounds(appTargets, getRotationChange(appTargets));
+        anim.play(getOpeningWindowAnimatorsForWidget(v, appTargets, wallpaperTargets, nonAppTargets,
+                windowTargetBounds, areAllTargetsTranslucent(appTargets)));
+
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mLauncher.addOnResumeCallback(() ->
+                        ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
+                                mLauncher.getStateManager().getState().getDepth(
+                                        mLauncher)).start());
+            }
+        });
+    }
+
+    /**
+     * Return the window bounds of the opening target.
+     * In multiwindow mode, we need to get the final size of the opening app window target to help
+     * figure out where the floating view should animate to.
+     */
+    private Rect getWindowTargetBounds(@NonNull RemoteAnimationTargetCompat[] appTargets,
+            int rotationChange) {
+        RemoteAnimationTargetCompat target = null;
+        for (RemoteAnimationTargetCompat t : appTargets) {
+            if (t.mode != MODE_OPENING) continue;
+            target = t;
+            break;
+        }
+        if (target == null) return new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
+        final Rect bounds = new Rect(target.screenSpaceBounds);
+        if (target.localBounds != null) {
+            bounds.set(target.localBounds);
+        } else {
+            bounds.offsetTo(target.position.x, target.position.y);
+        }
+        if (rotationChange != 0) {
+            if ((rotationChange % 2) == 1) {
+                // undoing rotation, so our "original" parent size is actually flipped
+                Utilities.rotateBounds(bounds, mDeviceProfile.heightPx, mDeviceProfile.widthPx,
+                        4 - rotationChange);
+            } else {
+                Utilities.rotateBounds(bounds, mDeviceProfile.widthPx, mDeviceProfile.heightPx,
+                        4 - rotationChange);
+            }
+        }
+        return bounds;
+    }
+
+    public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider,
+            CancellationSignal cancellationSignal) {
+        mRemoteAnimationProvider = animationProvider;
+        cancellationSignal.setOnCancelListener(() -> {
+            if (animationProvider == mRemoteAnimationProvider) {
+                mRemoteAnimationProvider = null;
+            }
+        });
+    }
+
+    /**
+     * Content is everything on screen except the background and the floating view (if any).
+     *
+     * @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.
+     */
+    private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
+            int startDelay) {
+        AnimatorSet launcherAnimator = new AnimatorSet();
+        Runnable endListener;
+
+        float[] alphas = isAppOpening
+                ? new float[]{1, 0}
+                : new float[]{0, 1};
+
+        float[] scales = isAppOpening
+                ? new float[]{1, mContentScale}
+                : new float[]{mContentScale, 1};
+
+        if (mLauncher.isInState(ALL_APPS)) {
+            // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
+            final View appsView = mLauncher.getAppsView();
+            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);
+            alpha.setInterpolator(LINEAR);
+            appsView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            alpha.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    appsView.setLayerType(View.LAYER_TYPE_NONE, null);
+                }
+            });
+            ObjectAnimator scale = ObjectAnimator.ofFloat(appsView, SCALE_PROPERTY, scales);
+            scale.setInterpolator(AGGRESSIVE_EASE);
+            scale.setDuration(CONTENT_SCALE_DURATION);
+
+            launcherAnimator.play(alpha);
+            launcherAnimator.play(scale);
+
+            endListener = () -> {
+                appsView.setAlpha(startAlpha);
+                SCALE_PROPERTY.set(appsView, startScale);
+                appsView.setLayerType(View.LAYER_TYPE_NONE, null);
+            };
+        } else if (mLauncher.isInState(OVERVIEW)) {
+            endListener = composeViewContentAnimator(launcherAnimator, alphas, scales);
+        } else {
+            List<View> viewsToAnimate = new ArrayList<>();
+            Workspace workspace = mLauncher.getWorkspace();
+            workspace.forEachVisiblePage(
+                    view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));
+
+            viewsToAnimate.add(mLauncher.getHotseat());
+
+            viewsToAnimate.forEach(view -> {
+                view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+                ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(view, SCALE_PROPERTY, scales)
+                        .setDuration(CONTENT_SCALE_DURATION);
+                scaleAnim.setInterpolator(DEACCEL_1_5);
+                launcherAnimator.play(scaleAnim);
+            });
+
+            final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get();
+            if (scrimEnabled) {
+                int scrimColor = Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
+                int scrimColorTrans = ColorUtils.setAlphaComponent(scrimColor, 0);
+                int[] colors = isAppOpening
+                        ? new int[]{scrimColorTrans, scrimColor}
+                        : new int[]{scrimColor, scrimColorTrans};
+                ScrimView scrimView = mLauncher.getScrimView();
+                if (scrimView.getBackground() instanceof ColorDrawable) {
+                    scrimView.setBackgroundColor(colors[0]);
+
+                    ObjectAnimator scrim = ObjectAnimator.ofArgb(scrimView, VIEW_BACKGROUND_COLOR,
+                            colors);
+                    scrim.setDuration(CONTENT_SCRIM_DURATION);
+                    scrim.setInterpolator(DEACCEL_1_5);
+                    launcherAnimator.play(scrim);
+                }
+            }
+
+            // Pause page indicator animations as they lead to layer trashing.
+            mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
+
+            endListener = () -> {
+                viewsToAnimate.forEach(view -> {
+                    SCALE_PROPERTY.set(view, 1f);
+                    view.setLayerType(View.LAYER_TYPE_NONE, null);
+                });
+                if (scrimEnabled) {
+                    mLauncher.getScrimView().setBackgroundColor(Color.TRANSPARENT);
+                }
+                mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
+            };
+        }
+
+        launcherAnimator.setStartDelay(startDelay);
+        return new Pair<>(launcherAnimator, endListener);
+    }
+
+    /**
+     * Compose recents view alpha and translation Y animation when launcher opens/closes apps.
+     *
+     * @param anim   the animator set to add to
+     * @param alphas the alphas to animate to over time
+     * @param scales the scale values to animator to over time
+     * @return listener to run when the animation ends
+     */
+    protected Runnable composeViewContentAnimator(@NonNull AnimatorSet anim,
+            float[] alphas, float[] scales) {
+        RecentsView overview = mLauncher.getOverviewPanel();
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
+                RecentsView.CONTENT_ALPHA, alphas);
+        alpha.setDuration(CONTENT_ALPHA_DURATION);
+        alpha.setInterpolator(LINEAR);
+        anim.play(alpha);
+        overview.setFreezeViewVisibility(true);
+
+        ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(overview, SCALE_PROPERTY, scales);
+        scaleAnim.setInterpolator(AGGRESSIVE_EASE);
+        scaleAnim.setDuration(CONTENT_SCALE_DURATION);
+        anim.play(scaleAnim);
+
+        return () -> {
+            overview.setFreezeViewVisibility(false);
+            SCALE_PROPERTY.set(overview, 1f);
+            mLauncher.getStateManager().reapplyState();
+        };
+    }
+
+    /**
+     * @return Animator that controls the window of the opening targets from app icons.
+     */
+    private Animator getOpeningWindowAnimators(View v,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
+            Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange) {
+        RectF launcherIconBounds = new RectF();
+        FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
+                !appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
+        Rect crop = new Rect();
+        Matrix matrix = new Matrix();
+
+        RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
+                wallpaperTargets, nonAppTargets, MODE_OPENING);
+        SurfaceTransactionApplier surfaceApplier =
+                new SurfaceTransactionApplier(floatingView);
+        openingTargets.addReleaseCheck(surfaceApplier);
+        RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
+
+        int[] dragLayerBounds = new int[2];
+        mDragLayer.getLocationOnScreen(dragLayerBounds);
+
+        final boolean hasSplashScreen;
+        if (supportsSSplashScreen()) {
+            int taskId = openingTargets.getFirstAppTargetTaskId();
+            Pair<Integer, Integer> defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0);
+            Pair<Integer, Integer> taskParams =
+                    mTaskStartParams.getOrDefault(taskId, defaultParams);
+            mTaskStartParams.remove(taskId);
+            hasSplashScreen = taskParams.first == STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else {
+            hasSplashScreen = false;
+        }
+
+        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);
+        // Set the crop here so we can calculate the corner radius below.
+        crop.set(left, top, right, bottom);
+
+        RectF floatingIconBounds = new RectF();
+        RectF tmpRectF = new RectF();
+        Point tmpPos = new Point();
+
+        AnimatorSet animatorSet = new AnimatorSet();
+        ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+        appAnimator.setDuration(APP_LAUNCH_DURATION);
+        appAnimator.setInterpolator(LINEAR);
+        appAnimator.addListener(floatingView);
+        appAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (v instanceof BubbleTextView) {
+                    ((BubbleTextView) v).setStayPressed(false);
+                }
+                openingTargets.release();
+            }
+        });
+
+        final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
+                ? Math.max(crop.width(), crop.height()) / 2f
+                : 0f;
+        final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
+                ? 0 : getWindowCornerRadius(mLauncher.getResources());
+        final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
+
+        MultiValueUpdateListener listener = new MultiValueUpdateListener() {
+            FloatProp mDx = new FloatProp(0, prop.dX, 0, prop.xDuration, AGGRESSIVE_EASE);
+            FloatProp mDy = new FloatProp(0, prop.dY, 0, prop.yDuration, AGGRESSIVE_EASE);
+
+            FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
+                    prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+            FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,
+                    APP_LAUNCH_ALPHA_START_DELAY, prop.alphaDuration, LINEAR);
+
+            FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0,
+                    RADIUS_DURATION, EXAGGERATED_EASE);
+            FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius, 0,
+                    APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+
+            FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,
+                    0, CROP_DURATION, EXAGGERATED_EASE);
+            FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
+                    0, CROP_DURATION, EXAGGERATED_EASE);
+            FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0,
+                    CROP_DURATION, EXAGGERATED_EASE);
+            FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,
+                    CROP_DURATION, EXAGGERATED_EASE);
+
+            FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
+                    NAV_FADE_OUT_INTERPOLATOR);
+            FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,
+                    ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
+
+            @Override
+            public void onUpdate(float percent, boolean initOnly) {
+                // Calculate the size of the scaled icon.
+                float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;
+                float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;
+
+                int left = (int) (mCropRectCenterX.value - mCropRectWidth.value / 2);
+                int top = (int) (mCropRectCenterY.value - mCropRectHeight.value / 2);
+                int right = (int) (left + mCropRectWidth.value);
+                int bottom = (int) (top + mCropRectHeight.value);
+                crop.set(left, top, right, bottom);
+
+                final int windowCropWidth = crop.width();
+                final int windowCropHeight = crop.height();
+                if (rotationChange != 0) {
+                    Utilities.rotateBounds(crop, mDeviceProfile.widthPx,
+                            mDeviceProfile.heightPx, rotationChange);
+                }
+
+                // Scale the size of the icon to match the size of the window crop.
+                float scaleX = iconWidth / windowCropWidth;
+                float scaleY = iconHeight / windowCropHeight;
+                float scale = Math.min(1f, Math.max(scaleX, scaleY));
+
+                float scaledCropWidth = windowCropWidth * scale;
+                float scaledCropHeight = windowCropHeight * scale;
+                float offsetX = (scaledCropWidth - iconWidth) / 2;
+                float offsetY = (scaledCropHeight - iconHeight) / 2;
+
+                // Calculate the window position to match the icon position.
+                tmpRectF.set(launcherIconBounds);
+                tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]);
+                tmpRectF.offset(mDx.value, mDy.value);
+                Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value);
+                float windowTransX0 = tmpRectF.left - offsetX - crop.left * scale;
+                float windowTransY0 = tmpRectF.top - offsetY - crop.top * scale;
+
+                // Calculate the icon position.
+                floatingIconBounds.set(launcherIconBounds);
+                floatingIconBounds.offset(mDx.value, mDy.value);
+                Utilities.scaleRectFAboutCenter(floatingIconBounds, mIconScaleToFitScreen.value);
+                floatingIconBounds.left -= offsetX;
+                floatingIconBounds.top -= offsetY;
+                floatingIconBounds.right += offsetX;
+                floatingIconBounds.bottom += offsetY;
+
+                if (initOnly) {
+                    // For the init pass, we want full alpha since the window is not yet ready.
+                    floatingView.update(1f, 255, floatingIconBounds, percent, 0f,
+                            mWindowRadius.value * scale, true /* isOpening */);
+                    return;
+                }
+
+                ArrayList<SurfaceParams> params = new ArrayList<>();
+                for (int i = appTargets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = appTargets[i];
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+
+                    if (target.mode == MODE_OPENING) {
+                        matrix.setScale(scale, scale);
+                        if (rotationChange == 1) {
+                            matrix.postTranslate(windowTransY0,
+                                    mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth));
+                        } else if (rotationChange == 2) {
+                            matrix.postTranslate(
+                                    mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth),
+                                    mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight));
+                        } else if (rotationChange == 3) {
+                            matrix.postTranslate(
+                                    mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight),
+                                    windowTransX0);
+                        } else {
+                            matrix.postTranslate(windowTransX0, windowTransY0);
+                        }
+
+                        floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
+                                mWindowRadius.value * scale, true /* isOpening */);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(1f - mIconAlpha.value)
+                                .withCornerRadius(mWindowRadius.value)
+                                .withShadowRadius(mShadowRadius.value);
+                    } else if (target.mode == MODE_CLOSING) {
+                        if (target.localBounds != null) {
+                            final Rect localBounds = target.localBounds;
+                            tmpPos.set(target.localBounds.left, target.localBounds.top);
+                        } else {
+                            tmpPos.set(target.position.x, target.position.y);
+                        }
+                        final Rect crop = new Rect(target.screenSpaceBounds);
+                        crop.offsetTo(0, 0);
+
+                        if ((rotationChange % 2) == 1) {
+                            int tmp = crop.right;
+                            crop.right = crop.bottom;
+                            crop.bottom = tmp;
+                            tmp = tmpPos.x;
+                            tmpPos.x = tmpPos.y;
+                            tmpPos.y = tmp;
+                        }
+                        matrix.setTranslate(tmpPos.x, tmpPos.y);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(1f);
+                    }
+                    params.add(builder.build());
+                }
+
+                if (navBarTarget != null) {
+                    final SurfaceParams.Builder navBuilder =
+                            new SurfaceParams.Builder(navBarTarget.leash);
+                    if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
+                        matrix.setScale(scale, scale);
+                        matrix.postTranslate(windowTransX0, windowTransY0);
+                        navBuilder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(mNavFadeIn.value);
+                    } else {
+                        navBuilder.withAlpha(mNavFadeOut.value);
+                    }
+                    params.add(navBuilder.build());
+                }
+
+                surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+            }
+        };
+        appAnimator.addUpdateListener(listener);
+        // Since we added a start delay, call update here to init the FloatingIconView properly.
+        listener.onUpdate(0, true /* initOnly */);
+
+        animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets));
+        return animatorSet;
+    }
+
+    private Animator getOpeningWindowAnimatorsForWidget(LauncherAppWidgetHostView v,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets, Rect windowTargetBounds,
+            boolean appTargetsAreTranslucent) {
+        final RectF widgetBackgroundBounds = new RectF();
+        final Rect appWindowCrop = new Rect();
+        final Matrix matrix = new Matrix();
+        RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
+                wallpaperTargets, nonAppTargets, MODE_OPENING);
+
+        RemoteAnimationTargetCompat openingTarget = openingTargets.getFirstAppTarget();
+        int fallbackBackgroundColor = 0;
+        if (openingTarget != null && supportsSSplashScreen()) {
+            fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId)
+                    ? mTaskStartParams.get(openingTarget.taskId).second : 0;
+            mTaskStartParams.remove(openingTarget.taskId);
+        }
+        if (fallbackBackgroundColor == 0) {
+            fallbackBackgroundColor =
+                    FloatingWidgetView.getDefaultBackgroundColor(mLauncher, openingTarget);
+        }
+
+        final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
+                ? 0 : getWindowCornerRadius(mLauncher.getResources());
+        final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher,
+                v, widgetBackgroundBounds,
+                new Size(windowTargetBounds.width(), windowTargetBounds.height()),
+                finalWindowRadius, appTargetsAreTranslucent, fallbackBackgroundColor);
+        final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
+                ? floatingView.getInitialCornerRadius() : 0;
+
+        SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView);
+        openingTargets.addReleaseCheck(surfaceApplier);
+
+        RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
+
+        AnimatorSet animatorSet = new AnimatorSet();
+        ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+        appAnimator.setDuration(APP_LAUNCH_DURATION);
+        appAnimator.setInterpolator(LINEAR);
+        appAnimator.addListener(floatingView);
+        appAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                openingTargets.release();
+            }
+        });
+        floatingView.setFastFinishRunnable(animatorSet::end);
+
+        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+            float mAppWindowScale = 1;
+            final FloatProp mWidgetForegroundAlpha = new FloatProp(1 /* start */,
+                    0 /* end */, 0 /* delay */,
+                    WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* duration */, LINEAR);
+            final FloatProp mWidgetFallbackBackgroundAlpha = new FloatProp(0 /* start */,
+                    1 /* end */, 0 /* delay */, 75 /* duration */, LINEAR);
+            final FloatProp mPreviewAlpha = new FloatProp(0 /* start */, 1 /* end */,
+                    WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* delay */,
+                    WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* duration */, LINEAR);
+            final FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius,
+                    0 /* start */, RADIUS_DURATION, LINEAR);
+            final FloatProp mCornerRadiusProgress = new FloatProp(0, 1, 0, RADIUS_DURATION, LINEAR);
+
+            // Window & widget background positioning bounds
+            final FloatProp mDx = new FloatProp(widgetBackgroundBounds.centerX(),
+                    windowTargetBounds.centerX(), 0 /* delay */, APP_LAUNCH_CURVED_DURATION,
+                    EXAGGERATED_EASE);
+            final FloatProp mDy = new FloatProp(widgetBackgroundBounds.centerY(),
+                    windowTargetBounds.centerY(), 0 /* delay */, APP_LAUNCH_DURATION,
+                    EXAGGERATED_EASE);
+            final FloatProp mWidth = new FloatProp(widgetBackgroundBounds.width(),
+                    windowTargetBounds.width(), 0 /* delay */, APP_LAUNCH_DURATION,
+                    EXAGGERATED_EASE);
+            final FloatProp mHeight = new FloatProp(widgetBackgroundBounds.height(),
+                    windowTargetBounds.height(), 0 /* delay */, APP_LAUNCH_DURATION,
+                    EXAGGERATED_EASE);
+
+            final FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
+                    NAV_FADE_OUT_INTERPOLATOR);
+            final FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,
+                    ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
+
+            @Override
+            public void onUpdate(float percent, boolean initOnly) {
+                widgetBackgroundBounds.set(mDx.value - mWidth.value / 2f,
+                        mDy.value - mHeight.value / 2f, mDx.value + mWidth.value / 2f,
+                        mDy.value + mHeight.value / 2f);
+                // Set app window scaling factor to match widget background width
+                mAppWindowScale = widgetBackgroundBounds.width() / windowTargetBounds.width();
+                // Crop scaled app window to match widget
+                appWindowCrop.set(0 /* left */, 0 /* top */,
+                        Math.round(windowTargetBounds.width()) /* right */,
+                        Math.round(widgetBackgroundBounds.height() / mAppWindowScale) /* bottom */);
+                matrix.setTranslate(widgetBackgroundBounds.left, widgetBackgroundBounds.top);
+                matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
+                        widgetBackgroundBounds.top);
+
+                ArrayList<SurfaceParams> params = new ArrayList<>();
+                float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
+                for (int i = appTargets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = appTargets[i];
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+                    if (target.mode == MODE_OPENING) {
+                        floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
+                                mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
+                                mCornerRadiusProgress.value);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(appWindowCrop)
+                                .withAlpha(mPreviewAlpha.value)
+                                .withCornerRadius(mWindowRadius.value / mAppWindowScale);
+                    }
+                    params.add(builder.build());
+                }
+
+                if (navBarTarget != null) {
+                    final SurfaceParams.Builder navBuilder =
+                            new SurfaceParams.Builder(navBarTarget.leash);
+                    if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
+                        navBuilder.withMatrix(matrix)
+                                .withWindowCrop(appWindowCrop)
+                                .withAlpha(mNavFadeIn.value);
+                    } else {
+                        navBuilder.withAlpha(mNavFadeOut.value);
+                    }
+                    params.add(navBuilder.build());
+                }
+
+                surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
+            }
+        });
+
+        animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets));
+        return animatorSet;
+    }
+
+    private ObjectAnimator getBackgroundAnimator(RemoteAnimationTargetCompat[] appTargets) {
+        // 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;
+        DepthController depthController = mLauncher.getDepthController();
+        ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
+                BACKGROUND_APP.getDepth(mLauncher))
+                .setDuration(APP_LAUNCH_DURATION);
+        if (allowBlurringLauncher) {
+            final SurfaceControl dimLayer;
+            if (BlurUtils.supportsBlursOnWindows()) {
+                // Create a temporary effect layer, that lives on top of launcher, so we can apply
+                // the blur to it. The EffectLayer will be fullscreen, which will help with caching
+                // optimizations on the SurfaceFlinger side:
+                // - Results would be able to be cached as a texture
+                // - There won't be texture allocation overhead, because EffectLayers don't have
+                //   buffers
+                ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
+                SurfaceControl parent = viewRootImpl != null
+                        ? viewRootImpl.getSurfaceControl()
+                        : null;
+                dimLayer = new SurfaceControl.Builder()
+                        .setName("Blur layer")
+                        .setParent(parent)
+                        .setOpaque(false)
+                        .setHidden(false)
+                        .setEffectLayer()
+                        .build();
+            } else {
+                dimLayer = null;
+            }
+
+            depthController.setSurface(dimLayer);
+            backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    depthController.setIsInLaunchTransition(true);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    depthController.setIsInLaunchTransition(false);
+                    depthController.setSurface(null);
+                    if (dimLayer != null) {
+                        new SurfaceControl.Transaction()
+                                .remove(dimLayer)
+                                .apply();
+                    }
+                }
+            });
+        }
+        return backgroundRadiusAnim;
+    }
+
+    /**
+     * Registers remote animations used when closing apps to home screen.
+     */
+    public void registerRemoteAnimations() {
+        if (SEPARATE_RECENTS_ACTIVITY.get()) {
+            return;
+        }
+        if (hasControlRemoteAppTransitionPermission()) {
+            mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
+
+            RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
+            definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
+                    WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
+                    new RemoteAnimationAdapterCompat(
+                            new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
+                                    false /* startAtFrontOfQueue */),
+                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+
+            if (KEYGUARD_ANIMATION.get()) {
+                mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
+                definition.addRemoteAnimation(
+                        WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+                        new RemoteAnimationAdapterCompat(
+                                new LauncherAnimationRunner(
+                                        mHandler, mKeyguardGoingAwayRunner,
+                                        true /* startAtFrontOfQueue */),
+                                CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+            }
+
+            new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
+        }
+    }
+
+    /**
+     * Registers remote animations used when closing apps to home screen.
+     */
+    public void registerRemoteTransitions() {
+        if (SEPARATE_RECENTS_ACTIVITY.get()) {
+            return;
+        }
+        if (hasControlRemoteAppTransitionPermission()) {
+            mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
+            mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
+                    new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
+                            false /* startAtFrontOfQueue */));
+            mLauncherOpenTransition.addHomeOpenCheck();
+            SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
+        }
+    }
+
+    public void onActivityDestroyed() {
+        unregisterRemoteAnimations();
+        unregisterRemoteTransitions();
+        mStartingWindowListener.setTransitionManager(null);
+        SystemUiProxy.INSTANCE.getNoCreate().setStartingWindowListener(null);
+    }
+
+    private void unregisterRemoteAnimations() {
+        if (SEPARATE_RECENTS_ACTIVITY.get()) {
+            return;
+        }
+        if (hasControlRemoteAppTransitionPermission()) {
+            new ActivityCompat(mLauncher).unregisterRemoteAnimations();
+
+            // Also clear strong references to the runners registered with the remote animation
+            // definition so we don't have to wait for the system gc
+            mWallpaperOpenRunner = null;
+            mAppLaunchRunner = null;
+            mKeyguardGoingAwayRunner = null;
+        }
+    }
+
+    private void unregisterRemoteTransitions() {
+        if (SEPARATE_RECENTS_ACTIVITY.get()) {
+            return;
+        }
+        if (hasControlRemoteAppTransitionPermission()) {
+            if (mLauncherOpenTransition == null) return;
+            SystemUiProxy.INSTANCE.getNoCreate().unregisterRemoteTransition(
+                    mLauncherOpenTransition);
+            mLauncherOpenTransition = null;
+            mWallpaperOpenTransitionRunner = null;
+        }
+    }
+
+    private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
+        return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode);
+    }
+
+    /**
+     * @return Runner that plays when user goes to Launcher
+     * ie. pressing home, swiping up from nav bar.
+     */
+    RemoteAnimationFactory createWallpaperOpenRunner(boolean fromUnlock) {
+        return new WallpaperOpenLauncherAnimationRunner(mHandler, fromUnlock);
+    }
+
+    /**
+     * Animator that controls the transformations of the windows when unlocking the device.
+     */
+    private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets) {
+        SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
+        ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
+        unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
+        float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 :
+                QuickStepContract.getWindowCornerRadius(mLauncher.getResources());
+        unlockAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                SurfaceParams[] params = new SurfaceParams[appTargets.length];
+                for (int i = appTargets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = appTargets[i];
+                    params[i] = new SurfaceParams.Builder(target.leash)
+                            .withAlpha(1f)
+                            .withWindowCrop(target.screenSpaceBounds)
+                            .withCornerRadius(cornerRadius)
+                            .build();
+                }
+                surfaceApplier.scheduleApply(params);
+            }
+        });
+        return unlockAnimator;
+    }
+
+    private static int getRotationChange(RemoteAnimationTargetCompat[] appTargets) {
+        int rotationChange = 0;
+        for (RemoteAnimationTargetCompat target : appTargets) {
+            if (Math.abs(target.rotationChange) > Math.abs(rotationChange)) {
+                rotationChange = target.rotationChange;
+            }
+        }
+        return rotationChange;
+    }
+
+    /**
+     * Animator that controls the transformations of the windows the targets that are closing.
+     */
+    private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets) {
+        final int rotationChange = getRotationChange(appTargets);
+        SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
+        Matrix matrix = new Matrix();
+        Point tmpPos = new Point();
+        Rect tmpRect = new Rect();
+        ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
+        int duration = CLOSING_TRANSITION_DURATION_MS;
+        float windowCornerRadius = mDeviceProfile.isMultiWindowMode
+                ? 0 : getWindowCornerRadius(mLauncher.getResources());
+        float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius;
+        closingAnimator.setDuration(duration);
+        closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
+            FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
+            FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
+            FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
+            FloatProp mShadowRadius = new FloatProp(startShadowRadius, 0, 0, duration,
+                    DEACCEL_1_7);
+
+            @Override
+            public void onUpdate(float percent, boolean initOnly) {
+                SurfaceParams[] params = new SurfaceParams[appTargets.length];
+                for (int i = appTargets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = appTargets[i];
+                    SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+
+                    if (target.localBounds != null) {
+                        tmpPos.set(target.localBounds.left, target.localBounds.top);
+                    } else {
+                        tmpPos.set(target.position.x, target.position.y);
+                    }
+
+                    final Rect crop = new Rect(target.screenSpaceBounds);
+                    crop.offsetTo(0, 0);
+                    if (target.mode == MODE_CLOSING) {
+                        tmpRect.set(target.screenSpaceBounds);
+                        if ((rotationChange % 2) != 0) {
+                            final int right = crop.right;
+                            crop.right = crop.bottom;
+                            crop.bottom = right;
+                        }
+                        matrix.setScale(mScale.value, mScale.value,
+                                tmpRect.centerX(),
+                                tmpRect.centerY());
+                        matrix.postTranslate(0, mDy.value);
+                        matrix.postTranslate(tmpPos.x, tmpPos.y);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(mAlpha.value)
+                                .withCornerRadius(windowCornerRadius)
+                                .withShadowRadius(mShadowRadius.value);
+                    } else if (target.mode == MODE_OPENING) {
+                        matrix.setTranslate(tmpPos.x, tmpPos.y);
+                        builder.withMatrix(matrix)
+                                .withWindowCrop(crop)
+                                .withAlpha(1f);
+                    }
+                    params[i] = builder.build();
+                }
+                surfaceApplier.scheduleApply(params);
+            }
+        });
+
+        return closingAnimator;
+    }
+
+    private boolean supportsSSplashScreen() {
+        return hasControlRemoteAppTransitionPermission()
+                && Utilities.ATLEAST_S
+                && ENABLE_SHELL_STARTING_SURFACE;
+    }
+
+    /**
+     * Returns true if we have permission to control remote app transisions
+     */
+    public boolean hasControlRemoteAppTransitionPermission() {
+        return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private void addCujInstrumentation(Animator anim, int cuj) {
+        anim.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mDragLayer.getViewTreeObserver().addOnDrawListener(
+                        new ViewTreeObserver.OnDrawListener() {
+                            boolean mHandled = false;
+
+                            @Override
+                            public void onDraw() {
+                                if (mHandled) {
+                                    return;
+                                }
+                                mHandled = true;
+
+                                InteractionJankMonitorWrapper.begin(mDragLayer, cuj);
+
+                                mDragLayer.post(() ->
+                                        mDragLayer.getViewTreeObserver().removeOnDrawListener(
+                                                this));
+                            }
+                        });
+                super.onAnimationStart(animation);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                InteractionJankMonitorWrapper.cancel(cuj);
+            }
+
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                InteractionJankMonitorWrapper.end(cuj);
+            }
+        });
+    }
+
+    /**
+     * Remote animation runner for animation from the app to Launcher, including recents.
+     */
+    protected class WallpaperOpenLauncherAnimationRunner implements RemoteAnimationFactory {
+
+        private final Handler mHandler;
+        private final boolean mFromUnlock;
+
+        public WallpaperOpenLauncherAnimationRunner(Handler handler, boolean fromUnlock) {
+            mHandler = handler;
+            mFromUnlock = fromUnlock;
+        }
+
+        @Override
+        public void onCreateAnimation(int transit,
+                RemoteAnimationTargetCompat[] appTargets,
+                RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets,
+                LauncherAnimationRunner.AnimationResult result) {
+            if (mLauncher.isDestroyed()) {
+                AnimatorSet anim = new AnimatorSet();
+                anim.play(getClosingWindowAnimators(appTargets, wallpaperTargets));
+                result.setAnimation(anim, mLauncher.getApplicationContext());
+                return;
+            }
+
+            if (!mLauncher.hasBeenResumed()) {
+                // If launcher is not resumed, wait until new async-frame after resume
+                mLauncher.addOnResumeCallback(() ->
+                        postAsyncCallback(mHandler, () ->
+                                onCreateAnimation(transit, appTargets, wallpaperTargets,
+                                        nonAppTargets, result)));
+                return;
+            }
+
+            if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
+                mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
+                mLauncher.getStateManager().moveToRestState();
+            }
+
+            AnimatorSet anim = null;
+            RemoteAnimationProvider provider = mRemoteAnimationProvider;
+            if (provider != null) {
+                anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
+            }
+
+            if (anim == null) {
+                anim = new AnimatorSet();
+                anim.play(mFromUnlock
+                        ? getUnlockWindowAnimator(appTargets, wallpaperTargets)
+                        : getClosingWindowAnimators(appTargets, wallpaperTargets));
+
+                // 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
+                // contents of launcher unless we know that we are animating home as a result
+                // of the home button press with quickstep, which will result in launcher being
+                // started on touch down, prior to the animation home (and won't be in the
+                // 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()) {
+                    addCujInstrumentation(
+                            anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+                    // Only register the content animation for cancellation when state changes
+                    mLauncher.getStateManager().setCurrentAnimation(anim);
+
+                    if (mLauncher.isInState(LauncherState.ALL_APPS)) {
+                        Pair<AnimatorSet, Runnable> contentAnimator =
+                                getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY);
+                        anim.play(contentAnimator.first);
+                        anim.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                contentAnimator.second.run();
+                            }
+                        });
+                    } else {
+                        anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
+                    }
+                }
+            }
+
+            mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
+            result.setAnimation(anim, mLauncher);
+        }
+    }
+
+    /**
+     * Remote animation runner for animation to launch an app.
+     */
+    private class AppLaunchAnimationRunner implements RemoteAnimationFactory {
+
+        private final View mV;
+        private final RunnableList mOnEndCallback;
+
+        AppLaunchAnimationRunner(View v, RunnableList onEndCallback) {
+            mV = v;
+            mOnEndCallback = onEndCallback;
+        }
+
+        @Override
+        public void onCreateAnimation(int transit,
+                RemoteAnimationTargetCompat[] appTargets,
+                RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets,
+                LauncherAnimationRunner.AnimationResult result) {
+            AnimatorSet anim = new AnimatorSet();
+            boolean launcherClosing =
+                    launcherIsATargetWithMode(appTargets, MODE_CLOSING);
+
+            final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;
+            final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
+            final boolean skipFirstFrame;
+            if (launchingFromWidget) {
+                composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
+                        wallpaperTargets, nonAppTargets);
+                addCujInstrumentation(
+                        anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET);
+                skipFirstFrame = true;
+            } else if (launchingFromRecents) {
+                composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
+                        launcherClosing);
+                addCujInstrumentation(
+                        anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);
+                skipFirstFrame = true;
+            } else {
+                composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
+                        launcherClosing);
+                addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_ICON);
+                skipFirstFrame = false;
+            }
+
+            if (launcherClosing) {
+                anim.addListener(mForceInvisibleListener);
+            }
+
+            result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
+                    skipFirstFrame);
+        }
+
+        @Override
+        public void onAnimationCancelled() {
+            mOnEndCallback.executeAllAndDestroy();
+        }
+    }
+
+    /**
+     * Class that holds all the variables for the app open animation.
+     */
+    static class AnimOpenProperties {
+
+        public final int cropCenterXStart;
+        public final int cropCenterYStart;
+        public final int cropWidthStart;
+        public final int cropHeightStart;
+
+        public final int cropCenterXEnd;
+        public final int cropCenterYEnd;
+        public final int cropWidthEnd;
+        public final int cropHeightEnd;
+
+        public final float dX;
+        public final float dY;
+
+        public final long xDuration;
+        public final long yDuration;
+        public final long alphaDuration;
+
+        public final float initialAppIconScale;
+        public final float finalAppIconScale;
+
+        public final float iconAlphaStart;
+
+        AnimOpenProperties(Resources r, DeviceProfile dp, Rect windowTargetBounds,
+                RectF launcherIconBounds, View view, int dragLayerLeft, int dragLayerTop,
+                boolean hasSplashScreen, boolean hasDifferentAppIcon) {
+            // Scale the app icon to take up the entire screen. This simplifies the math when
+            // animating the app window position / scale.
+            float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
+            float maxScaleX = smallestSize / launcherIconBounds.width();
+            float maxScaleY = smallestSize / launcherIconBounds.height();
+            float iconStartScale = 1f;
+            if (view instanceof BubbleTextView && !(view.getParent() instanceof DeepShortcutView)) {
+                Drawable dr = ((BubbleTextView) view).getIcon();
+                if (dr instanceof FastBitmapDrawable) {
+                    iconStartScale = ((FastBitmapDrawable) dr).getAnimatedScale();
+                }
+            }
+
+            initialAppIconScale = iconStartScale;
+            finalAppIconScale = Math.max(maxScaleX, maxScaleY);
+
+            // Animate the app icon to the center of the window bounds in screen coordinates.
+            float centerX = windowTargetBounds.centerX() - dragLayerLeft;
+            float centerY = windowTargetBounds.centerY() - dragLayerTop;
+
+            dX = centerX - launcherIconBounds.centerX();
+            dY = centerY - launcherIconBounds.centerY();
+
+            boolean useUpwardAnimation = launcherIconBounds.top > centerY
+                    || Math.abs(dY) < dp.cellHeightPx;
+            xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
+                    : APP_LAUNCH_DOWN_DURATION;
+            yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
+                    : APP_LAUNCH_DOWN_CURVED_DURATION;
+            alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
+                    : APP_LAUNCH_ALPHA_DOWN_DURATION;
+            iconAlphaStart = hasSplashScreen && !hasDifferentAppIcon ? 0 : 1f;
+
+            final int windowIconSize = ResourceUtils.getDimenByName("starting_surface_icon_size",
+                    r, 108);
+
+            cropCenterXStart = windowTargetBounds.centerX();
+            cropCenterYStart = windowTargetBounds.centerY();
+
+            cropWidthStart = windowIconSize;
+            cropHeightStart = windowIconSize;
+
+            cropWidthEnd = windowTargetBounds.width();
+            cropHeightEnd = windowTargetBounds.height();
+
+            cropCenterXEnd = windowTargetBounds.centerX();
+            cropCenterYEnd = windowTargetBounds.centerY();
+        }
+    }
+
+    private static class StartingWindowListener extends IStartingWindowListener.Stub {
+        private QuickstepTransitionManager mTransitionManager;
+
+        public void setTransitionManager(QuickstepTransitionManager transitionManager) {
+            mTransitionManager = transitionManager;
+        }
+
+        @Override
+        public void onTaskLaunching(int taskId, int supportedType, int color) {
+            mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
deleted file mode 100644
index da2aee4..0000000
--- a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
+++ /dev/null
@@ -1,32 +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;
-
-import android.os.Handler;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Used with WrappedLauncherAnimationRunner as an interface for the runner to call back to the
- * implementation.
- */
-public interface WrappedAnimationRunnerImpl {
-    Handler getHandler();
-    void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            LauncherAnimationRunner.AnimationResult result);
-}
diff --git a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
deleted file mode 100644
index 1753b62..0000000
--- a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
+++ /dev/null
@@ -1,56 +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;
-
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-import java.lang.ref.WeakReference;
-
-/**
- * This class is needed to wrap any animation runner that is a part of the
- * RemoteAnimationDefinition:
- * - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
- *   created, which in turn registers a new definition
- * - When the definition is registered, window manager retains a strong binder reference to the
- *   runner passed in
- * - If the Launcher activity is recreated, the new definition registered will replace the old
- *   reference in the system's activity record, but until the system server is GC'd, the binder
- *   reference will still exist, which references the runner in the Launcher process, which
- *   references the (old) Launcher activity through this class
- *
- * Instead we make the runner provided to the definition static only holding a weak reference to
- * the runner implementation.  When this animation manager is destroyed, we remove the Launcher
- * reference to the runner, leaving only the weak ref from the runner.
- */
-public class WrappedLauncherAnimationRunner<R extends WrappedAnimationRunnerImpl>
-        extends LauncherAnimationRunner {
-    private WeakReference<R> mImpl;
-
-    public WrappedLauncherAnimationRunner(R animationRunnerImpl, boolean startAtFrontOfQueue) {
-        super(animationRunnerImpl.getHandler(), startAtFrontOfQueue);
-        mImpl = new WeakReference<>(animationRunnerImpl);
-    }
-
-    @Override
-    public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
-        R animationRunnerImpl = mImpl.get();
-        if (animationRunnerImpl != null) {
-            animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
similarity index 90%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
rename to quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 914d9e9..63a569a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.appprediction;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 
 import android.annotation.TargetApi;
@@ -30,7 +29,6 @@
 import android.text.TextPaint;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import androidx.annotation.ColorInt;
 import androidx.core.content.ContextCompat;
@@ -41,7 +39,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.Themes;
 
@@ -77,6 +74,8 @@
 
     private boolean mIsScrolledOut = false;
 
+    private final int[] mDividerSize;
+
     public AppsDividerView(Context context) {
         this(context, null);
     }
@@ -90,7 +89,10 @@
         mLauncher = Launcher.getLauncher(context);
 
         boolean isMainColorDark = Themes.getAttrBoolean(context, R.attr.isMainColorDark);
-        mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
+        mDividerSize = new int[]{
+                getResources().getDimensionPixelSize(R.dimen.all_apps_divider_width),
+                getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height)
+        };
 
         mStrokeColor = ContextCompat.getColor(context, isMainColorDark
                 ? R.color.all_apps_prediction_row_separator_dark
@@ -134,7 +136,7 @@
                 if (row == this) {
                     break;
                 } else if (row.shouldDraw()) {
-                    sectionCount ++;
+                    sectionCount++;
                 }
             }
 
@@ -189,11 +191,11 @@
     @Override
     protected void onDraw(Canvas canvas) {
         if (mDividerType == DividerType.LINE) {
-            int side = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
-            int y = getHeight() - (getPaddingBottom() / 2);
-            int x1 = getPaddingLeft() + side;
-            int x2 = getWidth() - getPaddingRight() - side;
-            canvas.drawLine(x1, y, x2, y, mPaint);
+            int l = (getWidth() - getPaddingLeft() - mDividerSize[0]) / 2;
+            int t = getHeight() - (getPaddingBottom() / 2);
+            int radius = mDividerSize[1];
+            canvas.drawRoundRect(l, t, l + mDividerSize[0], t + mDividerSize[1], radius, radius,
+                    mPaint);
         } else if (mDividerType == DividerType.ALL_APPS_LABEL) {
             Layout textLayout = getAllAppsLabelLayout();
             int x = getWidth() / 2 - textLayout.getWidth() / 2;
@@ -286,13 +288,6 @@
     }
 
     @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
-        // Don't use setViewAlpha as we want to control the visibility ourselves.
-        setter.setFloat(this, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, allAppsFade);
-    }
-
-    @Override
     public void setVerticalScroll(int scroll, boolean isScrolledOut) {
         setTranslationY(scroll);
         mIsScrolledOut = isScrolledOut;
@@ -303,4 +298,9 @@
     public Class<AppsDividerView> getTypeClass() {
         return AppsDividerView.class;
     }
+
+    @Override
+    public View getFocusedChild() {
+        return null;
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
rename to quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
new file mode 100644
index 0000000..6afbf9a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appprediction;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.FloatingHeaderRow;
+import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.keyboard.FocusIndicatorHelper;
+import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.touch.ItemLongClickListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@TargetApi(Build.VERSION_CODES.P)
+public class PredictionRowView extends LinearLayout implements
+        OnDeviceProfileChangeListener, FloatingHeaderRow {
+
+    private final Launcher mLauncher;
+    private int mNumPredictedAppsPerRow;
+
+    // Helper to drawing the focus indicator.
+    private final FocusIndicatorHelper mFocusHelper;
+
+    // The set of predicted apps resolved from the component names and the current set of apps
+    private final List<WorkspaceItemInfo> mPredictedApps = new ArrayList<>();
+
+    private FloatingHeaderView mParent;
+    private boolean mScrolledOut;
+
+    private boolean mPredictionsEnabled = false;
+
+    @Nullable
+    private List<ItemInfo> mPendingPredictedItems;
+
+    public PredictionRowView(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public PredictionRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        setOrientation(LinearLayout.HORIZONTAL);
+
+        mFocusHelper = new SimpleFocusIndicatorHelper(this);
+        mLauncher = Launcher.getLauncher(context);
+        mLauncher.addOnDeviceProfileChangeListener(this);
+        mNumPredictedAppsPerRow = mLauncher.getDeviceProfile().numShownAllAppsColumns;
+        updateVisibility();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+    }
+
+    public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
+        mParent = parent;
+    }
+
+    private void updateVisibility() {
+        setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
+        if (mLauncher.getAppsView() != null) {
+            if (mPredictionsEnabled) {
+                mLauncher.getAppsView().getAppsStore().registerIconContainer(this);
+            } else {
+                mLauncher.getAppsView().getAppsStore().unregisterIconContainer(this);
+            }
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getExpectedHeight(),
+                MeasureSpec.EXACTLY));
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        mFocusHelper.draw(canvas);
+        super.dispatchDraw(canvas);
+    }
+
+    @Override
+    public int getExpectedHeight() {
+        return getVisibility() == GONE ? 0 :
+                Launcher.getLauncher(getContext()).getDeviceProfile().allAppsCellHeightPx
+                        + getPaddingTop() + getPaddingBottom();
+    }
+
+    @Override
+    public boolean shouldDraw() {
+        return getVisibility() != GONE;
+    }
+
+    @Override
+    public boolean hasVisibleContent() {
+        return mPredictionsEnabled;
+    }
+
+    @Override
+    public boolean isVisible() {
+        return getVisibility() == VISIBLE;
+    }
+
+    /**
+     * Returns the predicted apps.
+     */
+    public List<ItemInfoWithIcon> getPredictedApps() {
+        return new ArrayList<>(mPredictedApps);
+    }
+
+    /**
+     * Sets the current set of predicted apps.
+     *
+     * This can be called before we get the full set of applications, we should merge the results
+     * only in onPredictionsUpdated() which is idempotent.
+     *
+     * If the number of predicted apps is the same as the previous list of predicted apps,
+     * we can optimize by swapping them in place.
+     */
+    public void setPredictedApps(List<ItemInfo> items) {
+        if (!FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get()
+                && !mLauncher.isWorkspaceLoading()
+                && isShown()
+                && getWindowVisibility() == View.VISIBLE) {
+            mPendingPredictedItems = items;
+            return;
+        }
+
+        applyPredictedApps(items);
+    }
+
+    private void applyPredictedApps(List<ItemInfo> items) {
+        mPendingPredictedItems = null;
+        mPredictedApps.clear();
+        items.stream()
+                .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
+                .map(itemInfo -> (WorkspaceItemInfo) itemInfo)
+                .forEach(mPredictedApps::add);
+        applyPredictionApps();
+    }
+
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile dp) {
+        mNumPredictedAppsPerRow = dp.numShownAllAppsColumns;
+        removeAllViews();
+        applyPredictionApps();
+    }
+
+    private void applyPredictionApps() {
+        if (getChildCount() != mNumPredictedAppsPerRow) {
+            while (getChildCount() > mNumPredictedAppsPerRow) {
+                removeViewAt(0);
+            }
+            LayoutInflater inflater = mLauncher.getAppsView().getLayoutInflater();
+            while (getChildCount() < mNumPredictedAppsPerRow) {
+                BubbleTextView icon = (BubbleTextView) inflater.inflate(
+                        R.layout.all_apps_icon, this, false);
+                icon.setOnClickListener(ItemClickHandler.INSTANCE);
+                icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
+                icon.setLongPressTimeoutFactor(1f);
+                icon.setOnFocusChangeListener(mFocusHelper);
+
+                LayoutParams lp = (LayoutParams) icon.getLayoutParams();
+                // Ensure the all apps icon height matches the workspace icons in portrait mode.
+                lp.height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
+                lp.width = 0;
+                lp.weight = 1;
+                addView(icon);
+            }
+        }
+
+        int predictionCount = mPredictedApps.size();
+
+        for (int i = 0; i < getChildCount(); i++) {
+            BubbleTextView icon = (BubbleTextView) getChildAt(i);
+            icon.reset();
+            if (predictionCount > i) {
+                icon.setVisibility(View.VISIBLE);
+                icon.applyFromWorkspaceItem(mPredictedApps.get(i));
+            } else {
+                icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
+            }
+        }
+
+        boolean predictionsEnabled = predictionCount > 0;
+        if (predictionsEnabled != mPredictionsEnabled) {
+            mPredictionsEnabled = predictionsEnabled;
+            mLauncher.reapplyUi(false /* cancelCurrentAnimation */);
+            updateVisibility();
+        }
+        mParent.onHeightUpdated();
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+
+    @Override
+    public void setVerticalScroll(int scroll, boolean isScrolledOut) {
+        mScrolledOut = isScrolledOut;
+        if (!isScrolledOut) {
+            setTranslationY(scroll);
+        }
+        setAlpha(mScrolledOut ? 0 : 1);
+        if (getVisibility() != GONE) {
+            AlphaUpdateListener.updateVisibility(this);
+        }
+    }
+
+    @Override
+    public void setInsets(Rect insets, DeviceProfile grid) {
+        int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+                + grid.cellLayoutPaddingLeftRightPx;
+        setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
+    }
+
+    @Override
+    public Class<PredictionRowView> getTypeClass() {
+        return PredictionRowView.class;
+    }
+
+    @Override
+    public View getFocusedChild() {
+        return getChildAt(0);
+    }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+
+        if (mPendingPredictedItems != null && !isVisible) {
+            applyPredictedApps(mPendingPredictedItems);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
similarity index 91%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
index c968de9..3a7d821 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 
 import com.android.launcher3.BaseActivity;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ActivityTracker;
 
@@ -29,7 +30,6 @@
  */
 public class HotseatEduActivity extends Activity {
 
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -38,7 +38,8 @@
                 .addCategory(Intent.CATEGORY_HOME)
                 .setPackage(getPackageName())
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        new HotseatActivityTracker<>().addToIntent(homeIntent);
+
+        Launcher.ACTIVITY_TRACKER.registerCallback(new HotseatActivityTracker());
         startActivity(homeIntent);
         finish();
     }
@@ -49,7 +50,7 @@
         @Override
         public boolean init(BaseActivity activity, boolean alreadyOnHome) {
             QuickstepLauncher launcher = (QuickstepLauncher) activity;
-            if (launcher != null && launcher.getHotseatPredictionController() != null) {
+            if (launcher != null) {
                 launcher.getHotseatPredictionController().showEdu();
             }
             return false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
similarity index 93%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 4f95254..a6844e4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -47,35 +47,27 @@
  */
 public class HotseatEduController {
 
-    public static final String HOTSEAT_EDU_ACTION =
-            "com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU";
     public static final String SETTINGS_ACTION =
             "android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
 
     private final Launcher mLauncher;
     private final Hotseat mHotseat;
-    private HotseatRestoreHelper mRestoreHelper;
     private List<WorkspaceItemInfo> mPredictedApps;
     private HotseatEduDialog mActiveDialog;
 
     private ArrayList<ItemInfo> mNewItems = new ArrayList<>();
     private IntArray mNewScreens = null;
-    private Runnable mOnOnboardingComplete;
 
-    HotseatEduController(Launcher launcher, HotseatRestoreHelper restoreHelper, Runnable runnable) {
+    HotseatEduController(Launcher launcher) {
         mLauncher = launcher;
         mHotseat = launcher.getHotseat();
-        mRestoreHelper = restoreHelper;
-        mOnOnboardingComplete = runnable;
     }
 
     /**
      * Checks what type of migration should be used and migrates hotseat
      */
     void migrate() {
-        if (mRestoreHelper != null) {
-            mRestoreHelper.createBackup();
-        }
+        HotseatRestoreHelper.createBackup(mLauncher);
         if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
             migrateToFolder();
         } else {
@@ -97,7 +89,7 @@
         ArrayList<WorkspaceItemInfo> putIntoFolder = new ArrayList<>();
 
         //separate folders and items that can get in folders
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
             View view = mHotseat.getChildAt(i, 0);
             if (view == null) continue;
             ItemInfo info = (ItemInfo) view.getTag();
@@ -196,7 +188,7 @@
                     .getInt(LauncherSettings.Settings.EXTRA_VALUE);
             mNewScreens = IntArray.wrap(pageId);
         }
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mLauncher.getDeviceProfile().numShownHotseatIcons; i++) {
             View child = mHotseat.getChildAt(i, 0);
             if (child == null || child.getTag() == null) continue;
             ItemInfo tag = (ItemInfo) child.getTag();
@@ -227,12 +219,12 @@
     }
 
     void finishOnboarding() {
-        mOnOnboardingComplete.run();
+        mLauncher.getModel().onWorkspaceUiChanged();
     }
 
     void showDimissTip() {
         if (mHotseat.getShortcutsAndWidgets().getChildCount()
-                < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
+                < mLauncher.getDeviceProfile().numShownHotseatIcons) {
             Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
                     R.string.hotseat_prediction_settings, null,
                     () -> mLauncher.startActivity(getSettingsIntent()));
@@ -284,4 +276,3 @@
         return new Intent(SETTINGS_ACTION).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     }
 }
-
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
similarity index 86%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 4b8e434..14b0c5d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -40,7 +40,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.views.AbstractSlideInView;
 
 import java.util.List;
@@ -49,7 +48,7 @@
  * User education dialog for hybrid hotseat. Allows user to migrate hotseat items to a new page in
  * the workspace and shows predictions on the whole hotseat
  */
-public class HotseatEduDialog extends AbstractSlideInView implements Insettable {
+public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements Insettable {
 
     private static final int DEFAULT_CLOSE_DURATION = 200;
     protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
@@ -85,11 +84,11 @@
         mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
         mSampleHotseat = findViewById(R.id.sample_prediction);
 
-        DeviceProfile grid = mLauncher.getDeviceProfile();
+        DeviceProfile grid = mActivityContext.getDeviceProfile();
         Rect padding = grid.getHotseatLayoutPadding();
 
         mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
-        mSampleHotseat.setGridSize(grid.inv.numHotseatIcons, 1);
+        mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1);
         mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
 
         Button turnOnBtn = findViewById(R.id.turn_predictions_on);
@@ -111,27 +110,17 @@
 
         mHotseatEduController.moveHotseatItems();
         mHotseatEduController.finishOnboarding();
-        mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
+        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
     }
 
     private void onDismiss(View v) {
         mHotseatEduController.showDimissTip();
         mHotseatEduController.finishOnboarding();
-        mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_DENY);
+        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_DENY);
         handleClose(true);
     }
 
     @Override
-    public void logActionCommand(int command) {
-        // Since this is on-boarding popup, it is not a user controlled action.
-    }
-
-    @Override
-    public int getLogContainerType() {
-        return LauncherLogProto.ContainerType.TIP;
-    }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_ON_BOARD_POPUP) != 0;
     }
@@ -142,12 +131,12 @@
         int rightInset = insets.right - mInsets.right;
         int bottomInset = insets.bottom - mInsets.bottom;
         mInsets.set(insets);
-        if (mLauncher.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
+        if (mActivityContext.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
             setPadding(leftInset, getPaddingTop(), rightInset, 0);
             mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
                     mHotseatWrapper.getPaddingRight(), bottomInset);
             mHotseatWrapper.getLayoutParams().height =
-                    mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
+                    mActivityContext.getDeviceProfile().hotseatBarSizePx + insets.bottom;
 
         } else {
             setPadding(0, getPaddingTop(), 0, 0);
@@ -189,7 +178,7 @@
     }
 
     private void populatePreview(List<WorkspaceItemInfo> predictions) {
-        for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+        for (int i = 0; i < mActivityContext.getDeviceProfile().numShownHotseatIcons; i++) {
             WorkspaceItemInfo info = predictions.get(i);
             PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
             icon.setEnabled(false);
@@ -205,13 +194,13 @@
      */
     public void show(List<WorkspaceItemInfo> predictions) {
         if (getParent() != null
-                || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
+                || predictions.size() < mActivityContext.getDeviceProfile().numShownHotseatIcons
                 || mHotseatEduController == null) {
             return;
         }
-        AbstractFloatingView.closeAllOpenViews(mLauncher);
+        AbstractFloatingView.closeAllOpenViews(mActivityContext);
         attachToContainer();
-        mLauncher.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
+        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
         animateOpen();
         populatePreview(predictions);
     }
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
new file mode 100644
index 0000000..85e5ab0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -0,0 +1,518 @@
+/*
+ * 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.hybridhotseat;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
+import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.ComponentName;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer;
+import com.android.launcher3.logging.InstanceId;
+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.touch.ItemLongClickListener;
+import com.android.launcher3.uioverrides.PredictedAppIcon;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.views.ArrowTipView;
+import com.android.launcher3.views.Snackbar;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows
+ * pinning of predicted apps and manages replacement of predicted apps with user drag.
+ */
+public class HotseatPredictionController implements DragController.DragListener,
+        SystemShortcut.Factory<QuickstepLauncher>, DeviceProfile.OnDeviceProfileChangeListener,
+        DragSource, ViewGroup.OnHierarchyChangeListener {
+
+    private static final int FLAG_UPDATE_PAUSED = 1 << 0;
+    private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1;
+    private static final int FLAG_FILL_IN_PROGRESS = 1 << 2;
+    private static final int FLAG_REMOVING_PREDICTED_ICON = 1 << 3;
+
+    private int mHotSeatItemsCount;
+
+    private QuickstepLauncher mLauncher;
+    private final Hotseat mHotseat;
+    private final Runnable mUpdateFillIfNotLoading = this::updateFillIfNotLoading;
+
+    private List<ItemInfo> mPredictedItems = Collections.emptyList();
+
+    private AnimatorSet mIconRemoveAnimators;
+    private int mPauseFlags = 0;
+
+    private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
+
+    private final View.OnLongClickListener mPredictionLongClickListener = v -> {
+        if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
+        if (mLauncher.getWorkspace().isSwitchingState()) return false;
+        if (!mLauncher.getOnboardingPrefs().getBoolean(
+                OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN)) {
+            Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+                    R.string.hotseat_prediction_settings, null,
+                    () -> mLauncher.startActivity(getSettingsIntent()));
+            mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN);
+            mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+            return true;
+        }
+
+        // Start the drag
+        // Use a new itemInfo so that the original predicted item is stable
+        WorkspaceItemInfo dragItem = new WorkspaceItemInfo((WorkspaceItemInfo) v.getTag());
+        v.setVisibility(View.INVISIBLE);
+        mLauncher.getWorkspace().beginDragShared(
+                v, null, this, dragItem, new DragPreviewProvider(v),
+                mLauncher.getDefaultWorkspaceDragOptions());
+        return true;
+    };
+
+    public HotseatPredictionController(QuickstepLauncher launcher) {
+        mLauncher = launcher;
+        mHotseat = launcher.getHotseat();
+        mHotSeatItemsCount = mLauncher.getDeviceProfile().numShownHotseatIcons;
+        mLauncher.getDragController().addDragListener(this);
+
+        launcher.addOnDeviceProfileChangeListener(this);
+        mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this);
+    }
+
+    @Override
+    public void onChildViewAdded(View parent, View child) {
+        onHotseatHierarchyChanged();
+    }
+
+    @Override
+    public void onChildViewRemoved(View parent, View child) {
+        onHotseatHierarchyChanged();
+    }
+
+    private void onHotseatHierarchyChanged() {
+        if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+            // Post update after a single frame to avoid layout within layout
+            MAIN_EXECUTOR.getHandler().removeCallbacks(mUpdateFillIfNotLoading);
+            MAIN_EXECUTOR.getHandler().post(mUpdateFillIfNotLoading);
+        }
+    }
+
+    private void updateFillIfNotLoading() {
+        if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+            fillGapsWithPrediction(true);
+        }
+    }
+
+    /**
+     * Shows appropriate hotseat education based on prediction enabled and migration states.
+     */
+    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();
+            }
+        }));
+    }
+
+    /**
+     * 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() {
+        return !mPredictedItems.isEmpty();
+    }
+
+    private void fillGapsWithPrediction() {
+        fillGapsWithPrediction(false);
+    }
+
+    private void fillGapsWithPrediction(boolean animate) {
+        if (mPauseFlags != 0) {
+            return;
+        }
+
+        int predictionIndex = 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()) {
+            mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    fillGapsWithPrediction(animate);
+                    mIconRemoveAnimators.removeListener(this);
+                }
+            });
+            return;
+        }
+
+        mPauseFlags |= FLAG_FILL_IN_PROGRESS;
+        for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
+            View child = mHotseat.getChildAt(
+                    mHotseat.getCellXFromOrder(rank),
+                    mHotseat.getCellYFromOrder(rank));
+
+            if (child != null && !isPredictedIcon(child)) {
+                continue;
+            }
+            if (mPredictedItems.size() <= predictionIndex) {
+                // Remove predicted apps from the past
+                if (isPredictedIcon(child)) {
+                    mHotseat.removeView(child);
+                }
+                continue;
+            }
+            WorkspaceItemInfo predictedItem =
+                    (WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
+            if (isPredictedIcon(child) && child.isEnabled()) {
+                PredictedAppIcon icon = (PredictedAppIcon) child;
+                icon.applyFromWorkspaceItem(predictedItem);
+                icon.finishBinding(mPredictionLongClickListener);
+            } else {
+                newItems.add(predictedItem);
+            }
+            preparePredictionInfo(predictedItem, rank);
+        }
+        bindItems(newItems, animate);
+
+        mPauseFlags &= ~FLAG_FILL_IN_PROGRESS;
+    }
+
+    private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) {
+        AnimatorSet animationSet = new AnimatorSet();
+        for (WorkspaceItemInfo item : itemsToAdd) {
+            PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
+            mLauncher.getWorkspace().addInScreenFromBind(icon, item);
+            icon.finishBinding(mPredictionLongClickListener);
+            if (animate) {
+                animationSet.play(ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0.2f, 1));
+            }
+        }
+        if (animate) {
+            animationSet.addListener(
+                    forSuccessCallback(this::removeOutlineDrawings));
+            animationSet.start();
+        } else {
+            removeOutlineDrawings();
+        }
+
+        if (mLauncher.getTaskbarUIController() != null) {
+            mLauncher.getTaskbarUIController().onHotseatUpdated();
+        }
+    }
+
+    private void removeOutlineDrawings() {
+        if (mOutlineDrawings.isEmpty()) return;
+        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+            mHotseat.removeDelegatedCellDrawing(outlineDrawing);
+        }
+        mHotseat.invalidate();
+        mOutlineDrawings.clear();
+    }
+
+
+    /**
+     * Unregisters callbacks and frees resources
+     */
+    public void destroy() {
+        mLauncher.removeOnDeviceProfileChangeListener(this);
+    }
+
+    /**
+     * start and pauses predicted apps update on the hotseat
+     */
+    public void setPauseUIUpdate(boolean paused) {
+        mPauseFlags = paused
+                ? (mPauseFlags | FLAG_UPDATE_PAUSED)
+                : (mPauseFlags & ~FLAG_UPDATE_PAUSED);
+        if (!paused) {
+            fillGapsWithPrediction();
+        }
+    }
+
+    /**
+     * Sets or updates the predicted items
+     */
+    public void setPredictedItems(FixedContainerItems items) {
+        boolean shouldIgnoreVisibility = FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get()
+                || mLauncher.isWorkspaceLoading()
+                || mPredictedItems.equals(items.items)
+                || mHotseat.getShortcutsAndWidgets().getChildCount() < mHotSeatItemsCount;
+        if (!shouldIgnoreVisibility
+                && mHotseat.isShown()
+                && mHotseat.getWindowVisibility() == View.VISIBLE) {
+            mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> {
+                if (isVisible) {
+                    return;
+                }
+                mHotseat.setOnVisibilityAggregatedCallback(null);
+
+                applyPredictedItems(items);
+            });
+        } else {
+            mHotseat.setOnVisibilityAggregatedCallback(null);
+
+            applyPredictedItems(items);
+        }
+    }
+
+    /**
+     * Sets or updates the predicted items only once the hotseat becomes hidden to the user
+     */
+    private void applyPredictedItems(FixedContainerItems items) {
+        mPredictedItems = items.items;
+        if (mPredictedItems.isEmpty()) {
+            HotseatRestoreHelper.restoreBackup(mLauncher);
+        }
+        fillGapsWithPrediction();
+    }
+
+    /**
+     * Pins a predicted app icon into place.
+     */
+    public void pinPrediction(ItemInfo info) {
+        PredictedAppIcon icon = (PredictedAppIcon) mHotseat.getChildAt(
+                mHotseat.getCellXFromOrder(info.rank),
+                mHotseat.getCellYFromOrder(info.rank));
+        if (icon == null) {
+            return;
+        }
+        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo((WorkspaceItemInfo) info);
+        mLauncher.getModelWriter().addItemToDatabase(workspaceItemInfo,
+                LauncherSettings.Favorites.CONTAINER_HOTSEAT, workspaceItemInfo.screenId,
+                workspaceItemInfo.cellX, workspaceItemInfo.cellY);
+        ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
+        icon.pin(workspaceItemInfo);
+        mLauncher.getStatsLogManager().logger()
+                .withItemInfo(workspaceItemInfo)
+                .log(LAUNCHER_HOTSEAT_PREDICTION_PINNED);
+    }
+
+    private List<PredictedAppIcon> getPredictedIcons() {
+        List<PredictedAppIcon> icons = new ArrayList<>();
+        ViewGroup vg = mHotseat.getShortcutsAndWidgets();
+        for (int i = 0; i < vg.getChildCount(); i++) {
+            View child = vg.getChildAt(i);
+            if (isPredictedIcon(child)) {
+                icons.add((PredictedAppIcon) child);
+            }
+        }
+        return icons;
+    }
+
+    private void removePredictedApps(List<PredictedAppIcon.PredictedIconOutlineDrawing> outlines,
+            DropTarget.DragObject dragObject) {
+        if (mIconRemoveAnimators != null) {
+            mIconRemoveAnimators.end();
+        }
+        mIconRemoveAnimators = new AnimatorSet();
+        removeOutlineDrawings();
+        for (PredictedAppIcon icon : getPredictedIcons()) {
+            if (!icon.isEnabled()) {
+                continue;
+            }
+            if (dragObject.dragSource == this && icon.equals(dragObject.originalView)) {
+                removeIconWithoutNotify(icon);
+                continue;
+            }
+            int rank = ((WorkspaceItemInfo) icon.getTag()).rank;
+            outlines.add(new PredictedAppIcon.PredictedIconOutlineDrawing(
+                    mHotseat.getCellXFromOrder(rank), mHotseat.getCellYFromOrder(rank), icon));
+            icon.setEnabled(false);
+            ObjectAnimator animator = ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0);
+            animator.addListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    if (icon.getParent() != null) {
+                        removeIconWithoutNotify(icon);
+                    }
+                }
+            });
+            mIconRemoveAnimators.play(animator);
+        }
+        mIconRemoveAnimators.start();
+    }
+
+    /**
+     * Removes icon while suppressing any extra tasks performed on view-hierarchy changes.
+     * This avoids recursive/redundant updates as the control updates the UI anyway after
+     * it's animation.
+     */
+    private void removeIconWithoutNotify(PredictedAppIcon icon) {
+        mPauseFlags |= FLAG_REMOVING_PREDICTED_ICON;
+        mHotseat.removeView(icon);
+        mPauseFlags &= ~FLAG_REMOVING_PREDICTED_ICON;
+    }
+
+    @Override
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        removePredictedApps(mOutlineDrawings, dragObject);
+        if (mOutlineDrawings.isEmpty()) return;
+        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+            mHotseat.addDelegatedCellDrawing(outlineDrawing);
+        }
+        mPauseFlags |= FLAG_DRAG_IN_PROGRESS;
+        mHotseat.invalidate();
+    }
+
+    @Override
+    public void onDragEnd() {
+        mPauseFlags &= ~FLAG_DRAG_IN_PROGRESS;
+        fillGapsWithPrediction(true);
+    }
+
+    @Nullable
+    @Override
+    public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
+            ItemInfo itemInfo) {
+        if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+            return null;
+        }
+        return new PinPrediction(activity, itemInfo);
+    }
+
+    private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
+        itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+        itemInfo.rank = rank;
+        itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
+        itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
+        itemInfo.screenId = rank;
+    }
+
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile profile) {
+        this.mHotSeatItemsCount = profile.numShownHotseatIcons;
+    }
+
+    @Override
+    public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
+        //Does nothing
+    }
+
+    /**
+     * Logs rank info based on current list of predicted items
+     */
+    public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
+        ComponentName targetCN = itemInfo.getTargetComponent();
+        if (targetCN == null) {
+            return;
+        }
+        int rank = -1;
+        for (int i = mPredictedItems.size() - 1; i >= 0; i--) {
+            ItemInfo info = mPredictedItems.get(i);
+            if (targetCN.equals(info.getTargetComponent()) && itemInfo.user.equals(info.user)) {
+                rank = i;
+                break;
+            }
+        }
+        if (rank < 0) {
+            return;
+        }
+
+        int cardinality = 0;
+        for (PredictedAppIcon icon : getPredictedIcons()) {
+            ItemInfo info = (ItemInfo) icon.getTag();
+            cardinality |= 1 << info.screenId;
+        }
+
+        PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
+        containerBuilder.setCardinality(cardinality);
+        if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+            containerBuilder.setIndex(rank);
+        }
+        mLauncher.getStatsLogManager().logger()
+                .withInstanceId(instanceId)
+                .withRank(rank)
+                .withContainerInfo(ContainerInfo.newBuilder()
+                        .setPredictedHotseatContainer(containerBuilder)
+                        .build())
+                .log(LAUNCHER_HOTSEAT_RANKED);
+    }
+
+    private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
+
+        private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
+            super(R.drawable.ic_pin, R.string.pin_prediction, target,
+                    itemInfo);
+        }
+
+        @Override
+        public void onClick(View view) {
+            dismissTaskMenuView(mTarget);
+            pinPrediction(mItemInfo);
+        }
+    }
+
+    private static boolean isPredictedIcon(View view) {
+        return view instanceof PredictedAppIcon && view.getTag() instanceof WorkspaceItemInfo
+                && ((WorkspaceItemInfo) view.getTag()).container
+                == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
similarity index 66%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
index 5a038d2..080633a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3.hybridhotseat;
 
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
@@ -24,13 +24,10 @@
 import android.content.Context;
 import android.os.Bundle;
 
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.model.AllAppsList;
-import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.PredictionModel;
+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;
@@ -38,55 +35,47 @@
 
 import java.util.ArrayList;
 import java.util.Locale;
-import java.util.function.Consumer;
 
 /**
  * Model helper for app predictions in workspace
  */
-public class HotseatPredictionModel extends PredictionModel {
+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";
 
-
-    public HotseatPredictionModel(Context context) { }
-
     /**
-     * Creates and returns bundle using workspace items and cached items
+     * Creates and returns bundle using workspace items
      */
-    public void createBundle(Consumer<Bundle> cb) {
-        LauncherAppState appState = LauncherAppState.getInstance(mContext);
-        appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
-            @Override
-            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-                Bundle bundle = new Bundle();
-                ArrayList<AppTargetEvent> events = new ArrayList<>();
-                ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems);
-                workspaceItems.addAll(dataModel.appWidgets);
-                for (ItemInfo item : workspaceItems) {
-                    AppTarget target = getAppTargetFromInfo(item);
-                    if (target != null && !isTrackedForPrediction(item)) continue;
-                    events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
-                }
-                ArrayList<AppTarget> currentTargets = new ArrayList<>();
-                for (ItemInfo itemInfo : dataModel.cachedPredictedItems) {
-                    AppTarget target = getAppTargetFromInfo(itemInfo);
-                    if (target != null) currentTargets.add(target);
-                }
-                bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
-                bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
-                MAIN_EXECUTOR.execute(() -> cb.accept(bundle));
+    public static Bundle convertDataModelToAppTargetBundle(Context context, BgDataModel dataModel) {
+        Bundle bundle = new Bundle();
+        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));
+        }
+        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);
+                if (target != null) currentTargets.add(target);
             }
-        });
+        }
+        bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
+        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 AppTarget getAppTargetFromInfo(ItemInfo info) {
+    public static AppTarget getAppTargetFromInfo(Context context, ItemInfo info) {
         if (info == null) return null;
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
                 && info instanceof LauncherAppWidgetInfo
@@ -107,17 +96,17 @@
                     shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
-                    mContext.getPackageName(), info.user).build();
+                    context.getPackageName(), info.user).build();
         }
         return null;
     }
 
-
     /**
      * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
      * location using {@link ItemInfo}
      */
-    public AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
+    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,
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
similarity index 77%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
index 9e7c9fb..4956fa1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
@@ -19,8 +19,10 @@
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.content.Context;
+
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.model.GridBackupTable;
 import com.android.launcher3.provider.LauncherDbUtils;
@@ -29,29 +31,24 @@
  * A helper class to manage migration revert restoration for hybrid hotseat
  */
 public class HotseatRestoreHelper {
-    private final Launcher mLauncher;
-
-    HotseatRestoreHelper(Launcher context) {
-        mLauncher = context;
-    }
 
     /**
      * Creates a snapshot backup of Favorite table for future restoration use.
      */
-    public void createBackup() {
+    public static void createBackup(Context context) {
         MODEL_EXECUTOR.execute(() -> {
             try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
                     LauncherSettings.Settings.call(
-                            mLauncher.getContentResolver(),
+                            context.getContentResolver(),
                             LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
                             .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
-                InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
-                GridBackupTable backupTable = new GridBackupTable(mLauncher,
-                        transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
+                InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+                GridBackupTable backupTable = new GridBackupTable(context,
+                        transaction.getDb(), idp.numDatabaseHotseatIcons, idp.numColumns,
                         idp.numRows);
                 backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE);
                 transaction.commit();
-                LauncherSettings.Settings.call(mLauncher.getContentResolver(),
+                LauncherSettings.Settings.call(context.getContentResolver(),
                         LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE);
             }
         });
@@ -60,23 +57,23 @@
     /**
      * Finds and restores a previously saved snapshow of Favorites table
      */
-    public void restoreBackup() {
+    public static void restoreBackup(Context context) {
         MODEL_EXECUTOR.execute(() -> {
             try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
                     LauncherSettings.Settings.call(
-                            mLauncher.getContentResolver(),
+                            context.getContentResolver(),
                             LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
                             .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
                 if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) {
                     return;
                 }
-                InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
-                GridBackupTable backupTable = new GridBackupTable(mLauncher,
-                        transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
+                InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+                GridBackupTable backupTable = new GridBackupTable(context,
+                        transaction.getDb(), idp.numDatabaseHotseatIcons, idp.numColumns,
                         idp.numRows);
                 backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true);
                 transaction.commit();
-                mLauncher.getModel().forceReload();
+                LauncherAppState.getInstance(context).getModel().forceReload();
             }
         });
     }
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
new file mode 100644
index 0000000..eed493d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -0,0 +1,314 @@
+/*
+ * 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.model;
+
+import static android.app.prediction.AppTargetEvent.ACTION_DISMISS;
+import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH;
+import static android.app.prediction.AppTargetEvent.ACTION_PIN;
+import static android.app.prediction.AppTargetEvent.ACTION_UNPIN;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_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;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
+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_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.util.Executors.MODEL_EXECUTOR;
+
+import android.annotation.TargetApi;
+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.content.pm.ShortcutInfo;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.FolderContainer;
+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;
+
+import java.util.Locale;
+import java.util.Optional;
+import java.util.function.ObjIntConsumer;
+import java.util.function.Predicate;
+
+/**
+ * Utility class to track stats log and emit corresponding app events
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public class AppEventProducer implements StatsLogConsumer {
+
+    private static final int MSG_LAUNCH = 0;
+
+    private final Context mContext;
+    private final Handler mMessageHandler;
+    private final ObjIntConsumer<AppTargetEvent> mCallback;
+
+    private LauncherAtom.ItemInfo mLastDragItem;
+
+    public AppEventProducer(Context context, ObjIntConsumer<AppTargetEvent> callback) {
+        mContext = context;
+        mMessageHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleMessage);
+        mCallback = callback;
+    }
+
+    @WorkerThread
+    private boolean handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_LAUNCH: {
+                mCallback.accept((AppTargetEvent) msg.obj, msg.arg1);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @AnyThread
+    private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId, int targetPredictor) {
+        sendEvent(toAppTarget(atomInfo), atomInfo, eventId, targetPredictor);
+    }
+
+    @AnyThread
+    private void sendEvent(AppTarget target, LauncherAtom.ItemInfo locationInfo, int eventId,
+            int targetPredictor) {
+        if (target != null) {
+            AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
+                    .setLaunchLocation(getContainer(locationInfo))
+                    .build();
+            mMessageHandler.obtainMessage(MSG_LAUNCH, targetPredictor, 0, event).sendToTarget();
+        }
+    }
+
+    @Override
+    public void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
+        if (event == LAUNCHER_APP_LAUNCH_TAP
+                || event == LAUNCHER_TASK_LAUNCH_SWIPE_DOWN
+                || event == LAUNCHER_TASK_LAUNCH_TAP
+                || event == LAUNCHER_QUICKSWITCH_RIGHT
+                || event == LAUNCHER_QUICKSWITCH_LEFT) {
+            sendEvent(atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
+        } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST) {
+            sendEvent(atomInfo, ACTION_DISMISS, CONTAINER_PREDICTION);
+        } else if (event == LAUNCHER_ITEM_DRAG_STARTED) {
+            mLastDragItem = atomInfo;
+        } else if (event == LAUNCHER_ITEM_DROP_COMPLETED) {
+            if (mLastDragItem == null) {
+                return;
+            }
+            if (isTrackedForHotseatPrediction(atomInfo)) {
+                sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+            if (isTrackedForHotseatPrediction(mLastDragItem)) {
+                sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+            mLastDragItem = null;
+        } else if (event == LAUNCHER_ITEM_DROP_FOLDER_CREATED) {
+            if (isTrackedForHotseatPrediction(atomInfo)) {
+                sendEvent(createTempFolderTarget(), atomInfo, ACTION_PIN,
+                        CONTAINER_HOTSEAT_PREDICTION);
+                sendEvent(atomInfo, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+        } else if (event == LAUNCHER_FOLDER_CONVERTED_TO_ICON) {
+            if (isTrackedForHotseatPrediction(atomInfo)) {
+                sendEvent(createTempFolderTarget(), atomInfo, ACTION_UNPIN,
+                        CONTAINER_HOTSEAT_PREDICTION);
+                sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+        } else if (event == LAUNCHER_ITEM_DROPPED_ON_REMOVE) {
+            if (mLastDragItem != null && isTrackedForHotseatPrediction(mLastDragItem)) {
+                sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+        } else if (event == LAUNCHER_HOTSEAT_PREDICTION_PINNED) {
+            if (isTrackedForHotseatPrediction(atomInfo)) {
+                sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+            }
+        }
+    }
+
+    @Nullable
+    private AppTarget toAppTarget(LauncherAtom.ItemInfo info) {
+        UserHandle userHandle = Process.myUserHandle();
+        if (info.getIsWork()) {
+            userHandle = UserCache.INSTANCE.get(mContext).getUserProfiles().stream()
+                    .filter(((Predicate<UserHandle>) userHandle::equals).negate())
+                    .findAny()
+                    .orElse(null);
+        }
+        if (userHandle == null) {
+            return null;
+        }
+        ComponentName cn = null;
+        ShortcutInfo shortcutInfo = null;
+        String id = null;
+
+        switch (info.getItemCase()) {
+            case APPLICATION: {
+                LauncherAtom.Application app = info.getApplication();
+                if ((cn = parseNullable(app.getComponentName())) != null) {
+                    id = "app:" + cn.getPackageName();
+                }
+                break;
+            }
+            case SHORTCUT: {
+                LauncherAtom.Shortcut si = info.getShortcut();
+                if (!TextUtils.isEmpty(si.getShortcutId())
+                        && (cn = parseNullable(si.getShortcutName())) != null) {
+                    Optional<ShortcutInfo> opt = new ShortcutRequest(mContext,
+                            userHandle).forPackage(cn.getPackageName(), si.getShortcutId()).query(
+                            ShortcutRequest.ALL).stream().findFirst();
+                    if (opt.isPresent()) {
+                        shortcutInfo = opt.get();
+                    } else {
+                        return null;
+                    }
+                    id = "shortcut:" + si.getShortcutId();
+                }
+                break;
+            }
+            case WIDGET: {
+                LauncherAtom.Widget widget = info.getWidget();
+                if ((cn = parseNullable(widget.getComponentName())) != null) {
+                    id = "widget:" + cn.getPackageName();
+                }
+                break;
+            }
+            case TASK: {
+                LauncherAtom.Task task = info.getTask();
+                if ((cn = parseNullable(task.getComponentName())) != null) {
+                    id = "app:" + cn.getPackageName();
+                }
+                break;
+            }
+            case FOLDER_ICON:
+                return createTempFolderTarget();
+        }
+        if (id != null && cn != null) {
+            if (shortcutInfo != null) {
+                return new AppTarget.Builder(new AppTargetId(id), shortcutInfo).build();
+            }
+            return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle)
+                    .setClassName(cn.getClassName())
+                    .build();
+        }
+        return null;
+    }
+
+
+    private AppTarget createTempFolderTarget() {
+        return new AppTarget.Builder(new AppTargetId("folder:" + SystemClock.uptimeMillis()),
+                mContext.getPackageName(), Process.myUserHandle())
+                .build();
+    }
+
+    private String getContainer(LauncherAtom.ItemInfo info) {
+        ContainerInfo ci = info.getContainerInfo();
+        switch (ci.getContainerCase()) {
+            case WORKSPACE: {
+                // In case the item type is not widgets, the spaceX and spanY default to 1.
+                int spanX = info.getWidget().getSpanX();
+                int spanY = info.getWidget().getSpanY();
+                return getWorkspaceContainerString(ci.getWorkspace(), spanX, spanY);
+            }
+            case HOTSEAT: {
+                return getHotseatContainerString(ci.getHotseat());
+            }
+            case TASK_SWITCHER_CONTAINER: {
+                return "task-switcher";
+            }
+            case ALL_APPS_CONTAINER: {
+                return "all-apps";
+            }
+            case SEARCH_RESULT_CONTAINER: {
+                return "search-results";
+            }
+            case PREDICTED_HOTSEAT_CONTAINER: {
+                return "predictions/hotseat";
+            }
+            case PREDICTION_CONTAINER: {
+                return "predictions";
+            }
+            case SHORTCUTS_CONTAINER: {
+                return "deep-shortcuts";
+            }
+            case FOLDER: {
+                FolderContainer fc = ci.getFolder();
+                switch (fc.getParentContainerCase()) {
+                    case WORKSPACE:
+                        return "folder/" + getWorkspaceContainerString(fc.getWorkspace(), 1, 1);
+                    case HOTSEAT:
+                        return "folder/" + getHotseatContainerString(fc.getHotseat());
+                }
+                return "folder";
+            }
+        }
+        return "";
+    }
+
+    private static String getWorkspaceContainerString(WorkspaceContainer wc, int spanX, int spanY) {
+        return String.format(Locale.ENGLISH, "workspace/%d/[%d,%d]/[%d,%d]",
+                wc.getPageIndex(), wc.getGridX(), wc.getGridY(), spanX, spanY);
+    }
+
+    private static String getHotseatContainerString(HotseatContainer hc) {
+        return String.format(Locale.ENGLISH, "hotseat/%1$d/[%1$d,0]/[1,1]", hc.getIndex());
+    }
+
+    private static ComponentName parseNullable(String componentNameString) {
+        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/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
new file mode 100644
index 0000000..b0fba3d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -0,0 +1,119 @@
+/*
+ * 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.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE;
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.app.prediction.AppTarget;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+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.WorkspaceItemInfo;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Task to update model as a result of predicted apps update
+ */
+public class PredictionUpdateTask extends BaseModelUpdateTask {
+
+    private final List<AppTarget> mTargets;
+    private final PredictorState mPredictorState;
+
+    PredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets) {
+        mPredictorState = predictorState;
+        mTargets = targets;
+    }
+
+    @Override
+    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+        Context context = app.getContext();
+
+        // TODO: remove this
+        Utilities.getDevicePrefs(context).edit()
+                .putBoolean(LAST_PREDICTION_ENABLED_STATE, !mTargets.isEmpty()).apply();
+
+        FixedContainerItems fci = mPredictorState.items;
+        Set<UserHandle> usersForChangedShortcuts = new HashSet<>(fci.items.stream()
+                .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT)
+                .map(info -> info.user)
+                .collect(Collectors.toSet()));
+        fci.items.clear();
+
+        for (AppTarget target : mTargets) {
+            WorkspaceItemInfo itemInfo;
+            ShortcutInfo si = target.getShortcutInfo();
+            if (si != null) {
+                usersForChangedShortcuts.add(si.getUserHandle());
+                itemInfo = new WorkspaceItemInfo(si, context);
+                app.getIconCache().getShortcutIcon(itemInfo, si);
+            } else {
+                String className = target.getClassName();
+                if (COMPONENT_CLASS_MARKER.equals(className)) {
+                    // TODO: Implement this
+                    continue;
+                }
+                ComponentName cn = new ComponentName(target.getPackageName(), className);
+                UserHandle user = target.getUser();
+                itemInfo = apps.data.stream()
+                        .filter(info -> user.equals(info.user) && cn.equals(info.componentName))
+                        .map(ai -> {
+                            app.getIconCache().getTitleAndIcon(ai, false);
+                            return ai.makeWorkspaceItem();
+                        })
+                        .findAny()
+                        .orElseGet(() -> {
+                            LauncherActivityInfo lai = context.getSystemService(LauncherApps.class)
+                                    .resolveActivity(AppInfo.makeLaunchIntent(cn), user);
+                            if (lai == null) {
+                                return null;
+                            }
+                            AppInfo ai = new AppInfo(context, lai, user);
+                            app.getIconCache().getTitleAndIcon(ai, lai, false);
+                            return ai.makeWorkspaceItem();
+                        });
+
+                if (itemInfo == null) {
+                    continue;
+                }
+            }
+
+            itemInfo.container = fci.containerId;
+            fci.items.add(itemInfo);
+        }
+
+        bindExtraContainerItems(fci);
+        usersForChangedShortcuts.forEach(
+                u -> dataModel.updateShortcutPinnedState(app.getContext(), u));
+
+        // Save to disk
+        mPredictorState.storage.write(context, fci.items);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
new file mode 100644
index 0000000..a9c2a5e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -0,0 +1,376 @@
+/*
+ * 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 android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.formatElapsedTime;
+
+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.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+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 android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+import android.util.Log;
+
+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.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.PersistedItemArray;
+import com.android.quickstep.logging.StatsLogCompatManager;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+/**
+ * Model delegate which loads prediction items
+ */
+public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChangeListener {
+
+    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 int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
+
+    private static final boolean IS_DEBUG = false;
+    private static final String TAG = "QuickstepModelDelegate";
+
+    private final PredictorState mAllAppsState =
+            new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions");
+    private final PredictorState mHotseatState =
+            new PredictorState(CONTAINER_HOTSEAT_PREDICTION, "hotseat_predictions");
+    private final PredictorState mWidgetsRecommendationState =
+            new PredictorState(CONTAINER_WIDGETS_PREDICTION, "widgets_prediction");
+
+    private final InvariantDeviceProfile mIDP;
+    private final AppEventProducer mAppEventProducer;
+
+    protected boolean mActive = false;
+
+    public QuickstepModelDelegate(Context context) {
+        mAppEventProducer = new AppEventProducer(context, this::onAppTargetEvent);
+
+        mIDP = InvariantDeviceProfile.INSTANCE.get(context);
+        mIDP.addOnChangeListener(this);
+        StatsLogCompatManager.LOGS_CONSUMER.add(mAppEventProducer);
+    }
+
+    @Override
+    @WorkerThread
+    public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) {
+        // TODO: Implement caching and preloading
+        super.loadItems(ums, pinnedShortcuts);
+
+        WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(
+                mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns);
+        mAllAppsState.items.setItems(
+                mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
+        mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
+
+        WorkspaceItemFactory hotseatFactory =
+                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons);
+        mHotseatState.items.setItems(
+                mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
+        mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items);
+
+        // Widgets prediction isn't used frequently. And thus, it is not persisted on disk.
+        mDataModel.extraItems.put(CONTAINER_WIDGETS_PREDICTION, mWidgetsRecommendationState.items);
+        mActive = true;
+    }
+
+    @Override
+    public void workspaceLoadComplete() {
+        super.workspaceLoadComplete();
+        recreatePredictors();
+    }
+
+    @Override
+    @WorkerThread
+    public void modelLoadComplete() {
+        super.modelLoadComplete();
+
+        // Log snapshot of the model
+        SharedPreferences prefs = getDevicePrefs(mApp.getContext());
+        long lastSnapshotTimeMillis = prefs.getLong(LAST_SNAPSHOT_TIME_MILLIS, 0);
+        // Log snapshot only if previous snapshot was older than a day
+        long now = System.currentTimeMillis();
+        if (now - lastSnapshotTimeMillis < DAY_IN_MILLIS) {
+            if (IS_DEBUG) {
+                String elapsedTime = formatElapsedTime((now - lastSnapshotTimeMillis) / 1000);
+                Log.d(TAG, String.format(
+                        "Skipped snapshot logging since previous snapshot was %s old.",
+                        elapsedTime));
+            }
+        } else {
+            IntSparseArrayMap<ItemInfo> itemsIdMap;
+            synchronized (mDataModel) {
+                itemsIdMap = mDataModel.itemsIdMap.clone();
+            }
+            InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+            for (ItemInfo info : itemsIdMap) {
+                FolderInfo parent = info.container > 0
+                        ? (FolderInfo) itemsIdMap.get(info.container) : null;
+                StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId);
+            }
+            additionalSnapshotEvents(instanceId);
+            prefs.edit().putLong(LAST_SNAPSHOT_TIME_MILLIS, now).apply();
+        }
+    }
+
+    protected void additionalSnapshotEvents(InstanceId snapshotInstanceId){}
+
+    @Override
+    public void validateData() {
+        super.validateData();
+        if (mAllAppsState.predictor != null) {
+            mAllAppsState.predictor.requestPredictionUpdate();
+        }
+        if (mWidgetsRecommendationState.predictor != null) {
+            mWidgetsRecommendationState.predictor.requestPredictionUpdate();
+        }
+    }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+        mActive = false;
+        StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer);
+
+        destroyPredictors();
+        mIDP.removeOnChangeListener(this);
+    }
+
+    private void destroyPredictors() {
+        mAllAppsState.destroyPredictor();
+        mHotseatState.destroyPredictor();
+        mWidgetsRecommendationState.destroyPredictor();
+    }
+
+    @WorkerThread
+    private void recreatePredictors() {
+        destroyPredictors();
+        if (!mActive) {
+            return;
+        }
+        Context context = mApp.getContext();
+        AppPredictionManager apm = context.getSystemService(AppPredictionManager.class);
+        if (apm == null) {
+            return;
+        }
+
+        registerPredictor(mAllAppsState, apm.createAppPredictionSession(
+                new AppPredictionContext.Builder(context)
+                        .setUiSurface("home")
+                        .setPredictedTargetCount(mIDP.numDatabaseAllAppsColumns)
+                        .build()));
+
+        // TODO: get bundle
+        registerPredictor(mHotseatState, apm.createAppPredictionSession(
+                new AppPredictionContext.Builder(context)
+                        .setUiSurface("hotseat")
+                        .setPredictedTargetCount(mIDP.numDatabaseHotseatIcons)
+                        .setExtras(convertDataModelToAppTargetBundle(context, mDataModel))
+                        .build()));
+
+        registerWidgetsPredictor(apm.createAppPredictionSession(
+                new AppPredictionContext.Builder(context)
+                        .setUiSurface("widgets")
+                        .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
+                        .build()));
+    }
+
+    private void registerPredictor(PredictorState state, AppPredictor predictor) {
+        state.predictor = predictor;
+        state.predictor.registerPredictionUpdates(
+                Executors.MODEL_EXECUTOR, t -> handleUpdate(state, t));
+        state.predictor.requestPredictionUpdate();
+    }
+
+    private void handleUpdate(PredictorState state, List<AppTarget> targets) {
+        if (state.setTargets(targets)) {
+            // No diff, skip
+            return;
+        }
+        mApp.getModel().enqueueModelUpdateTask(new PredictionUpdateTask(state, targets));
+    }
+
+    private void registerWidgetsPredictor(AppPredictor predictor) {
+        mWidgetsRecommendationState.predictor = predictor;
+        mWidgetsRecommendationState.predictor.registerPredictionUpdates(
+                Executors.MODEL_EXECUTOR, targets -> {
+                    if (mWidgetsRecommendationState.setTargets(targets)) {
+                        // No diff, skip
+                        return;
+                    }
+                    mApp.getModel().enqueueModelUpdateTask(
+                            new WidgetsPredictionUpdateTask(mWidgetsRecommendationState, targets));
+                });
+        mWidgetsRecommendationState.predictor.requestPredictionUpdate();
+    }
+
+    @Override
+    public void onIdpChanged(InvariantDeviceProfile profile) {
+        // Reinitialize everything
+        Executors.MODEL_EXECUTOR.execute(this::recreatePredictors);
+    }
+
+    private void onAppTargetEvent(AppTargetEvent event, int client) {
+        PredictorState state = client == CONTAINER_PREDICTION ? mAllAppsState : mHotseatState;
+        if (state.predictor != null) {
+            state.predictor.notifyAppTargetEvent(event);
+        }
+    }
+
+    static class PredictorState {
+
+        public final FixedContainerItems items;
+        public final PersistedItemArray<ItemInfo> storage;
+        public AppPredictor predictor;
+
+        private List<AppTarget> mLastTargets;
+
+        PredictorState(int container, String storageName) {
+            items = new FixedContainerItems(container);
+            storage = new PersistedItemArray<>(storageName);
+            mLastTargets = Collections.emptyList();
+        }
+
+        public void destroyPredictor() {
+            if (predictor != null) {
+                predictor.destroy();
+                predictor = null;
+            }
+        }
+
+        /**
+         * Sets the new targets and returns true if it was the same as before.
+         */
+        boolean setTargets(List<AppTarget> newTargets) {
+            List<AppTarget> oldTargets = mLastTargets;
+            mLastTargets = newTargets;
+
+            int size = oldTargets.size();
+            return size == newTargets.size() && IntStream.range(0, size)
+                    .allMatch(i -> areAppTargetsSame(oldTargets.get(i), newTargets.get(i)));
+        }
+    }
+
+    /**
+     * Compares two targets for the properties which we care about
+     */
+    private static boolean areAppTargetsSame(AppTarget t1, AppTarget t2) {
+        if (!Objects.equals(t1.getPackageName(), t2.getPackageName())
+                || !Objects.equals(t1.getUser(), t2.getUser())
+                || !Objects.equals(t1.getClassName(), t2.getClassName())) {
+            return false;
+        }
+
+        ShortcutInfo s1 = t1.getShortcutInfo();
+        ShortcutInfo s2 = t2.getShortcutInfo();
+        if (s1 != null) {
+            if (s2 == null || !Objects.equals(s1.getId(), s2.getId())) {
+                return false;
+            }
+        } else if (s2 != null) {
+            return false;
+        }
+        return true;
+    }
+
+    private static class WorkspaceItemFactory implements PersistedItemArray.ItemFactory<ItemInfo> {
+
+        private final LauncherAppState mAppState;
+        private final UserManagerState mUMS;
+        private final Map<ShortcutKey, ShortcutInfo> mPinnedShortcuts;
+        private final int mMaxCount;
+
+        private int mReadCount = 0;
+
+        protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums,
+                Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount) {
+            mAppState = appState;
+            mUMS = ums;
+            mPinnedShortcuts = pinnedShortcuts;
+            mMaxCount = maxCount;
+        }
+
+        @Nullable
+        @Override
+        public ItemInfo createInfo(int itemType, UserHandle user, Intent intent) {
+            if (mReadCount >= mMaxCount) {
+                return null;
+            }
+            switch (itemType) {
+                case ITEM_TYPE_APPLICATION: {
+                    LauncherActivityInfo lai = mAppState.getContext()
+                            .getSystemService(LauncherApps.class)
+                            .resolveActivity(intent, user);
+                    if (lai == null) {
+                        return null;
+                    }
+                    AppInfo info = new AppInfo(lai, user, mUMS.isUserQuiet(user));
+                    mAppState.getIconCache().getTitleAndIcon(info, lai, false);
+                    mReadCount++;
+                    return info.makeWorkspaceItem();
+                }
+                case ITEM_TYPE_DEEP_SHORTCUT: {
+                    ShortcutKey key = ShortcutKey.fromIntent(intent, user);
+                    if (key == null) {
+                        return null;
+                    }
+                    ShortcutInfo si = mPinnedShortcuts.get(key);
+                    if (si == null) {
+                        return null;
+                    }
+                    WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mAppState.getContext());
+                    mAppState.getIconCache().getShortcutIcon(wii, si);
+                    mReadCount++;
+                    return wii;
+                }
+            }
+            return null;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 810f4e3..154b78b 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -18,8 +18,7 @@
 
 import static android.content.ContentResolver.SCHEME_CONTENT;
 
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
+import static com.android.launcher3.Utilities.newContentObserver;
 
 import android.annotation.TargetApi;
 import android.app.RemoteAction;
@@ -35,7 +34,7 @@
 import android.os.Bundle;
 import android.os.DeadObjectException;
 import android.os.Handler;
-import android.os.Message;
+import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -43,7 +42,7 @@
 import android.util.Log;
 
 import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseDraggingActivity;
@@ -55,6 +54,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.RemoteActionShortcut;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.BgObjectWithLooper;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Preconditions;
@@ -63,21 +63,16 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.function.Consumer;
 
 /**
  * Data model for digital wellbeing status of apps.
  */
 @TargetApi(Build.VERSION_CODES.Q)
-public final class WellbeingModel {
+public final class WellbeingModel extends BgObjectWithLooper {
     private static final String TAG = "WellbeingModel";
     private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
     private static final boolean DEBUG = false;
 
-    private static final int MSG_PACKAGE_ADDED = 1;
-    private static final int MSG_PACKAGE_REMOVED = 2;
-    private static final int MSG_FULL_REFRESH = 3;
-
     private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
     private static final int IN_MINIMAL_DEVICE = 2;
 
@@ -99,9 +94,9 @@
 
     private final Context mContext;
     private final String mWellbeingProviderPkg;
-    private final Handler mWorkerHandler;
 
-    private final ContentObserver mContentObserver;
+    private Handler mWorkerHandler;
+    private ContentObserver mContentObserver;
 
     private final Object mModelLock = new Object();
     // Maps the action Id to the corresponding RemoteAction
@@ -112,60 +107,73 @@
 
     private WellbeingModel(final Context context) {
         mContext = context;
-        mWorkerHandler =
-                new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
-
         mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
-        mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
-            @Override
-            public void onChange(boolean selfChange, Uri uri) {
-                if (DEBUG || mIsInTest) {
-                    Log.d(TAG, "ContentObserver.onChange() called with: selfChange = ["
-                            + selfChange + "], uri = [" + uri + "]");
-                }
-                Preconditions.assertUIThread();
+        initializeInBackground("WellbeingHandler");
+    }
 
-                if (uri.getPath().contains(PATH_ACTIONS)) {
-                    // Wellbeing reports that app actions have changed.
-                    updateWellbeingData();
-                } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
-                    // Wellbeing reports that minimal device state or config is changed.
-                    updateLauncherModel(context);
-                }
-            }
-        };
-        FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () ->
-                updateLauncherModel(context));
-
+    @Override
+    protected void onInitialized(Looper looper) {
+        mWorkerHandler = new Handler(looper);
+        mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged);
         if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
-            context.registerReceiver(
-                    new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
+            mContext.registerReceiver(
+                    new SimpleBroadcastReceiver(t -> restartObserver()),
                     PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
                             Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
                             Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
-                            Intent.ACTION_PACKAGE_RESTARTED));
+                            Intent.ACTION_PACKAGE_RESTARTED),
+                    null, mWorkerHandler);
 
             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
             filter.addDataScheme("package");
-            context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
-                    filter);
+            mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
+                    filter, null, mWorkerHandler);
 
             restartObserver();
         }
     }
 
+    @WorkerThread
+    private void onWellbeingUriChanged(Uri uri) {
+        Preconditions.assertNonUiThread();
+        if (DEBUG || mIsInTest) {
+            Log.d(TAG, "ContentObserver.onChange() called with: uri = [" + uri + "]");
+        }
+        if (uri.getPath().contains(PATH_ACTIONS)) {
+            // Wellbeing reports that app actions have changed.
+            updateAllPackages();
+        } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
+            // Wellbeing reports that minimal device state or config is changed.
+            if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
+                return;
+            }
+
+            // Temporary bug fix for b/169771796. Wellbeing provides the layout configuration when
+            // minimal device is enabled. We always want to reload the configuration from Wellbeing
+            // since the layout configuration might have changed.
+            mContext.deleteDatabase(DB_NAME_MINIMAL_DEVICE);
+
+            final Bundle extras = new Bundle();
+            String dbFile;
+            if (isInMinimalDeviceMode()) {
+                dbFile = DB_NAME_MINIMAL_DEVICE;
+                extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
+                        mWellbeingProviderPkg + ".api");
+            } else {
+                dbFile = InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
+            }
+            LauncherSettings.Settings.call(mContext.getContentResolver(),
+                    LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
+                    dbFile, extras);
+        }
+    }
+
     public void setInTest(boolean inTest) {
         mIsInTest = inTest;
     }
 
-    protected void onWellbeingProviderChanged(Intent intent) {
-        if (DEBUG || mIsInTest) {
-            Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
-        }
-        restartObserver();
-    }
-
+    @WorkerThread
     private void restartObserver() {
         final ContentResolver resolver = mContext.getContentResolver();
         resolver.unregisterContentObserver(mContentObserver);
@@ -180,7 +188,7 @@
             Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
             if (mIsInTest) throw new RuntimeException(e);
         }
-        updateWellbeingData();
+        updateAllPackages();
     }
 
     @MainThread
@@ -213,74 +221,40 @@
         }
     }
 
-    private void updateWellbeingData() {
-        mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
-    }
-
-    private void updateLauncherModel(@NonNull final Context context) {
-        if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
-            reloadLauncherInNormalMode(context);
-            return;
-        }
-        runWithMinimalDeviceConfigs((bundle) -> {
-            if (bundle.getInt(EXTRA_MINIMAL_DEVICE_STATE, UNKNOWN_MINIMAL_DEVICE_STATE)
-                    == IN_MINIMAL_DEVICE) {
-                reloadLauncherInMinimalMode(context);
-            } else {
-                reloadLauncherInNormalMode(context);
-            }
-        });
-    }
-
-    private void reloadLauncherInNormalMode(@NonNull final Context context) {
-        LauncherSettings.Settings.call(context.getContentResolver(),
-                LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
-                InvariantDeviceProfile.INSTANCE.get(context).dbFile);
-    }
-
-    private void reloadLauncherInMinimalMode(@NonNull final Context context) {
-        final Bundle extras = new Bundle();
-        extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
-                mWellbeingProviderPkg + ".api");
-        LauncherSettings.Settings.call(context.getContentResolver(),
-                LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
-                DB_NAME_MINIMAL_DEVICE, extras);
-    }
-
     private Uri.Builder apiBuilder() {
         return new Uri.Builder()
                 .scheme(SCHEME_CONTENT)
                 .authority(mWellbeingProviderPkg + ".api");
     }
 
-    /**
-     * Fetch most up-to-date minimal device config.
-     */
     @WorkerThread
-    private void runWithMinimalDeviceConfigs(Consumer<Bundle> consumer) {
+    private boolean isInMinimalDeviceMode() {
         if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
-            return;
+            return false;
         }
         if (DEBUG || mIsInTest) {
-            Log.d(TAG, "runWithMinimalDeviceConfigs() called");
+            Log.d(TAG, "isInMinimalDeviceMode() called");
         }
         Preconditions.assertNonUiThread();
 
         final Uri contentUri = apiBuilder().build();
-        final Bundle remoteBundle;
         try (ContentProviderClient client = mContext.getContentResolver()
                 .acquireUnstableContentProviderClient(contentUri)) {
-            remoteBundle = client.call(
+            final Bundle remoteBundle = client == null ? null : client.call(
                     METHOD_GET_MINIMAL_DEVICE_CONFIG, null /* args */, null /* extras */);
-            consumer.accept(remoteBundle);
+            return remoteBundle != null
+                    && remoteBundle.getInt(EXTRA_MINIMAL_DEVICE_STATE,
+                    UNKNOWN_MINIMAL_DEVICE_STATE) == IN_MINIMAL_DEVICE;
         } catch (Exception e) {
             Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
             if (mIsInTest) throw new RuntimeException(e);
         }
-        if (DEBUG || mIsInTest) Log.i(TAG, "runWithMinimalDeviceConfigs(): finished");
+        if (DEBUG || mIsInTest) Log.i(TAG, "isInMinimalDeviceMode(): finished");
+        return false;
     }
 
-    private boolean updateActions(String... packageNames) {
+    @WorkerThread
+    private boolean updateActions(String[] packageNames) {
         if (packageNames.length == 0) {
             return true;
         }
@@ -343,68 +317,60 @@
         return true;
     }
 
-    private boolean handleMessage(Message msg) {
-        switch (msg.what) {
-            case MSG_PACKAGE_REMOVED: {
-                String packageName = (String) msg.obj;
-                mWorkerHandler.removeCallbacksAndMessages(packageName);
-                synchronized (mModelLock) {
-                    mPackageToActionId.remove(packageName);
-                }
-                return true;
-            }
-            case MSG_PACKAGE_ADDED: {
-                String packageName = (String) msg.obj;
-                mWorkerHandler.removeCallbacksAndMessages(packageName);
-                if (!updateActions(packageName)) {
-                    scheduleRefreshRetry(msg);
-                }
-                return true;
-            }
-
-            case MSG_FULL_REFRESH: {
-                // Remove all existing messages
-                mWorkerHandler.removeCallbacksAndMessages(null);
-                final String[] packageNames = mContext.getSystemService(LauncherApps.class)
-                        .getActivityList(null, Process.myUserHandle()).stream()
-                        .map(li -> li.getApplicationInfo().packageName).distinct()
-                        .toArray(String[]::new);
-                if (!updateActions(packageNames)) {
-                    scheduleRefreshRetry(msg);
-                }
-                return true;
-            }
+    @WorkerThread
+    private void updateActionsWithRetry(int retryCount, @Nullable String packageName) {
+        if (DEBUG || mIsInTest) {
+            Log.i(TAG,
+                    "updateActionsWithRetry(); retryCount: " + retryCount + ", package: "
+                            + packageName);
         }
-        return false;
-    }
+        String[] packageNames = TextUtils.isEmpty(packageName)
+                ? mContext.getSystemService(LauncherApps.class)
+                .getActivityList(null, Process.myUserHandle()).stream()
+                .map(li -> li.getApplicationInfo().packageName).distinct()
+                .toArray(String[]::new)
+                : new String[]{packageName};
 
-    private void scheduleRefreshRetry(Message originalMsg) {
-        int retryCount = originalMsg.arg1;
+        mWorkerHandler.removeCallbacksAndMessages(packageName);
+        if (updateActions(packageNames)) {
+            return;
+        }
         if (retryCount >= RETRY_TIMES_MS.length) {
             // To many retries, skip
             return;
         }
-
-        Message msg = Message.obtain(originalMsg);
-        msg.arg1 = retryCount + 1;
-        mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
+        mWorkerHandler.postDelayed(
+                () -> {
+                    if (DEBUG || mIsInTest) Log.i(TAG, "Retrying; attempt " + (retryCount + 1));
+                    updateActionsWithRetry(retryCount + 1, packageName);
+                },
+                packageName, RETRY_TIMES_MS[retryCount]);
     }
 
+    @WorkerThread
+    private void updateAllPackages() {
+        if (DEBUG || mIsInTest) Log.i(TAG, "updateAllPackages");
+        updateActionsWithRetry(0, null);
+    }
+
+    @WorkerThread
     private void onAppPackageChanged(Intent intent) {
         if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
-        Preconditions.assertUIThread();
+        Preconditions.assertNonUiThread();
 
         final String packageName = intent.getData().getSchemeSpecificPart();
         if (packageName == null || packageName.length() == 0) {
             // they sent us a bad intent
             return;
         }
-
         final String action = intent.getAction();
         if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
-            Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
+            mWorkerHandler.removeCallbacksAndMessages(packageName);
+            synchronized (mModelLock) {
+                mPackageToActionId.remove(packageName);
+            }
         } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
-            Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
+            updateActionsWithRetry(0, packageName);
         }
     }
 
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
new file mode 100644
index 0000000..3d891e8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -0,0 +1,107 @@
+/*
+ * 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 android.app.prediction.AppTarget;
+import android.content.ComponentName;
+import android.text.TextUtils;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/** Task to update model as a result of predicted widgets update */
+public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask {
+    private final PredictorState mPredictorState;
+    private final List<AppTarget> mTargets;
+
+    WidgetsPredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets) {
+        mPredictorState = predictorState;
+        mTargets = targets;
+    }
+
+    /**
+     * Uses the app predication result to infer widgets that the user may want to use.
+     *
+     * <p>The algorithm uses the app prediction ranking to create a widgets ranking which only
+     * includes one widget per app and excludes widgets that have already been added to the
+     * workspace.
+     */
+    @Override
+    public void execute(LauncherAppState appState, BgDataModel dataModel, AllAppsList apps) {
+        Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
+                widget -> new ComponentKey(widget.providerName, widget.user)).collect(
+                Collectors.toSet());
+        Map<PackageUserKey, List<WidgetItem>> allWidgets =
+                dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
+
+        FixedContainerItems fixedContainerItems = mPredictorState.items;
+        fixedContainerItems.items.clear();
+
+        if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
+            for (AppTarget app : mTargets) {
+                PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(),
+                        app.getUser());
+                if (allWidgets.containsKey(packageUserKey)) {
+                    List<WidgetItem> notAddedWidgets = allWidgets.get(packageUserKey).stream()
+                            .filter(item ->
+                                    !widgetsInWorkspace.contains(
+                                            new ComponentKey(item.componentName, item.user)))
+                            .collect(Collectors.toList());
+                    if (notAddedWidgets.size() > 0) {
+                        // Even an apps have more than one widgets, we only include one widget.
+                        fixedContainerItems.items.add(
+                                new PendingAddWidgetInfo(
+                                        notAddedWidgets.get(0).widgetInfo,
+                                        CONTAINER_WIDGETS_PREDICTION));
+                    }
+                }
+            }
+        } else {
+            Map<ComponentKey, WidgetItem> widgetItems =
+                    allWidgets.values().stream().flatMap(List::stream)
+                            .collect(Collectors.toMap(widget -> (ComponentKey) widget,
+                                    widget -> widget));
+            for (AppTarget app : mTargets) {
+                if (TextUtils.isEmpty(app.getClassName())) {
+                    continue;
+                }
+                ComponentKey targetWidget = new ComponentKey(
+                        new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
+                if (widgetItems.containsKey(targetWidget)) {
+                    fixedContainerItems.items.add(
+                            new PendingAddWidgetInfo(widgetItems.get(
+                                    targetWidget).widgetInfo,
+                                    CONTAINER_WIDGETS_PREDICTION));
+                }
+            }
+        }
+        bindExtraContainerItems(fixedContainerItems);
+
+        // Don't store widgets prediction to disk because it is not used frequently.
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
index e302b4f..4d7cc85 100644
--- a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
+++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.proxy;
 
 import android.app.Activity;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender.SendIntentException;
@@ -48,19 +49,20 @@
             return;
         }
 
-        if (mParams.intent != null) {
-            startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
-            return;
-        } else if (mParams.intentSender != null) {
-            try {
+        try {
+            if (mParams.intent != null) {
+                startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
+                return;
+            } else if (mParams.intentSender != null) {
                 startIntentSenderForResult(mParams.intentSender, mParams.requestCode,
                         mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues,
                         mParams.extraFlags,
                         mParams.options);
                 return;
-            } catch (SendIntentException e) {
-                mParams.deliverResult(this, RESULT_CANCELED, null);
             }
+        } catch (NullPointerException | ActivityNotFoundException | SecurityException
+                | SendIntentException e) {
+            mParams.deliverResult(this, RESULT_CANCELED, null);
         }
         finishAndRemoveTask();
     }
diff --git a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
index 13501a4..1f268cc 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/BackButtonAlphaHandler.java
@@ -18,6 +18,7 @@
 
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.AnimatedFloat.VALUE;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
@@ -30,7 +31,7 @@
 import com.android.quickstep.SystemUiProxy;
 
 /**
- * State handler for animating back button alpha
+ * State handler for animating back button alpha in two-button nav mode.
  */
 public class BackButtonAlphaHandler implements StateHandler<LauncherState> {
 
@@ -47,18 +48,11 @@
     @Override
     public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
             PendingAnimation animation) {
-        if (config.onlyPlayAtomicComponent()) {
+        if (SysUINavigationMode.getMode(mLauncher) != TWO_BUTTONS) {
             return;
         }
 
-        if (!SysUINavigationMode.getMode(mLauncher).hasGestures) {
-            // If the nav mode is not gestural, then force back button alpha to be 1
-            UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
-                    BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, 1f, true /* animate */);
-            return;
-        }
-
-        mBackAlpha.value = SystemUiProxy.INSTANCE.get(mLauncher).getLastBackButtonAlpha();
+        mBackAlpha.value = SystemUiProxy.INSTANCE.get(mLauncher).getLastNavButtonAlpha();
         animation.setFloat(mBackAlpha, VALUE,
                 mLauncher.shouldBackButtonBeHidden(toState) ? 0 : 1, LINEAR);
     }
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index fe8f0c6..370fb8e 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -24,8 +24,12 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.os.IBinder;
+import android.os.SystemProperties;
 import android.util.FloatProperty;
+import android.view.CrossWindowBlurListeners;
+import android.view.SurfaceControl;
 import android.view.View;
+import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
 
 import com.android.launcher3.BaseActivity;
@@ -37,11 +41,10 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.systemui.shared.system.BlurUtils;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SurfaceControlCompat;
-import com.android.systemui.shared.system.TransactionCompat;
 import com.android.systemui.shared.system.WallpaperManagerCompat;
 
+import java.util.function.Consumer;
+
 /**
  * Controls blur and wallpaper zoom, for the Launcher surface only.
  */
@@ -91,23 +94,37 @@
                 @Override
                 public void onDraw() {
                     View view = mLauncher.getDragLayer();
-                    setSurface(new SurfaceControlCompat(view));
+                    ViewRootImpl viewRootImpl = view.getViewRootImpl();
+                    setSurface(viewRootImpl != null ? viewRootImpl.getSurfaceControl() : null);
                     view.post(() -> view.getViewTreeObserver().removeOnDrawListener(this));
                 }
             };
 
+    private final Consumer<Boolean> mCrossWindowBlurListener = new Consumer<Boolean>() {
+        @Override
+        public void accept(Boolean enabled) {
+            mCrossWindowBlursEnabled = enabled;
+            dispatchTransactionSurface(mDepth);
+        }
+    };
+
     private final Launcher mLauncher;
     /**
      * Blur radius when completely zoomed out, in pixels.
      */
     private int mMaxBlurRadius;
+    private boolean mCrossWindowBlursEnabled;
     private WallpaperManagerCompat mWallpaperManager;
-    private SurfaceControlCompat mSurface;
+    private SurfaceControl mSurface;
     /**
      * Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
      * @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
      */
     private float mDepth;
+    /**
+     * If we're launching and app and should not be blurring the screen for performance reasons.
+     */
+    private boolean mBlurDisabledForAppLaunch;
 
     // Workaround for animating the depth when multiwindow mode changes.
     private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
@@ -123,6 +140,7 @@
             mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
             mWallpaperManager = new WallpaperManagerCompat(mLauncher);
         }
+
         if (mLauncher.getRootView() != null && mOnAttachListener == null) {
             mOnAttachListener = new View.OnAttachStateChangeListener() {
                 @Override
@@ -132,13 +150,20 @@
                     if (windowToken != null) {
                         mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
                     }
+                    CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
+                            mCrossWindowBlurListener);
                 }
 
                 @Override
                 public void onViewDetachedFromWindow(View view) {
+                    CrossWindowBlurListeners.getInstance().removeListener(mCrossWindowBlurListener);
                 }
             };
             mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener);
+            if (mLauncher.getRootView().isAttachedToWindow()) {
+                CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(),
+                        mCrossWindowBlurListener);
+            }
         }
     }
 
@@ -157,22 +182,17 @@
     /**
      * Sets the specified app target surface to apply the blur to.
      */
-    public void setSurfaceToApp(RemoteAnimationTargetCompat target) {
-        if (target != null) {
-            setSurface(target.leash);
-        } else {
-            setActivityStarted(mLauncher.isStarted());
+    public void 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;
         }
-    }
 
-    private void setSurface(SurfaceControlCompat surface) {
         if (mSurface != surface) {
             mSurface = surface;
             if (surface != null) {
-                setDepth(mDepth);
-            } else {
-                // If there is no surface, then reset the ratio
-                setDepth(0f);
+                dispatchTransactionSurface(mDepth);
             }
         }
     }
@@ -186,15 +206,15 @@
         float toDepth = toState.getDepth(mLauncher);
         if (Float.compare(mDepth, toDepth) != 0) {
             setDepth(toDepth);
+        } else if (toState == LauncherState.OVERVIEW) {
+            dispatchTransactionSurface(mDepth);
         }
     }
 
     @Override
     public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
             PendingAnimation animation) {
-        if (mSurface == null
-                || config.onlyPlayAtomicComponent()
-                || config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER)
+        if (config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER)
                 || mIgnoreStateChangesDuringMultiWindowAnimation) {
             return;
         }
@@ -205,6 +225,19 @@
         }
     }
 
+    /**
+     * If we're launching an app from the home screen.
+     */
+    public void setIsInLaunchTransition(boolean inLaunchTransition) {
+        boolean blurEnabled = SystemProperties.getBoolean("ro.launcher.blur.appLaunch", true);
+        mBlurDisabledForAppLaunch = inLaunchTransition && !blurEnabled;
+        if (!inLaunchTransition) {
+            // Reset depth at the end of the launch animation, so the wallpaper won't be
+            // zoomed out if an app crashes.
+            setDepth(0f);
+        }
+    }
+
     private void setDepth(float depth) {
         depth = Utilities.boundToRange(depth, 0, 1);
         // Round out the depth to dedupe frequent, non-perceptable updates
@@ -213,32 +246,36 @@
         if (Float.compare(mDepth, depthF) == 0) {
             return;
         }
+        if (dispatchTransactionSurface(depthF)) {
+            mDepth = depthF;
+        }
+    }
 
+    private boolean dispatchTransactionSurface(float depth) {
         boolean supportsBlur = BlurUtils.supportsBlursOnWindows();
         if (supportsBlur && (mSurface == null || !mSurface.isValid())) {
-            return;
+            return false;
         }
-        mDepth = depthF;
         ensureDependencies();
         IBinder windowToken = mLauncher.getRootView().getWindowToken();
         if (windowToken != null) {
-            mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
+            mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
         }
 
         if (supportsBlur) {
-            final int blur;
-            if (mLauncher.isInState(LauncherState.ALL_APPS) && mDepth == 1) {
-                // All apps has a solid background. We don't need to draw blurs after it's fully
-                // visible. This will take us out of GPU composition, saving battery and increasing
-                // performance.
-                blur = 0;
-            } else {
-                blur = (int) (mDepth * mMaxBlurRadius);
-            }
-            new TransactionCompat()
+            // 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;
+
+            int blur = opaque || isOverview || !mCrossWindowBlursEnabled
+                    || mBlurDisabledForAppLaunch ? 0 : (int) (depth * mMaxBlurRadius);
+            new SurfaceControl.Transaction()
                     .setBackgroundBlurRadius(mSurface, blur)
+                    .setOpaque(mSurface, opaque)
                     .apply();
         }
+        return true;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
new file mode 100644
index 0000000..540f748
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
@@ -0,0 +1,80 @@
+/*
+ * 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/ImeBarView.java b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
new file mode 100644
index 0000000..287caab
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
@@ -0,0 +1,77 @@
+/*
+ * 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
new file mode 100644
index 0000000..c2d107c
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -0,0 +1,275 @@
+/*
+ * 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.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepTransitionManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.states.StateAnimationConfig;
+
+
+/**
+ * A data source which integrates with a Launcher instance
+ * TODO: Rename to have Launcher prefix
+ */
+
+public class LauncherTaskbarUIController extends TaskbarUIController {
+
+    private final BaseQuickstepLauncher mLauncher;
+    private final TaskbarStateHandler mTaskbarStateHandler;
+    private final TaskbarAnimationController mTaskbarAnimationController;
+    private final TaskbarHotseatController mHotseatController;
+
+    private final TaskbarActivityContext mContext;
+    final TaskbarDragLayer mTaskbarDragLayer;
+    final TaskbarView mTaskbarView;
+
+    private @Nullable Animator mAnimator;
+    private boolean mIsAnimatingToLauncher;
+
+    public LauncherTaskbarUIController(
+            BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
+        mContext = context;
+        mTaskbarDragLayer = context.getDragLayer();
+        mTaskbarView = mTaskbarDragLayer.findViewById(R.id.taskbar_view);
+
+        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();
+        mLauncher.setTaskbarUIController(this);
+    }
+
+    @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);
+        mLauncher.setTaskbarUIController(null);
+    }
+
+    @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);
+            }
+        };
+    }
+
+    /**
+     * 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;
+            }
+        });
+        mAnimator.start();
+    }
+
+    /**
+     * Create Taskbar animation when going from an app to Launcher.
+     * @param toState If known, the state we will end up in when reaching Launcher.
+     */
+    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();
+    }
+
+    /**
+     * @param ev MotionEvent in screen coordinates.
+     * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
+     */
+    public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
+        return mTaskbarView.isEventOverAnyItem(ev);
+    }
+
+    public boolean isDraggingItem() {
+        return mTaskbarView.isDraggingItem();
+    }
+
+    /**
+     * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
+     */
+    @Override
+    public void alignRealHotseatWithTaskbar() {
+        Rect hotseatBounds = new Rect();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        int hotseatHeight = grid.workspacePadding.bottom + grid.taskbarSize;
+        int taskbarOffset = mLauncher.getHotseat().getTaskbarOffsetY();
+        int hotseatTopDiff = hotseatHeight - grid.taskbarSize - taskbarOffset;
+        int hotseatBottomDiff = taskbarOffset;
+
+        RectF hotseatBoundsF = mTaskbarView.getHotseatBounds();
+        Utilities.scaleRectFAboutPivot(hotseatBoundsF, getTaskbarScaleOnHome(),
+                mTaskbarView.getPivotX(), mTaskbarView.getPivotY());
+        hotseatBoundsF.round(hotseatBounds);
+        mLauncher.getHotseat().setPadding(hotseatBounds.left,
+                hotseatBounds.top + hotseatTopDiff,
+                mTaskbarView.getWidth() - hotseatBounds.right,
+                mTaskbarView.getHeight() - hotseatBounds.bottom + hotseatBottomDiff);
+    }
+
+    /**
+     * Returns the ratio of the taskbar icon size on home vs in an app.
+     */
+    public float getTaskbarScaleOnHome() {
+        DeviceProfile inAppDp = mContext.getDeviceProfile();
+        DeviceProfile onHomeDp = mLauncher.getDeviceProfile();
+        return (float) onHomeDp.cellWidthPx / inAppDp.cellWidthPx;
+    }
+
+    void setTaskbarViewVisible(boolean isVisible) {
+        mTaskbarView.setIconsVisibility(isVisible);
+        mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f);
+    }
+
+    /**
+     * Contains methods that TaskbarAnimationController can call to interface with
+     * TaskbarController.
+     */
+    protected interface TaskbarAnimationControllerCallbacks {
+        void updateTaskbarBackgroundAlpha(float alpha);
+        void updateTaskbarVisibilityAlpha(float alpha);
+        void updateImeBarVisibilityAlpha(float alpha);
+        void updateTaskbarScale(float scale);
+        void updateTaskbarTranslationY(float translationY);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
new file mode 100644
index 0000000..4ba0ee0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -0,0 +1,333 @@
+/*
+ * 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.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 com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+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.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+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.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.Themes;
+import com.android.launcher3.util.TraceHelper;
+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.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+/**
+ * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
+ * that are used by both Launcher and Taskbar (such as Folder) to reference a generic
+ * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer.
+ */
+public class TaskbarActivityContext extends ContextThemeWrapper implements ActivityContext {
+
+    private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
+            SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
+    private static final String TAG = "TaskbarActivityContext";
+
+    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 WindowManager mWindowManager;
+    private WindowManager.LayoutParams mWindowLayoutParams;
+
+    private final SysUINavigationMode.Mode mNavMode;
+    private final TaskbarNavButtonController mNavButtonController;
+
+    private final boolean mIsSafeModeEnabled;
+
+    @NonNull
+    private TaskbarUIController mUIController = TaskbarUIController.DEFAULT;
+
+    private final View.OnClickListener mOnTaskbarIconClickListener;
+    private final View.OnLongClickListener mOnTaskbarIconLongClickListener;
+
+    public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
+            TaskbarNavButtonController buttonController) {
+        super(windowContext, Themes.getActivityThemeRes(windowContext));
+        mDeviceProfile = dp;
+        mNavButtonController = buttonController;
+        mNavMode = SysUINavigationMode.getMode(windowContext);
+        mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
+                () -> getPackageManager().isSafeMode());
+
+        mOnTaskbarIconLongClickListener =
+                new TaskbarDragController(this)::startSystemDragOnLongClick;
+        mOnTaskbarIconClickListener = this::onTaskbarIconClicked;
+
+        float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
+        float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
+        mDeviceProfile.updateIconSize(iconScale, getResources());
+
+        mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
+        mDragLayer = (TaskbarDragLayer) mLayoutInflater
+                .inflate(R.layout.taskbar, null, false);
+        mIconController = new TaskbarIconController(this, mDragLayer);
+        mDragController = new MyDragController(this);
+
+        Display display = windowContext.getDisplay();
+        Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
+                ? windowContext.getApplicationContext()
+                : windowContext.getApplicationContext().createDisplayContext(display);
+        mWindowManager = c.getSystemService(WindowManager.class);
+    }
+
+    public void init() {
+        mWindowLayoutParams = new WindowManager.LayoutParams(
+                MATCH_PARENT,
+                mDeviceProfile.taskbarSize,
+                TYPE_APPLICATION_OVERLAY,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                PixelFormat.TRANSLUCENT);
+        mWindowLayoutParams.setTitle(WINDOW_TITLE);
+        mWindowLayoutParams.packageName = getPackageName();
+        mWindowLayoutParams.gravity = Gravity.BOTTOM;
+        mWindowLayoutParams.setFitInsetsTypes(0);
+        mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+        mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mWindowLayoutParams.setSystemApplicationOverlay(true);
+
+        WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
+        wmWrapper.setProvidesInsetsTypes(
+                mWindowLayoutParams,
+                new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
+        );
+
+        mIconController.init(mOnTaskbarIconClickListener, mOnTaskbarIconLongClickListener);
+        mWindowManager.addView(mDragLayer, mWindowLayoutParams);
+    }
+
+    /**
+     * Updates the TaskbarContainer height (pass deviceProfile.taskbarSize to reset).
+     */
+    public void setTaskbarWindowHeight(int height) {
+        if (mWindowLayoutParams.height == height) {
+            return;
+        }
+        mWindowLayoutParams.height = height;
+        mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+    }
+
+    public boolean canShowNavButtons() {
+        return ENABLE_THREE_BUTTON_TASKBAR && mNavMode == Mode.THREE_BUTTONS;
+    }
+
+    @Override
+    public LayoutInflater getLayoutInflater() {
+        return mLayoutInflater;
+    }
+
+    @Override
+    public TaskbarDragLayer getDragLayer() {
+        return mDragLayer;
+    }
+
+    @Override
+    public DeviceProfile getDeviceProfile() {
+        return mDeviceProfile;
+    }
+
+    @Override
+    public Rect getFolderBoundingBox() {
+        return mDragLayer.getFolderBoundingBox();
+    }
+
+    @Override
+    public DragController getDragController() {
+        return mDragController;
+    }
+
+    /**
+     * Sets a new data-source for this taskbar instance
+     */
+    public void setUIController(@NonNull TaskbarUIController uiController) {
+        mUIController.onDestroy();
+        mUIController = uiController;
+        mIconController.setUIController(mUIController);
+        mUIController.onCreate();
+    }
+
+    /**
+     * Called when this instance of taskbar is no longer needed
+     */
+    public void onDestroy() {
+        setUIController(TaskbarUIController.DEFAULT);
+        mIconController.onDestroy();
+        mWindowManager.removeViewImmediate(mDragLayer);
+    }
+
+    void onNavigationButtonClick(@TaskbarButton int buttonType) {
+        mNavButtonController.onButtonClick(buttonType);
+    }
+
+    /**
+     * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
+     */
+    public void setImeIsVisible(boolean isImeVisible) {
+        mIconController.setImeIsVisible(isImeVisible);
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
+     */
+    protected void setTaskbarWindowFullscreen(boolean fullscreen) {
+        setTaskbarWindowHeight(fullscreen ? MATCH_PARENT : getDeviceProfile().taskbarSize);
+    }
+
+    protected void onTaskbarIconClicked(View view) {
+        Object tag = view.getTag();
+        if (tag instanceof Task) {
+            Task task = (Task) tag;
+            ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
+                    ActivityOptions.makeBasic());
+        } else if (tag instanceof FolderInfo) {
+            FolderIcon folderIcon = (FolderIcon) view;
+            Folder folder = folderIcon.getFolder();
+            setTaskbarWindowFullscreen(true);
+
+            getDragLayer().post(() -> {
+                folder.animateOpen();
+
+                folder.iterateOverItems((itemInfo, itemView) -> {
+                    itemView.setOnClickListener(mOnTaskbarIconClickListener);
+                    itemView.setOnLongClickListener(mOnTaskbarIconLongClickListener);
+                    // To play haptic when dragging, like other Taskbar items do.
+                    itemView.setHapticFeedbackEnabled(true);
+                    return false;
+                });
+            });
+        } else if (tag instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
+            if (!(info.isDisabled() && ItemClickHandler.handleDisabledItemClicked(info, this))) {
+                Intent intent = new Intent(info.getIntent())
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                try {
+                    if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
+                        Toast.makeText(this, R.string.safemode_shortcut_error,
+                                Toast.LENGTH_SHORT).show();
+                    } else  if (info.isPromise()) {
+                        intent = new PackageManagerHelper(this)
+                                .getMarketIntent(info.getTargetPackage())
+                                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        startActivity(intent);
+
+                    } else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                        String id = info.getDeepShortcutId();
+                        String packageName = intent.getPackage();
+                        getSystemService(LauncherApps.class)
+                                .startShortcut(packageName, id, null, null, info.user);
+                    } else if (info.user.equals(Process.myUserHandle())) {
+                        startActivity(intent);
+                    } else {
+                        getSystemService(LauncherApps.class).startMainActivity(
+                                intent.getComponent(), info.user, intent.getSourceBounds(), null);
+                    }
+                } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+                    Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
+                            .show();
+                    Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
+                }
+            }
+        } else {
+            Log.e(TAG, "Unknown type clicked: " + tag);
+        }
+
+        AbstractFloatingView.closeAllOpenViews(this);
+    }
+
+    private static class MyDragController extends DragController<TaskbarActivityContext> {
+        MyDragController(TaskbarActivityContext activity) {
+            super(activity);
+        }
+
+        @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;
+        }
+
+        @Override
+        protected void exitDrag() {
+        }
+
+        @Override
+        protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+            return null;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
new file mode 100644
index 0000000..e20ddf8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
@@ -0,0 +1,151 @@
+/*
+ * 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/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
new file mode 100644
index 0000000..ee44927
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -0,0 +1,143 @@
+/*
+ * 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.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.os.UserHandle;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ClipDescriptionCompat;
+import com.android.systemui.shared.system.LauncherAppsCompat;
+
+/**
+ * Handles long click on Taskbar items to start a system drag and drop operation.
+ */
+public class TaskbarDragController {
+
+    private final Context mContext;
+    private final int mDragIconSize;
+
+    public TaskbarDragController(Context context) {
+        mContext = context;
+        Resources resources = mContext.getResources();
+        mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
+    }
+
+    /**
+     * 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) {
+        if (!(view instanceof BubbleTextView)) {
+            return false;
+        }
+
+        BubbleTextView btv = (BubbleTextView) view;
+        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
+            @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);
+            }
+
+            @Override
+            public void onDrawShadow(Canvas canvas) {
+                canvas.save();
+                float scale = (float) mDragIconSize / btv.getIconSize();
+                canvas.scale(scale, scale);
+                btv.getIcon().draw(canvas);
+                canvas.restore();
+            }
+        };
+
+        Object tag = view.getTag();
+        ClipDescription clipDescription = null;
+        Intent intent = null;
+        if (tag instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
+            LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
+            clipDescription = new ClipDescription(item.title,
+                    new String[] {
+                            item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                                    ? ClipDescriptionCompat.MIMETYPE_APPLICATION_SHORTCUT
+                                    : ClipDescriptionCompat.MIMETYPE_APPLICATION_ACTIVITY
+                    });
+            intent = new Intent();
+            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
+                intent.putExtra(Intent.EXTRA_SHORTCUT_ID, item.getDeepShortcutId());
+            } else {
+                intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
+                        LauncherAppsCompat.getMainActivityLaunchIntent(launcherApps,
+                                item.getIntent().getComponent(), null, item.user));
+            }
+            intent.putExtra(Intent.EXTRA_USER, item.user);
+        } else if (tag instanceof Task) {
+            Task task = (Task) tag;
+            clipDescription = new ClipDescription(task.titleDescription,
+                    new String[] {
+                            ClipDescriptionCompat.MIMETYPE_APPLICATION_TASK
+                    });
+            intent = new Intent();
+            intent.putExtra(ClipDescriptionCompat.EXTRA_TASK_ID, task.key.id);
+            intent.putExtra(Intent.EXTRA_USER, UserHandle.of(task.key.userId));
+        }
+
+        if (clipDescription != null && intent != null) {
+            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);
+        }
+        return false;
+    }
+
+    /**
+     * Hide the original Taskbar item while it is being dragged.
+     */
+    private View.OnDragListener getDraggedViewDragListener() {
+        return (view, dragEvent) -> {
+            switch (dragEvent.getAction()) {
+                case DragEvent.ACTION_DRAG_STARTED:
+                    view.setVisibility(INVISIBLE);
+                    return true;
+                case DragEvent.ACTION_DRAG_ENDED:
+                    view.setVisibility(VISIBLE);
+                    view.setOnDragListener(null);
+                    return true;
+            }
+            return false;
+        };
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
new file mode 100644
index 0000000..45ec911
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.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 android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInsetsListener;
+
+/**
+ * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
+ */
+public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
+
+    private final int mFolderMargin;
+    private final Paint mTaskbarBackgroundPaint;
+
+    private TaskbarIconController.Callbacks mControllerCallbacks;
+    private TaskbarView mTaskbarView;
+
+    private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
+
+    public TaskbarDragLayer(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    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));
+        recreateControllers();
+    }
+
+    @Override
+    public void recreateControllers() {
+        mControllers = new TouchController[0];
+    }
+
+    public void init(TaskbarIconController.Callbacks callbacks, TaskbarView taskbarView) {
+        mControllerCallbacks = callbacks;
+        mTaskbarView = taskbarView;
+    }
+
+    private void onComputeTaskbarInsets(InsetsInfo insetsInfo) {
+        if (mControllerCallbacks != null) {
+            mControllerCallbacks.updateInsetsTouchability(insetsInfo);
+        }
+    }
+
+    protected void onDestroy() {
+        ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
+                mTaskbarInsetsComputer);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        onDestroy();
+    }
+
+    @Override
+    protected boolean canFindActiveController() {
+        // Unlike super class, we want to be able to find controllers when touches occur in the
+        // gesture area. For example, this allows Folder to close itself when touching the Taskbar.
+        return true;
+    }
+
+    public void updateImeBarVisibilityAlpha(float alpha) {
+        if (mControllerCallbacks != null) {
+            mControllerCallbacks.updateImeBarVisibilityAlpha(alpha);
+        }
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        if (mControllerCallbacks != null) {
+            mControllerCallbacks.onDragLayerViewRemoved();
+        }
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        canvas.drawRect(0, canvas.getHeight() - mTaskbarView.getHeight(), canvas.getWidth(),
+                canvas.getHeight(), mTaskbarBackgroundPaint);
+        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.
+     */
+    protected void setTaskbarBackgroundAlpha(float alpha) {
+        mTaskbarBackgroundPaint.setAlpha((int) (alpha * 255));
+        invalidate();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
new file mode 100644
index 0000000..91cf7ef
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.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.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
new file mode 100644
index 0000000..683a5b9
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarIconController.java
@@ -0,0 +1,163 @@
+/*
+ * 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/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
new file mode 100644
index 0000000..d026bfb
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -0,0 +1,169 @@
+/*
+ * 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.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+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.Context;
+import android.hardware.display.DisplayManager;
+import android.inputmethodservice.InputMethodService;
+import android.view.Display;
+
+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.util.DisplayController;
+import com.android.launcher3.util.DisplayController.Info;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.TouchInteractionService;
+
+/**
+ * Class to manager taskbar lifecycle
+ */
+public class TaskbarManager implements DisplayController.DisplayInfoChangeListener,
+        SysUINavigationMode.NavigationModeChangeListener {
+
+    private final Context mContext;
+    private final DisplayController mDisplayController;
+    private final SysUINavigationMode mSysUINavigationMode;
+    private final TaskbarNavButtonController mNavButtonController;
+
+    private TaskbarActivityContext mTaskbarActivityContext;
+    private BaseQuickstepLauncher mLauncher;
+
+    private static final int CHANGE_FLAGS =
+            CHANGE_ACTIVE_SCREEN | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
+
+    private boolean mUserUnlocked = false;
+
+    public TaskbarManager(TouchInteractionService service) {
+        mDisplayController = DisplayController.INSTANCE.get(service);
+        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);
+
+        mDisplayController.addChangeListener(this);
+        mSysUINavigationMode.addModeChangeListener(this);
+        recreateTaskbar();
+    }
+
+    @Override
+    public void onNavigationModeChanged(Mode newMode) {
+        recreateTaskbar();
+    }
+
+    @Override
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        if ((flags & CHANGE_FLAGS) != 0) {
+            recreateTaskbar();
+        }
+    }
+
+    private void destroyExistingTaskbar() {
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onDestroy();
+            mTaskbarActivityContext = null;
+        }
+    }
+
+    /**
+     * Called when the user is unlocked
+     */
+    public void onUserUnlocked() {
+        mUserUnlocked = true;
+        recreateTaskbar();
+    }
+
+    /**
+     * Sets or clears a launcher to act as taskbar callback
+     */
+    public void setLauncher(@Nullable BaseQuickstepLauncher launcher) {
+        mLauncher = launcher;
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.setUIController(mLauncher == null
+                    ? TaskbarUIController.DEFAULT
+                    : new LauncherTaskbarUIController(launcher, mTaskbarActivityContext));
+        }
+    }
+
+    private void recreateTaskbar() {
+        destroyExistingTaskbar();
+        if (!FeatureFlags.ENABLE_TASKBAR.get()) {
+            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.setUIController(
+                    new LauncherTaskbarUIController(mLauncher, mTaskbarActivityContext));
+        }
+    }
+
+    /**
+     * 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;
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.setImeIsVisible(isImeVisible);
+        }
+    }
+
+    /**
+     * 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}
+     */
+    public void updateImeStatus(int displayId, int vis, int backDisposition,
+            boolean showImeSwitcher) {
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.updateImeStatus(displayId, vis, showImeSwitcher);
+        }
+    }
+
+    /**
+     * Called when the manager is no longer needed
+     */
+    public void destroy() {
+        destroyExistingTaskbar();
+        mDisplayController.removeChangeListener(this);
+        mSysUINavigationMode.removeModeChangeListener(this);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
new file mode 100644
index 0000000..3b5afad
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -0,0 +1,100 @@
+/*
+ * 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 android.view.Display.DEFAULT_DISPLAY;
+
+import android.content.Intent;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.annotation.IntDef;
+
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TouchInteractionService;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * 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 {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            BUTTON_BACK,
+            BUTTON_HOME,
+            BUTTON_RECENTS,
+            BUTTON_IME_SWITCH
+    })
+
+    public @interface TaskbarButton {}
+
+    static final int BUTTON_BACK = 1;
+    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;
+
+    private final TouchInteractionService mService;
+
+    public TaskbarNavButtonController(TouchInteractionService service) {
+        mService = service;
+    }
+
+    public void onButtonClick(@TaskbarButton int buttonType) {
+        switch (buttonType) {
+            case BUTTON_BACK:
+                executeBack();
+                break;
+            case BUTTON_HOME:
+                navigateHome();
+                break;
+            case BUTTON_RECENTS:
+                navigateToOverview();;
+                break;
+            case BUTTON_IME_SWITCH:
+                showIMESwitcher();
+                break;
+        }
+    }
+
+    private void navigateHome() {
+        mService.startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    private void navigateToOverview() {
+        mService.getOverviewCommandHelper()
+                .addCommand(OverviewCommandHelper.TYPE_SHOW);
+    }
+
+    private void executeBack() {
+        SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
+    }
+
+    private void showIMESwitcher() {
+        mService.getSystemService(InputMethodManager.class)
+                .showInputMethodPickerFromSystem(true /* showAuxiliarySubtypes */,
+                        DEFAULT_DISPLAY);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
new file mode 100644
index 0000000..a701aae
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -0,0 +1,75 @@
+/*
+ * 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
new file mode 100644
index 0000000..50adead
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.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.taskbar;
+
+/**
+ * Base class for providing different taskbar UI
+ */
+public class TaskbarUIController {
+
+    public static final TaskbarUIController DEFAULT = new TaskbarUIController();
+
+    /**
+     * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
+     */
+    public void alignRealHotseatWithTaskbar() { }
+
+    protected void onCreate() { }
+
+    protected void onDestroy() { }
+
+    protected boolean isTaskbarTouchable() {
+        return true;
+    }
+
+    protected void onImeVisible(TaskbarDragLayer container, boolean isVisible) {
+        container.updateImeBarVisibilityAlpha(isVisible ? 1 : 0);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
new file mode 100644
index 0000000..c6573a6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -0,0 +1,439 @@
+/*
+ * 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.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 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.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 {
+
+    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 int mItemMarginLeftRight;
+
+    private final TaskbarActivityContext mActivityContext;
+
+    // Initialized in TaskbarController constructor.
+    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);
+    }
+
+    public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mActivityContext = ActivityContext.lookupContext(context);
+
+        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();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mSystemButtonContainer = findViewById(R.id.system_button_layout);
+        mHotseatIconsContainer = findViewById(R.id.hotseat_icons_layout);
+    }
+
+    protected void construct(OnClickListener clickListener, OnLongClickListener longClickListener,
+                ButtonProvider buttonProvider) {
+        mIconClickListener = clickListener;
+        mIconLongClickListener = longClickListener;
+        mButtonProvider = buttonProvider;
+
+        if (mActivityContext.canShowNavButtons()) {
+            createNavButtons();
+        } else {
+            mSystemButtonContainer.setVisibility(GONE);
+        }
+
+        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());
+        }
+    }
+
+    /**
+     * Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos.
+     */
+    protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+        for (int i = 0; i < hotseatItemInfos.length; i++) {
+            ItemInfo hotseatItemInfo = hotseatItemInfos[
+                    !mIsRtl ? i : hotseatItemInfos.length - i - 1];
+            View hotseatView = mHotseatIconsContainer.getChildAt(i);
+
+            // 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()) {
+                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);
+                if (isFolder) {
+                    FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
+                    FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
+                            mActivityContext, this, folderInfo);
+                    folderIcon.setTextVisible(false);
+                    hotseatView = folderIcon;
+                } 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);
+            }
+
+            // 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);
+            }
+            updateHotseatItemVisibility(hotseatView);
+        }
+    }
+
+    protected void updateHotseatItemsVisibility() {
+        for (int i = mHotseatIconsContainer.getChildCount() - 1; i >= 0; i--) {
+            updateHotseatItemVisibility(mHotseatIconsContainer.getChildAt(i));
+        }
+    }
+
+    private void updateHotseatItemVisibility(View hotseatView) {
+        hotseatView.setVisibility(
+                hotseatView.getTag() != null ? VISIBLE : (mAreHolesAllowed ? INVISIBLE : GONE));
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (!mTouchEnabled) {
+            return true;
+        }
+        return super.dispatchTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean handled = delegateTouchIfNecessary(event);
+        return super.onTouchEvent(event) || handled;
+    }
+
+    public void setTouchesEnabled(boolean touchEnabled) {
+        this.mTouchEnabled = touchEnabled;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Add back/home/recents buttons into a single ViewGroup that will be inserted at
+     * {@param navButtonStartIndex}
+     */
+    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;
+        }
+        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();
+        }
+    }
+
+    // FolderIconParent implemented methods.
+
+    @Override
+    public void drawFolderLeaveBehindForIcon(FolderIcon child) {
+        mLeaveBehindFolderIcon = child;
+        invalidate();
+    }
+
+    @Override
+    public void clearFolderLeaveBehind(FolderIcon child) {
+        mLeaveBehindFolderIcon = null;
+        invalidate();
+    }
+
+    // End FolderIconParent implemented methods.
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mLeaveBehindFolderIcon != null) {
+            canvas.save();
+            canvas.translate(mLeaveBehindFolderIcon.getLeft(), mLeaveBehindFolderIcon.getTop());
+            mLeaveBehindFolderIcon.getFolderBackground().drawLeaveBehind(canvas);
+            canvas.restore();
+        }
+    }
+
+    private View inflate(@LayoutRes int layoutResId) {
+        return mActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        // 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;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 1e03b05..76a5782 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -18,13 +18,23 @@
 
 import android.app.Person;
 import android.content.pm.ShortcutInfo;
+import android.view.Display;
 
 import com.android.launcher3.Utilities;
 
 public class ApiWrapper {
 
+    public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
+
     public static Person[] getPersons(ShortcutInfo si) {
         Person[] persons = si.getPersons();
         return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
     }
+
+    /**
+     * Returns true if the display is an internal displays
+     */
+    public static boolean isInternalDisplay(Display display) {
+        return display.getType() == Display.TYPE_INTERNAL;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index aad7e17..1d52315 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -18,19 +18,17 @@
 
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.graphics.OverviewScrim.SCRIM_MULTIPLIER;
-import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+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,9 +38,9 @@
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.graphics.OverviewScrim;
 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;
 
 /**
@@ -65,23 +63,18 @@
     public void setState(@NonNull LauncherState state) {
         float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher);
         RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]);
-        ADJACENT_PAGE_OFFSET.set(mRecentsView, scaleAndOffset[1]);
+        ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
         TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
 
         getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
-        OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
-        SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
-        SCRIM_MULTIPLIER.set(scrim, 1f);
         getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
+        RECENTS_GRID_PROGRESS.set(mRecentsView,
+                state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
     }
 
     @Override
     public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
             PendingAnimation builder) {
-        if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
-            // The entire recents animation is played atomically.
-            return;
-        }
         if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
             return;
         }
@@ -100,23 +93,27 @@
         float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher);
         setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
                 config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
-        setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
+        setter.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1],
                 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));
-        OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
-        setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
-                config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
-        setter.setFloat(scrim, SCRIM_MULTIPLIER, 1f,
-                config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
 
         setter.setFloat(
                 mRecentsView, getTaskModalnessProperty(),
                 toState.getOverviewModalness(),
                 config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
+        setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS,
+                toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f, LINEAR);
     }
 
     abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
index 010694b..c115bbb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
@@ -60,6 +60,20 @@
         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,
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
similarity index 73%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
rename to quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 597c17b..d839a36 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -15,13 +15,11 @@
  */
 package com.android.launcher3.uioverrides;
 
-import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.PIN_PREDICTION;
-import static com.android.launcher3.graphics.IconShape.getShape;
-
 import android.content.Context;
 import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Rect;
@@ -29,20 +27,17 @@
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.graphics.IconPalette;
-import com.android.launcher3.hybridhotseat.HotseatPredictionController;
+import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.IconNormalizer;
 import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
@@ -53,8 +48,7 @@
 /**
  * A BubbleTextView with a ring around it's drawable
  */
-public class PredictedAppIcon extends DoubleShadowBubbleTextView implements
-        LauncherAccessibilityDelegate.AccessibilityActionHandler {
+public class PredictedAppIcon extends DoubleShadowBubbleTextView {
 
     private static final int RING_SHADOW_COLOR = 0x99000000;
     private static final float RING_EFFECT_RATIO = 0.095f;
@@ -63,9 +57,13 @@
     private final DeviceProfile mDeviceProfile;
     private final Paint mIconRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Path mRingPath = new Path();
-    private boolean mIsPinned = false;
-    private final int mNormalizedIconRadius;
+    private final int mNormalizedIconSize;
+    private final Path mShapePath;
+    private final Matrix mTmpMatrix = new Matrix();
+
     private final BlurMaskFilter mShadowFilter;
+
+    private boolean mIsPinned = false;
     private int mPlateColor;
     boolean mDrawForDrag = false;
 
@@ -80,19 +78,18 @@
     public PredictedAppIcon(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         mDeviceProfile = ActivityContext.lookupContext(context).getDeviceProfile();
-        mNormalizedIconRadius = IconNormalizer.getNormalizedCircleSize(getIconSize()) / 2;
+        mNormalizedIconSize = IconNormalizer.getNormalizedCircleSize(getIconSize());
         int shadowSize = context.getResources().getDimensionPixelSize(
                 R.dimen.blur_size_thin_outline);
         mShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.OUTER);
+        mShapePath = GraphicsUtils.getShapePath(mNormalizedIconSize);
     }
 
     @Override
     public void onDraw(Canvas canvas) {
         int count = canvas.save();
         if (!mIsPinned) {
-            boolean isBadged = getTag() instanceof WorkspaceItemInfo
-                    && !Process.myUserHandle().equals(((ItemInfo) getTag()).user);
-            drawEffect(canvas, isBadged);
+            drawEffect(canvas);
             canvas.translate(getWidth() * RING_EFFECT_RATIO, getHeight() * RING_EFFECT_RATIO);
             canvas.scale(1 - 2 * RING_EFFECT_RATIO, 1 - 2 * RING_EFFECT_RATIO);
         }
@@ -114,8 +111,7 @@
     @Override
     public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
         super.applyFromWorkspaceItem(info);
-        int color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
-        mPlateColor = ColorUtils.setAlphaComponent(color, 200);
+        mPlateColor = ColorUtils.setAlphaComponent(mDotParams.color, 200);
         if (mIsPinned) {
             setContentDescription(info.contentDescription);
         } else {
@@ -148,29 +144,6 @@
     }
 
     @Override
-    public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) {
-        if (!mIsPinned) {
-            accessibilityNodeInfo.addAction(
-                    new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION,
-                            getContext().getText(R.string.pin_prediction)));
-        }
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, ItemInfo info) {
-        QuickstepLauncher launcher = Launcher.cast(Launcher.getLauncher(getContext()));
-        if (action == PIN_PREDICTION) {
-            if (launcher == null || launcher.getHotseatPredictionController() == null) {
-                return false;
-            }
-            HotseatPredictionController controller = launcher.getHotseatPredictionController();
-            controller.pinPrediction(info);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
     public void getIconBounds(Rect outBounds) {
         super.getIconBounds(outBounds);
         if (!mIsPinned && !mIsDrawingDot) {
@@ -179,31 +152,63 @@
         }
     }
 
+    public boolean isPinned() {
+        return mIsPinned;
+    }
+
     private int getOutlineOffsetX() {
-        return (getMeasuredWidth() / 2) - mNormalizedIconRadius;
+        return (getMeasuredWidth() - mNormalizedIconSize) / 2;
     }
 
     private int getOutlineOffsetY() {
-        return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx;
+        if (mDisplay != DISPLAY_TASKBAR) {
+            return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx;
+        }
+        return (getMeasuredHeight() - mNormalizedIconSize) / 2;
     }
 
-    private void drawEffect(Canvas canvas, boolean isBadged) {
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        updateRingPath();
+    }
+
+    @Override
+    public void setTag(Object tag) {
+        super.setTag(tag);
+        updateRingPath();
+    }
+
+    private void updateRingPath() {
+        boolean isBadged = false;
+        if (getTag() instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
+            isBadged = !Process.myUserHandle().equals(info.user)
+                    || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+                    || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+        }
+
+        mRingPath.reset();
+        mTmpMatrix.setTranslate(getOutlineOffsetX(), getOutlineOffsetY());
+
+        mRingPath.addPath(mShapePath, mTmpMatrix);
+        if (isBadged) {
+            float outlineSize = mNormalizedIconSize * RING_EFFECT_RATIO;
+            float iconSize = getIconSize() * (1 - 2 * RING_EFFECT_RATIO);
+            float badgeSize = LauncherIcons.getBadgeSizeForIconSize((int) iconSize) + outlineSize;
+            float scale = badgeSize / mNormalizedIconSize;
+            mTmpMatrix.postTranslate(mNormalizedIconSize, mNormalizedIconSize);
+            mTmpMatrix.preScale(scale, scale);
+            mTmpMatrix.preTranslate(-mNormalizedIconSize, -mNormalizedIconSize);
+            mRingPath.addPath(mShapePath, mTmpMatrix);
+        }
+    }
+
+    private void drawEffect(Canvas canvas) {
         // Don't draw ring effect if item is about to be dragged.
         if (mDrawForDrag) {
             return;
         }
-        mRingPath.reset();
-        getShape().addToPath(mRingPath, getOutlineOffsetX(), getOutlineOffsetY(),
-                mNormalizedIconRadius);
-        if (isBadged) {
-            float outlineSize = mNormalizedIconRadius * RING_EFFECT_RATIO * 2;
-            float iconSize = getIconSize() * (1 - 2 * RING_EFFECT_RATIO);
-            float badgeSize = LauncherIcons.getBadgeSizeForIconSize((int) iconSize) + outlineSize;
-            float badgeInset = mNormalizedIconRadius * 2 - badgeSize;
-            getShape().addToPath(mRingPath, getOutlineOffsetX() + badgeInset,
-                    getOutlineOffsetY() + badgeInset, badgeSize / 2);
-
-        }
         mIconRingPaint.setColor(RING_SHADOW_COLOR);
         mIconRingPaint.setMaskFilter(mShadowFilter);
         canvas.drawPath(mRingPath, mIconRingPaint);
@@ -249,17 +254,13 @@
      */
     public static class PredictedIconOutlineDrawing extends CellLayout.DelegatedCellDrawing {
 
-        private int mOffsetX;
-        private int mOffsetY;
-        private int mIconRadius;
-        private Paint mOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        private final PredictedAppIcon mIcon;
+        private final Paint mOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
         public PredictedIconOutlineDrawing(int cellX, int cellY, PredictedAppIcon icon) {
             mDelegateCellX = cellX;
             mDelegateCellY = cellY;
-            mOffsetX = icon.getOutlineOffsetX();
-            mOffsetY = icon.getOutlineOffsetY();
-            mIconRadius = icon.mNormalizedIconRadius;
+            mIcon = icon;
             mOutlinePaint.setStyle(Paint.Style.FILL);
             mOutlinePaint.setColor(Color.argb(24, 245, 245, 245));
         }
@@ -269,7 +270,10 @@
          */
         @Override
         public void drawUnderItem(Canvas canvas) {
-            getShape().drawShape(canvas, mOffsetX, mOffsetY, mIconRadius, mOutlinePaint);
+            canvas.save();
+            canvas.translate(mIcon.getOutlineOffsetX(), mIcon.getOutlineOffsetY());
+            canvas.drawPath(mIcon.mShapePath, mOutlinePaint);
+            canvas.restore();
         }
 
         /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
rename to quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
new file mode 100644
index 0000000..c2c721a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.window.SplashScreen;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+
+/** Provides a Quickstep specific animation when launching an activity from an app widget. */
+class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
+
+    private static final String TAG = "QuickstepInteractionHandler";
+
+    private final QuickstepLauncher mLauncher;
+
+    QuickstepInteractionHandler(QuickstepLauncher launcher) {
+        mLauncher = launcher;
+    }
+
+    @SuppressWarnings("NewApi")
+    @Override
+    public boolean onInteraction(View view, PendingIntent pendingIntent,
+            RemoteViews.RemoteResponse remoteResponse) {
+        LauncherAppWidgetHostView hostView = findHostViewAncestor(view);
+        if (hostView == null) {
+            Log.e(TAG, "View did not have a LauncherAppWidgetHostView ancestor.");
+            return RemoteViews.startPendingIntent(hostView, pendingIntent,
+                    remoteResponse.getLaunchOptions(view));
+        }
+        Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
+        ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
+                .getActivityLaunchOptions(hostView);
+        if (Utilities.ATLEAST_S && !pendingIntent.isActivity()) {
+            // In the event this pending intent eventually launches an activity, i.e. a trampoline,
+            // use the Quickstep transition animation.
+            try {
+                ActivityTaskManager.getService()
+                        .registerRemoteAnimationForNextActivityStart(
+                                pendingIntent.getCreatorPackage(),
+                                activityOptions.options.getRemoteAnimationAdapter());
+            } catch (RemoteException e) {
+                // Do nothing.
+            }
+        }
+        activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        Object itemInfo = hostView.getTag();
+        if (itemInfo instanceof ItemInfo) {
+            mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
+        }
+        options = Pair.create(options.first, activityOptions.options);
+        if (pendingIntent.isActivity()) {
+            logAppLaunch(itemInfo);
+        }
+        return RemoteViews.startPendingIntent(hostView, pendingIntent, options);
+    }
+
+    /**
+     * Logs that the app was launched from the widget.
+     * @param itemInfo the widget info.
+     */
+    private void logAppLaunch(Object itemInfo) {
+        StatsLogManager.StatsLogger logger = mLauncher.getStatsLogManager().logger();
+        if (itemInfo instanceof ItemInfo) {
+            logger.withItemInfo((ItemInfo) itemInfo);
+        }
+        logger.log(LAUNCHER_APP_LAUNCH_TAP);
+    }
+
+    private LauncherAppWidgetHostView findHostViewAncestor(View v) {
+        while (v != null) {
+            if (v instanceof LauncherAppWidgetHostView) return (LauncherAppWidgetHostView) v;
+            v = (View) v.getParent();
+        }
+        return null;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
new file mode 100644
index 0000000..ec9893c
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -0,0 +1,368 @@
+/*
+ * 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.uioverrides;
+
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_WIDGET_APP_START;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepAccessibilityDelegate;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.hybridhotseat.HotseatPredictionController;
+import com.android.launcher3.logging.InstanceId;
+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;
+import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
+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.OnboardingPrefs;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskUtils;
+import com.android.quickstep.util.QuickstepOnboardingPrefs;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+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;
+
+public class QuickstepLauncher extends BaseQuickstepLauncher {
+
+    public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
+    /**
+     * Reusable command for applying the shelf height on the background thread.
+     */
+    public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
+            SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
+
+    private FixedContainerItems mAllAppsPredictions;
+    private HotseatPredictionController mHotseatPredictionController;
+
+    @Override
+    protected void setupViews() {
+        super.setupViews();
+        mHotseatPredictionController = new HotseatPredictionController(this);
+    }
+
+    @Override
+    protected void logAppLaunch(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(
+                getStateManager().getCurrentStableState())) {
+            instanceId = mAllAppsSessionLogId;
+        }
+
+        StatsLogger logger = getStatsLogManager()
+                .logger().withItemInfo(info).withInstanceId(instanceId);
+
+        if (mAllAppsPredictions != null
+                && (info.itemType == ITEM_TYPE_APPLICATION
+                || info.itemType == ITEM_TYPE_SHORTCUT
+                || info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) {
+            int count = mAllAppsPredictions.items.size();
+            for (int i = 0; i < count; i++) {
+                ItemInfo targetInfo = mAllAppsPredictions.items.get(i);
+                if (targetInfo.itemType == info.itemType
+                        && targetInfo.user.equals(info.user)
+                        && Objects.equals(targetInfo.getIntent(), info.getIntent())) {
+                    logger.withRank(i);
+                    break;
+                }
+
+            }
+        }
+        logger.log(LAUNCHER_APP_LAUNCH_TAP);
+
+        mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
+    }
+
+    @Override
+    protected LauncherAccessibilityDelegate createAccessibilityDelegate() {
+        return new QuickstepAccessibilityDelegate(this);
+    }
+
+    /**
+     * Returns Prediction controller for hybrid hotseat
+     */
+    public HotseatPredictionController getHotseatPredictionController() {
+        return mHotseatPredictionController;
+    }
+
+    @Override
+    protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+        return new QuickstepOnboardingPrefs(this, sharedPrefs);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        onStateOrResumeChanging(false /* inTransition */);
+    }
+
+    @Override
+    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);
+    }
+
+    @Override
+    protected void onActivityFlagsChanged(int changeBits) {
+        super.onActivityFlagsChanged(changeBits);
+        if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
+                | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
+            onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
+        }
+
+        if (((changeBits & ACTIVITY_STATE_STARTED) != 0
+                || (changeBits & getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
+            mHotseatPredictionController.setPauseUIUpdate(false);
+        }
+    }
+
+    @Override
+    protected void showAllAppsFromIntent(boolean alreadyOnHome) {
+        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
+        super.showAllAppsFromIntent(alreadyOnHome);
+    }
+
+    @Override
+    public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+        return Stream.concat(
+                Stream.of(mHotseatPredictionController), super.getSupportedShortcuts());
+    }
+
+    /**
+     * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
+     */
+    private void onStateOrResumeChanging(boolean inTransition) {
+        LauncherState state = getStateManager().getState();
+        boolean started = ((getActivityFlags() & ACTIVITY_STATE_STARTED)) != 0;
+        if (started) {
+            DeviceProfile profile = getDeviceProfile();
+            boolean willUserBeActive =
+                    (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
+            boolean visible = (state == NORMAL || state == OVERVIEW)
+                    && (willUserBeActive || isUserActive())
+                    && !profile.isVerticalBarLayout()
+                    && profile.isPhone && !profile.isLandscape;
+            UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
+                    profile.hotseatBarSizePx);
+        }
+        if (state == NORMAL && !inTransition) {
+            ((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
+        }
+    }
+
+    @Override
+    public void bindExtraContainerItems(FixedContainerItems item) {
+        if (item.containerId == Favorites.CONTAINER_PREDICTION) {
+            mAllAppsPredictions = item;
+            getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
+                    .setPredictedApps(item.items);
+        } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+            mHotseatPredictionController.setPredictedItems(item);
+        } else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
+            getPopupDataProvider().setRecommendedWidgets(item.items);
+        }
+    }
+
+    @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();
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mHotseatPredictionController.destroy();
+    }
+
+    @Override
+    public void onStateSetEnd(LauncherState state) {
+        super.onStateSetEnd(state);
+
+        switch (state.ordinal) {
+            case HINT_STATE_ORDINAL: {
+                Workspace workspace = getWorkspace();
+                getStateManager().goToState(NORMAL);
+                if (workspace.getNextPage() != Workspace.DEFAULT_PAGE) {
+                    workspace.post(workspace::moveToDefaultScreen);
+                }
+                break;
+            }
+            case HINT_STATE_TWO_BUTTON_ORDINAL: {
+                getStateManager().goToState(OVERVIEW);
+                getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                break;
+            }
+            case OVERVIEW_STATE_ORDINAL: {
+                RecentsView rv = getOverviewPanel();
+                sendCustomAccessibilityEvent(
+                        rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
+                break;
+            }
+            case QUICK_SWITCH_STATE_ORDINAL: {
+                RecentsView rv = getOverviewPanel();
+                TaskView tasktolaunch = rv.getTaskViewAt(0);
+                if (tasktolaunch != null) {
+                    tasktolaunch.launchTask(success -> {
+                        if (!success) {
+                            getStateManager().goToState(OVERVIEW);
+                        } else {
+                            getStateManager().moveToRestState();
+                        }
+                    });
+                } else {
+                    getStateManager().goToState(NORMAL);
+                }
+                break;
+            }
+
+        }
+    }
+
+    @Override
+    public TouchController[] createTouchControllers() {
+        Mode mode = SysUINavigationMode.getMode(this);
+
+        ArrayList<TouchController> list = new ArrayList<>();
+        list.add(getDragController());
+        switch (mode) {
+            case NO_BUTTON:
+                list.add(new NoButtonQuickSwitchTouchController(this));
+                list.add(new NavBarToHomeTouchController(this));
+                list.add(new NoButtonNavbarToOverviewTouchController(this));
+                break;
+            case TWO_BUTTONS:
+                list.add(new TwoButtonNavbarTouchController(this));
+                list.add(getDeviceProfile().isVerticalBarLayout()
+                        ? new TransposedQuickSwitchTouchController(this)
+                        : new QuickSwitchTouchController(this));
+                list.add(new PortraitStatesTouchController(this));
+                break;
+            case THREE_BUTTONS:
+            default:
+                list.add(new PortraitStatesTouchController(this));
+        }
+
+        if (!getDeviceProfile().isMultiWindowMode) {
+            list.add(new StatusBarTouchController(this));
+        }
+
+        list.add(new LauncherTaskViewController(this));
+        return list.toArray(new TouchController[list.size()]);
+    }
+
+    @Override
+    public AtomicAnimationFactory createAtomicAnimationFactory() {
+        return new QuickstepAtomicAnimationFactory(this);
+    }
+
+    protected LauncherAppWidgetHost createAppWidgetHost() {
+        LauncherAppWidgetHost appWidgetHost = super.createAppWidgetHost();
+        if (ENABLE_QUICKSTEP_WIDGET_APP_START.get()) {
+            appWidgetHost.setInteractionHandler(new QuickstepInteractionHandler(this));
+        }
+        return appWidgetHost;
+    }
+
+    private static final class LauncherTaskViewController extends
+            TaskViewTouchController<Launcher> {
+
+        LauncherTaskViewController(Launcher activity) {
+            super(activity);
+        }
+
+        @Override
+        protected boolean isRecentsInteractive() {
+            return mActivity.isInState(OVERVIEW) || mActivity.isInState(OVERVIEW_MODAL_TASK);
+        }
+
+        @Override
+        protected boolean isRecentsModal() {
+            return mActivity.isInState(OVERVIEW_MODAL_TASK);
+        }
+
+        @Override
+        protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
+            mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
+        }
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        RecentsView recentsView = getOverviewPanel();
+        writer.println("\nQuickstepLauncher:");
+        writer.println(prefix + "\tmOrientationState: " + (recentsView == null ? "recentsNull" :
+                recentsView.getPagedViewOrientedState()));
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
similarity index 65%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
rename to quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 5ccc1e8..996d36a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,12 +15,17 @@
  */
 package com.android.launcher3.uioverrides;
 
-import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
+import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.LauncherState.SPLIT_PLACHOLDER_VIEW;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
+import static com.android.quickstep.views.SplitPlaceholderView.ALPHA_FLOAT;
+import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
 
 import android.annotation.TargetApi;
 import android.os.Build;
@@ -30,7 +35,7 @@
 
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.states.StateAnimationConfig;
@@ -69,11 +74,19 @@
 
         if (toState.overviewUi) {
             // While animating into recents, update the visible task data as needed
-            builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
+            builder.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
             mRecentsView.updateEmptyMessage();
         } else {
             builder.addListener(
-                    AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
+                    AnimatorListeners.forSuccessCallback(mRecentsView::resetTaskVisuals));
+        }
+
+        // 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());
         }
 
         setAlphas(builder, config, toState);
@@ -81,14 +94,28 @@
                 toState.getOverviewFullscreenProgress(), LINEAR);
     }
 
+    /**
+     * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+     */
+    private boolean isSplitSelectionState(@NonNull LauncherState toState) {
+        return toState == OVERVIEW_SPLIT_SELECT;
+    }
+
     private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
             LauncherState state) {
-        float buttonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1 : 0;
+        float clearAllButtonAlpha = state.areElementsVisible(mLauncher, CLEAR_ALL_BUTTON) ? 1 : 0;
         propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
-                buttonAlpha, LINEAR);
+                clearAllButtonAlpha, LINEAR);
+        float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS)
+                && mRecentsView.shouldShowOverviewActionsForState(state) ? 1 : 0;
         propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
-                MultiValueAlpha.VALUE, buttonAlpha, config.getInterpolator(
+                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/WallpaperColorInfo.java b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
deleted file mode 100644
index 36c0e34..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
+++ /dev/null
@@ -1,115 +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;
-
-import static android.app.WallpaperManager.FLAG_SYSTEM;
-
-import android.annotation.TargetApi;
-import android.app.WallpaperColors;
-import android.app.WallpaperManager;
-import android.app.WallpaperManager.OnColorsChangedListener;
-import android.content.Context;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.system.TonalCompat;
-import com.android.systemui.shared.system.TonalCompat.ExtractionInfo;
-
-import java.util.ArrayList;
-
-@TargetApi(Build.VERSION_CODES.P)
-public class WallpaperColorInfo implements OnColorsChangedListener {
-
-    private static final int MAIN_COLOR_LIGHT = 0xffdadce0;
-    private static final int MAIN_COLOR_DARK = 0xff202124;
-    private static final int MAIN_COLOR_REGULAR = 0xff000000;
-
-    public static final MainThreadInitializedObject<WallpaperColorInfo> INSTANCE =
-            new MainThreadInitializedObject<>(WallpaperColorInfo::new);
-
-    private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
-    private final WallpaperManager mWallpaperManager;
-    private final TonalCompat mTonalCompat;
-
-    private ExtractionInfo mExtractionInfo;
-
-    private OnChangeListener[] mTempListeners = new OnChangeListener[0];
-
-    private WallpaperColorInfo(Context context) {
-        mWallpaperManager = context.getSystemService(WallpaperManager.class);
-        mTonalCompat = new TonalCompat(context);
-
-        mWallpaperManager.addOnColorsChangedListener(this, new Handler(Looper.getMainLooper()));
-        update(mWallpaperManager.getWallpaperColors(FLAG_SYSTEM));
-    }
-
-    public int getMainColor() {
-        return mExtractionInfo.mainColor;
-    }
-
-    public int getSecondaryColor() {
-        return mExtractionInfo.secondaryColor;
-    }
-
-    public boolean isDark() {
-        return mExtractionInfo.supportsDarkTheme;
-    }
-
-    public boolean supportsDarkText() {
-        return mExtractionInfo.supportsDarkText;
-    }
-
-    public boolean isMainColorDark() {
-        return mExtractionInfo.mainColor == MAIN_COLOR_DARK;
-    }
-
-    @Override
-    public void onColorsChanged(WallpaperColors colors, int which) {
-        if ((which & FLAG_SYSTEM) != 0) {
-            update(colors);
-            notifyChange();
-        }
-    }
-
-    private void update(WallpaperColors wallpaperColors) {
-        mExtractionInfo = mTonalCompat.extractDarkColors(wallpaperColors);
-    }
-
-    public void addOnChangeListener(OnChangeListener listener) {
-        mListeners.add(listener);
-    }
-
-    public void removeOnChangeListener(OnChangeListener listener) {
-        mListeners.remove(listener);
-    }
-
-    private void notifyChange() {
-        // Create a new array to avoid concurrent modification when the activity destroys itself.
-        mTempListeners = mListeners.toArray(mTempListeners);
-        for (OnChangeListener listener : mTempListeners) {
-            if (listener != null) {
-                listener.onExtractedColorsChanged(this);
-            }
-        }
-    }
-
-    public interface OnChangeListener {
-        void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo);
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
index 7beb9db..d14e8ef 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
@@ -19,6 +19,7 @@
 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 {
@@ -44,4 +45,8 @@
     @Override
     public void handleWtfs() {
     }
+
+    public boolean isDebuggable() {
+        return Utilities.IS_DEBUG_DEVICE;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 29a6be0..f8c9fd1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -16,17 +16,15 @@
 package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
 
 import android.content.Context;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.SysUINavigationMode;
+import com.android.launcher3.util.Themes;
 
 /**
  * Definition for AllApps state
@@ -43,7 +41,7 @@
     };
 
     public AllAppsState(int id) {
-        super(id, ContainerType.ALLAPPS, STATE_FLAGS);
+        super(id, LAUNCHER_STATE_ALLAPPS, STATE_FLAGS);
     }
 
     @Override
@@ -66,19 +64,15 @@
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
         ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW
                 .getWorkspaceScaleAndTranslation(launcher);
-        if (SysUINavigationMode.getMode(launcher) == NO_BUTTON && !ENABLE_OVERVIEW_ACTIONS.get()) {
-            float normalScale = 1;
-            // Scale down halfway to where we'd be in overview, to prepare for a potential pause.
-            scaleAndTranslation.scale = (scaleAndTranslation.scale + normalScale) / 2;
-        } else {
-            scaleAndTranslation.scale = 1;
-        }
+        scaleAndTranslation.scale = 1;
         return scaleAndTranslation;
     }
 
     @Override
     protected float getDepthUnchecked(Context context) {
-        return 1f;
+        // 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.
+        return 2f;
     }
 
     @Override
@@ -88,17 +82,16 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
-    }
-
-    @Override
-    public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        float offset = ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ? 1 : 0;
-        return new float[] {0.9f, offset};
+        return ALL_APPS_CONTENT;
     }
 
     @Override
     public LauncherState getHistoryForState(LauncherState previousState) {
         return previousState == OVERVIEW ? OVERVIEW : NORMAL;
     }
+
+    @Override
+    public int getWorkspaceScrimColor(Launcher launcher) {
+        return Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
similarity index 75%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
rename to quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 8ff05f2..fe5a347 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -15,12 +15,15 @@
  */
 package com.android.launcher3.uioverrides.states;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+
 import android.content.Context;
+import android.graphics.Color;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
 
@@ -33,7 +36,7 @@
             | FLAG_WORKSPACE_INACCESSIBLE | FLAG_NON_INTERACTIVE | FLAG_CLOSE_POPUPS;
 
     public BackgroundAppState(int id) {
-        this(id, LauncherLogProto.ContainerType.TASKSWITCHER);
+        this(id, LAUNCHER_STATE_BACKGROUND);
     }
 
     protected BackgroundAppState(int id, int logContainer) {
@@ -68,26 +71,25 @@
     @Override
     public int getVisibleElements(Launcher launcher) {
         return super.getVisibleElements(launcher)
-                & ~OVERVIEW_BUTTONS & ~VERTICAL_SWIPE_INDICATOR;
+                & ~OVERVIEW_ACTIONS
+                & ~CLEAR_ALL_BUTTON
+                & ~VERTICAL_SWIPE_INDICATOR
+                | TASKBAR;
     }
 
     @Override
-    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
-        if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
-            // Translate hotseat offscreen if we show it in overview.
-            RecentsView recentsView = launcher.getOverviewPanel();
-            ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher);
-            scaleAndTranslation.translationY += LayoutUtils.getShelfTrackingDistance(launcher,
-                    launcher.getDeviceProfile(),
-                    recentsView.getPagedOrientationHandler());
-            return scaleAndTranslation;
-        }
-        return super.getHotseatScaleAndTranslation(launcher);
+    public boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
+        return false;
     }
 
     @Override
     protected float getDepthUnchecked(Context context) {
-        return 1f;
+        return 1;
+    }
+
+    @Override
+    public int getWorkspaceScrimColor(Launcher launcher) {
+        return Color.TRANSPARENT;
     }
 
     public static float[] getOverviewScaleAndOffsetForBackgroundState(
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
similarity index 81%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
rename to quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index fc0dcd5..6f084a1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -15,13 +15,15 @@
  */
 package com.android.launcher3.uioverrides.states;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
+
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -34,7 +36,7 @@
             FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE;
 
     public OverviewModalTaskState(int id) {
-        super(id, ContainerType.OVERVIEW, STATE_FLAGS);
+        super(id, LAUNCHER_STATE_OVERVIEW, STATE_FLAGS);
     }
 
     @Override
@@ -44,7 +46,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return OVERVIEW_BUTTONS;
+        return OVERVIEW_ACTIONS | CLEAR_ALL_BUTTON;
     }
 
     @Override
@@ -69,13 +71,12 @@
     }
 
     public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
-        Rect out = new Rect();
-        activity.<RecentsView>getOverviewPanel().getTaskSize(out);
-        int taskHeight = out.height();
-        activity.<RecentsView>getOverviewPanel().getModalTaskSize(out);
-        int newHeight = out.height();
+        Point taskSize = activity.<RecentsView>getOverviewPanel().getSelectedTaskSize();
+        Rect modalTaskSize = new Rect();
+        activity.<RecentsView>getOverviewPanel().getModalTaskSize(modalTaskSize);
 
-        float scale = (float) newHeight / taskHeight;
+        float scale = Math.min((float) modalTaskSize.height() / taskSize.y,
+                (float) modalTaskSize.width() / taskSize.x);
 
         return new float[] {scale, NO_OFFSET};
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
new file mode 100644
index 0000000..8c128c8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides.states;
+
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
+
+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;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * Definition for overview state
+ */
+public class OverviewState extends LauncherState {
+
+    protected static final Rect sTempRect = new Rect();
+
+    private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
+            | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE
+            | FLAG_CLOSE_POPUPS;
+
+    public OverviewState(int id) {
+        this(id, STATE_FLAGS);
+    }
+
+    protected OverviewState(int id, int stateFlags) {
+        this(id, LAUNCHER_STATE_OVERVIEW, stateFlags);
+    }
+
+    protected OverviewState(int id, int logContainer, int stateFlags) {
+        super(id, logContainer, stateFlags);
+    }
+
+    @Override
+    public int getTransitionDuration(Context context) {
+        // In gesture modes, overview comes in all the way from the side, so give it more time.
+        return SysUINavigationMode.INSTANCE.get(context).getMode().hasGestures ? 380 : 250;
+    }
+
+    @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;
+        recentsView.getTaskSize(sTempRect);
+        float scale = (float) sTempRect.width() / workspacePageWidth;
+        float parallaxFactor = 0.5f;
+        return new ScaleAndTranslation(scale, 0, -getDefaultSwipeHeight(launcher) * parallaxFactor);
+    }
+
+    @Override
+    public float[] getOverviewScaleAndOffset(Launcher launcher) {
+        return new float[] {NO_SCALE, NO_OFFSET};
+    }
+
+    @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
+            public float getPageAlpha(int pageIndex) {
+                return 0;
+            }
+        };
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
+    }
+
+    @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();
+    }
+
+    @Override
+    public String getDescription(Launcher launcher) {
+        return launcher.getString(R.string.accessibility_recent_apps);
+    }
+
+    public static float getDefaultSwipeHeight(Launcher launcher) {
+        return LayoutUtils.getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
+    }
+
+    @Override
+    protected float getDepthUnchecked(Context context) {
+        //TODO revert when b/178661709 is fixed
+        return SystemProperties.getBoolean("ro.launcher.depth.overview", true) ? 1 : 0;
+    }
+
+    @Override
+    public void onBackPressed(Launcher launcher) {
+        TaskView taskView = launcher.<RecentsView>getOverviewPanel().getRunningTaskView();
+        if (taskView != null) {
+            taskView.launchTaskAnimated();
+        } else {
+            super.onBackPressed(launcher);
+        }
+    }
+
+    public static OverviewState newBackgroundState(int id) {
+        return new BackgroundAppState(id);
+    }
+
+    public static OverviewState newSwitchState(int id) {
+        return new QuickSwitchState(id);
+    }
+
+    /**
+     *  New Overview substate that represents the overview in modal mode (one task shown on its own)
+     */
+    public static OverviewState newModalTaskState(int id) {
+        return new OverviewModalTaskState(id);
+    }
+
+    /**
+     * New Overview substate representing state where 1 app for split screen has been selected and
+     * pinned and user is selecting the second one
+     */
+    public static OverviewState newSplitSelectState(int id) {
+        return new SplitScreenSelectState(id);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
similarity index 70%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
rename to quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 2c7373e..d36e76b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -15,8 +15,11 @@
  */
 package com.android.launcher3.uioverrides.states;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+
 import com.android.launcher3.Launcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 
 /**
  * State to indicate we are about to launch a recent task. Note that this state is only used when
@@ -26,7 +29,7 @@
 public class QuickSwitchState extends BackgroundAppState {
 
     public QuickSwitchState(int id) {
-        super(id, LauncherLogProto.ContainerType.APP);
+        super(id, LAUNCHER_STATE_BACKGROUND);
     }
 
     @Override
@@ -34,11 +37,22 @@
         float shiftRange = launcher.getAllAppsController().getShiftRange();
         float shiftProgress = getVerticalProgress(launcher) - NORMAL.getVerticalProgress(launcher);
         float translationY = shiftProgress * shiftRange;
-        return new ScaleAndTranslation(1, 0, translationY);
+        return new ScaleAndTranslation(0.9f, 0, translationY);
+    }
+
+    @Override
+    public int getWorkspaceScrimColor(Launcher launcher) {
+        return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
+    }
+
+    @Override
+    public float getVerticalProgress(Launcher launcher) {
+        // Don't move all apps shelf while quick-switching (just let it fade).
+        return 1f;
     }
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return NONE;
+        return TASKBAR;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
new file mode 100644
index 0000000..0e2fbbc
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -0,0 +1,176 @@
+/*
+ * 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.uioverrides.states;
+
+import static android.view.View.VISIBLE;
+
+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;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+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.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
+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.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.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
+
+import android.animation.ValueAnimator;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.util.RecentsAtomicAnimationFactory;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Animation factory for quickstep specific transitions
+ */
+public class QuickstepAtomicAnimationFactory extends
+        RecentsAtomicAnimationFactory<QuickstepLauncher, LauncherState> {
+
+    // Scale recents takes before animating in
+    private static final float RECENTS_PREPARE_SCALE = 1.33f;
+    // Scale workspace takes before animating in
+    private static final float WORKSPACE_PREPARE_SCALE = 0.92f;
+    // Constants to specify how to scroll RecentsView to the default page if it's not already there.
+    private static final int DEFAULT_PAGE = 0;
+    private static final int PER_PAGE_SCROLL_DURATION = 150;
+    private static final int MAX_PAGE_SCROLL_DURATION = 750;
+
+    // Due to use of physics, duration may differ between devices so we need to calculate and
+    // cache the value.
+    private int mHintToNormalDuration = -1;
+
+    public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) {
+        super(activity);
+    }
+
+    @Override
+    public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
+            StateAnimationConfig config) {
+        RecentsView overview = mActivity.getOverviewPanel();
+        if (toState == NORMAL && fromState == OVERVIEW) {
+            config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f));
+            config.setInterpolator(ANIM_SCRIM_FADE, LINEAR);
+            config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
+            config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+
+            if (SysUINavigationMode.getMode(mActivity).hasGestures
+                    && overview.getTaskViewCount() > 0) {
+                // Overview is going offscreen, so keep it at its current scale and opacity.
+                config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
+                config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
+                config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X,
+                        clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f));
+                config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
+            } else {
+                config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
+                config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
+                config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
+            }
+
+            // Scroll RecentsView to page 0 as it goes offscreen, if necessary.
+            int numPagesToScroll = overview.getNextPage() - DEFAULT_PAGE;
+            long scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
+                    numPagesToScroll * PER_PAGE_SCROLL_DURATION);
+            config.duration = Math.max(config.duration, scrollDuration);
+            overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
+
+            Workspace workspace = mActivity.getWorkspace();
+            // Start from a higher workspace scale, but only if we're invisible so we don't jump.
+            boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
+            if (isWorkspaceVisible) {
+                CellLayout currentChild = (CellLayout) workspace.getChildAt(
+                        workspace.getCurrentPage());
+                isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
+                        && currentChild.getShortcutsAndWidgets().getAlpha() > 0;
+            }
+            if (!isWorkspaceVisible) {
+                workspace.setScaleX(WORKSPACE_PREPARE_SCALE);
+                workspace.setScaleY(WORKSPACE_PREPARE_SCALE);
+            }
+            Hotseat hotseat = mActivity.getHotseat();
+            boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
+            if (!isHotseatVisible) {
+                hotseat.setScaleX(WORKSPACE_PREPARE_SCALE);
+                hotseat.setScaleY(WORKSPACE_PREPARE_SCALE);
+            }
+        } else if ((fromState == NORMAL || fromState == HINT_STATE
+                || fromState == HINT_STATE_TWO_BUTTON) && toState == OVERVIEW) {
+            if (SysUINavigationMode.getMode(mActivity).hasGestures) {
+                config.setInterpolator(ANIM_WORKSPACE_SCALE,
+                        fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
+                config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
+
+                // Scrolling in tasks, so show straight away
+                if (overview.getTaskViewCount() > 0) {
+                    config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+                } else {
+                    config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+                }
+            } else {
+                config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
+                config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+
+                // Scale up the recents, if it is not coming from the side
+                if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
+                    RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
+                }
+            }
+            config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_SCRIM_FADE, t -> {
+                // Animate at the same rate until reaching progress 1, and skip the overshoot.
+                return Math.min(1, OVERSHOOT_1_2.getInterpolation(t));
+            });
+            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
+        } else if (fromState == HINT_STATE && toState == NORMAL) {
+            config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
+            if (mHintToNormalDuration == -1) {
+                ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(),
+                        toState.getWorkspaceScaleAndTranslation(mActivity).scale);
+                mHintToNormalDuration = (int) va.getDuration();
+            }
+            config.duration = Math.max(config.duration, mHintToNormalDuration);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
new file mode 100644
index 0000000..6968494
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -0,0 +1,55 @@
+/*
+ * 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.uioverrides.states;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * New Overview substate representing state where 1 app for split screen has been selected and
+ * pinned and user is selecting the second one
+ */
+public class SplitScreenSelectState extends OverviewState {
+    public SplitScreenSelectState(int id) {
+        super(id);
+    }
+
+    @Override
+    public void onBackPressed(Launcher launcher) {
+        launcher.getStateManager().goToState(OVERVIEW);
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return SPLIT_PLACHOLDER_VIEW;
+    }
+
+    @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;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
deleted file mode 100644
index bef191e..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.android.launcher3.uioverrides.touchcontrollers;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.quickstep.SystemUiProxy;
-
-/**
- * Touch controller for handling edge swipes in landscape/seascape UI
- */
-public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController {
-
-    private static final String TAG = "LandscapeEdgeSwipeCtrl";
-
-    public LandscapeEdgeSwipeController(Launcher l) {
-        super(l, SingleAxisSwipeDetector.HORIZONTAL);
-    }
-
-    @Override
-    protected boolean canInterceptTouch(MotionEvent ev) {
-        if (mCurrentAnimation != null) {
-            // If we are already animating from a previous state, we can intercept.
-            return true;
-        }
-        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
-            return false;
-        }
-        return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
-    }
-
-    @Override
-    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
-        boolean draggingFromNav = mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
-        return draggingFromNav ? OVERVIEW : NORMAL;
-    }
-
-    @Override
-    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
-        return LauncherLogProto.ContainerType.NAVBAR;
-    }
-
-    @Override
-    protected float getShiftRange() {
-        return mLauncher.getDragLayer().getWidth();
-    }
-
-    @Override
-    protected float initCurrentAnimation(@AnimationFlags int animComponent) {
-        float range = getShiftRange();
-        long maxAccuracy = (long) (2 * range);
-        mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
-                maxAccuracy, animComponent);
-        return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range;
-    }
-
-    @Override
-    protected int getDirectionForLog() {
-        return mLauncher.getDeviceProfile().isSeascape() ? Direction.RIGHT : Direction.LEFT;
-    }
-
-    @Override
-    protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
-        super.onSwipeInteractionCompleted(targetState, logAction);
-        if (mStartState == NORMAL && targetState == OVERVIEW) {
-            SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
-        }
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
similarity index 74%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index c6aaa43..86c42ca 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -17,18 +17,19 @@
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
-import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
 import android.animation.ValueAnimator;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.animation.Interpolator;
 
@@ -38,25 +39,19 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.OverviewScrim;
-import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.TouchController;
+import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.AssistantUtilities;
 import com.android.quickstep.util.OverviewToHomeAnim;
 import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 /**
  * Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
@@ -105,37 +100,19 @@
     }
 
     private boolean canInterceptTouch(MotionEvent ev) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NavBarToHomeTouchController.canInterceptTouch "
-                    + ev);
-        }
         boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
         if (!cameFromNavBar) {
             return false;
         }
         if (mStartState.overviewUi || mStartState == ALL_APPS) {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.PAUSE_NOT_DETECTED,
-                        "NavBarToHomeTouchController.canInterceptTouch true 1 "
-                                + mStartState.overviewUi + " " + (mStartState == ALL_APPS));
-            }
             return true;
         }
         int typeToClose = ENABLE_ALL_APPS_EDU.get() ? TYPE_ALL & ~TYPE_ALL_APPS_EDU : TYPE_ALL;
         if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, typeToClose) != null) {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.PAUSE_NOT_DETECTED,
-                        "NavBarToHomeTouchController.canInterceptTouch true 2 "
-                                + AbstractFloatingView.getTopOpenView(mLauncher), new Exception());
-            }
             return true;
         }
         if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
                 && AssistantUtilities.isExcludedAssistantRunning()) {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.PAUSE_NOT_DETECTED,
-                        "NavBarToHomeTouchController.canInterceptTouch true 3");
-            }
             return true;
         }
         return false;
@@ -163,14 +140,11 @@
             AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher,
                     builder);
 
-            builder.setFloat(mLauncher.getDragLayer().getOverviewScrim(),
-                    OverviewScrim.SCRIM_MULTIPLIER, OVERVIEW_TO_HOME_SCRIM_MULTIPLIER,
-                    PULLBACK_INTERPOLATOR);
-
             if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-                builder.addOnFrameCallback(
-                        () -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
+                builder.addOnFrameCallback(recentsView::redrawLiveTile);
             }
+
+            AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_TASK_MENU);
         } else if (mStartState == ALL_APPS) {
             AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
             builder.setFloat(allAppsController, ALL_APPS_PROGRESS,
@@ -188,8 +162,8 @@
         if (topView != null) {
             topView.addHintCloseAnim(mPullbackDistance, PULLBACK_INTERPOLATOR, builder);
         }
-        mCurrentAnimation = builder.createPlaybackController()
-                .setOnCancelRunnable(this::clearState);
+        mCurrentAnimation = builder.createPlaybackController();
+        mCurrentAnimation.getTarget().addListener(newCancelListener(this::clearState));
     }
 
     private void clearState() {
@@ -210,7 +184,6 @@
     @Override
     public void onDragEnd(float velocity) {
         boolean fling = mSwipeDetector.isFling(velocity);
-        final int logAction = fling ? Touch.FLING : Touch.SWIPE;
         float progress = mCurrentAnimation.getProgressFraction();
         float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress);
         boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
@@ -226,24 +199,22 @@
                         .animateWithVelocity(velocity);
             } else {
                 mLauncher.getStateManager().goToState(mEndState, true,
-                        () -> onSwipeInteractionCompleted(mEndState));
+                        forSuccessCallback(() -> onSwipeInteractionCompleted(mEndState)));
             }
             if (mStartState != mEndState) {
-                logStateChange(mStartState.containerType, logAction);
+                logHomeGesture();
             }
             AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(mLauncher);
             if (topOpenView != null) {
                 AbstractFloatingView.closeAllOpenViews(mLauncher);
-                logStateChange(topOpenView.getLogContainerType(), logAction);
+                // TODO: add to WW log
             }
-            ActivityManagerWrapper.getInstance()
-                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+            TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
         } else {
             // Quickly return to the state we came from (we didn't move far).
             ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
             anim.setFloatValues(progress, 0);
-            anim.addListener(AnimationSuccessListener.forRunnable(
-                    () -> onSwipeInteractionCompleted(mStartState)));
+            anim.addListener(forSuccessCallback(() -> onSwipeInteractionCompleted(mStartState)));
             anim.setDuration(80).start();
         }
     }
@@ -254,17 +225,10 @@
         AccessibilityManagerCompat.sendStateEventToTest(mLauncher, targetState.ordinal);
     }
 
-    private void logStateChange(int startContainerType, int logAction) {
-        mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
-                LauncherLogProto.Action.Direction.UP,
-                mSwipeDetector.getDownX(), mSwipeDetector.getDownY(),
-                LauncherLogProto.ContainerType.NAVBAR,
-                startContainerType,
-                mEndState.containerType,
-                mLauncher.getWorkspace().getCurrentPage());
+    private void logHomeGesture() {
         mLauncher.getStatsLogManager().logger()
-                .withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
-                .withDstState(StatsLogManager.containerTypeToAtomState(mEndState.containerType))
+                .withSrcState(mStartState.statsLogOrdinal)
+                .withDstState(mEndState.statsLogOrdinal)
                 .log(LAUNCHER_HOME_GESTURE);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
similarity index 67%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 8f0f683..283743d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -16,30 +16,32 @@
 
 package com.android.launcher3.uioverrides.touchcontrollers;
 
+import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
+import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 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.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.graphics.PointF;
-import android.util.Log;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.graphics.OverviewScrim;
-import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 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.views.RecentsView;
 
@@ -48,8 +50,8 @@
  * the nav bar falls back to go to All Apps. Swiping from the nav bar without holding goes to the
  * first home screen instead of to Overview.
  */
-public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController {
-
+public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouchController {
+    private static final float ONE_HANDED_ACTIVATED_SLOP_MULTIPLIER = 2.5f;
 
     // How much of the movement to use for translating overview after swipe and hold.
     private static final float OVERVIEW_MOVEMENT_FACTOR = 0.25f;
@@ -57,8 +59,11 @@
     private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f;
 
     private final RecentsView mRecentsView;
+    private final MotionPauseDetector mMotionPauseDetector;
+    private final float mMotionPauseMinDisplacement;
 
     private boolean mDidTouchStartInNavBar;
+    private boolean mStartedOverview;
     private boolean mReachedOverview;
     // The last recorded displacement before we reached overview.
     private PointF mStartDisplacement = new PointF();
@@ -71,16 +76,8 @@
     public NoButtonNavbarToOverviewTouchController(Launcher l) {
         super(l);
         mRecentsView = l.getOverviewPanel();
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController.ctor");
-        }
-    }
-
-    @Override
-    protected float getMotionPauseMaxDisplacement() {
-        // No need to disallow pause when swiping up all the way up the screen (unlike
-        // FlingAndHoldTouchController where user is probably intending to go to all apps).
-        return Float.MAX_VALUE;
+        mMotionPauseDetector = new MotionPauseDetector(l);
+        mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
     }
 
     @Override
@@ -101,11 +98,11 @@
     }
 
     @Override
-    protected float initCurrentAnimation(int animComponents) {
-        float progressMultiplier = super.initCurrentAnimation(animComponents);
+    protected float initCurrentAnimation() {
+        float progressMultiplier = super.initCurrentAnimation();
         if (mToState == HINT_STATE) {
             // Track the drag across the entire height of the screen.
-            progressMultiplier = -1 / getShiftRange();
+            progressMultiplier = -1f / mLauncher.getDeviceProfile().heightPx;
         }
         return progressMultiplier;
     }
@@ -113,13 +110,21 @@
     @Override
     public void onDragStart(boolean start, float startDisplacement) {
         super.onDragStart(start, startDisplacement);
-        if (mFromState == NORMAL && mToState == HINT_STATE) {
-            mNormalToHintOverviewScrimAnimator = ObjectAnimator.ofFloat(
-                    mLauncher.getDragLayer().getOverviewScrim(),
-                    OverviewScrim.SCRIM_PROGRESS,
-                    mFromState.getOverviewScrimAlpha(mLauncher),
-                    mToState.getOverviewScrimAlpha(mLauncher));
+
+        mMotionPauseDetector.clear();
+
+        if (handlingOverviewAnim()) {
+            mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
         }
+
+        if (mFromState == NORMAL && mToState == HINT_STATE) {
+            mNormalToHintOverviewScrimAnimator = ObjectAnimator.ofArgb(
+                    mLauncher.getScrimView(),
+                    VIEW_BACKGROUND_COLOR,
+                    mFromState.getWorkspaceScrimColor(mLauncher),
+                    mToState.getWorkspaceScrimColor(mLauncher));
+        }
+        mStartedOverview = false;
         mReachedOverview = false;
         mOverviewResistYAnim = null;
     }
@@ -134,7 +139,13 @@
 
     @Override
     public void onDragEnd(float velocity) {
-        super.onDragEnd(velocity);
+        if (mStartedOverview) {
+            goToOverviewOrHomeOnDragEnd(velocity);
+        } else {
+            super.onDragEnd(velocity);
+        }
+
+        mMotionPauseDetector.clear();
         mNormalToHintOverviewScrimAnimator = null;
         if (mLauncher.isInState(OVERVIEW)) {
             // Normally we would cleanup the state based on mCurrentAnimation, but since we stop
@@ -156,41 +167,41 @@
         }
     }
 
-    @Override
-    protected void onMotionPauseChanged(boolean isPaused) {
+    private void onMotionPauseDetected() {
         if (mCurrentAnimation == null) {
             return;
         }
         mNormalToHintOverviewScrimAnimator = null;
-        mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
-            mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
-                mOverviewResistYAnim = AnimatorControllerWithResistance
-                        .createRecentsResistanceFromOverviewAnim(mLauncher, null)
-                        .createPlaybackController();
-                mReachedOverview = true;
-                maybeSwipeInteractionToOverviewComplete();
-            });
-        });
+        mCurrentAnimation.getTarget().addListener(newCancelListener(() ->
+                mLauncher.getStateManager().goToState(OVERVIEW, true, forSuccessCallback(() -> {
+                    mOverviewResistYAnim = AnimatorControllerWithResistance
+                            .createRecentsResistanceFromOverviewAnim(mLauncher, null)
+                            .createPlaybackController();
+                    mReachedOverview = true;
+                    maybeSwipeInteractionToOverviewComplete();
+                }))));
+
+        mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
+        mCurrentAnimation.dispatchOnCancel();
+        mStartedOverview = true;
         VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
     }
 
     private void maybeSwipeInteractionToOverviewComplete() {
-        if (mReachedOverview && mDetector.isSettlingState()) {
-            onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
+        if (mReachedOverview && !mDetector.isDraggingState()) {
+            onSwipeInteractionCompleted(OVERVIEW);
         }
     }
 
-    @Override
-    protected boolean handlingOverviewAnim() {
-        return mDidTouchStartInNavBar && super.handlingOverviewAnim();
+    private boolean handlingOverviewAnim() {
+        int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
+        return mDidTouchStartInNavBar && mStartState == NORMAL
+                && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
     }
 
     @Override
     public boolean onDrag(float yDisplacement, float xDisplacement, MotionEvent event) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController");
-        }
-        if (mMotionPauseDetector.isPaused()) {
+        if (mStartedOverview) {
             if (!mReachedOverview) {
                 mStartDisplacement.set(xDisplacement, yDisplacement);
                 mStartY = event.getY();
@@ -205,20 +216,21 @@
                             * OVERVIEW_MOVEMENT_FACTOR);
                 }
             }
-            // Stay in Overview.
-            return true;
         }
-        return super.onDrag(yDisplacement, xDisplacement, event);
+
+        float upDisplacement = -yDisplacement;
+        mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
+                || upDisplacement < mMotionPauseMinDisplacement);
+        mMotionPauseDetector.addPosition(event);
+
+        // Stay in Overview.
+        return mStartedOverview || super.onDrag(yDisplacement, xDisplacement, event);
     }
 
-    @Override
-    protected void goToOverviewOnDragEnd(float velocity) {
-        float velocityDp = dpiFromPx(velocity);
-        boolean isFling = Math.abs(velocityDp) > 1;
-        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
-        boolean goToHomeInsteadOfOverview = isFling;
+    private void goToOverviewOrHomeOnDragEnd(float velocity) {
+        boolean goToHomeInsteadOfOverview = !mMotionPauseDetector.isPaused();
         if (goToHomeInsteadOfOverview) {
-            new OverviewToHomeAnim(mLauncher, ()-> onSwipeInteractionCompleted(NORMAL, Touch.FLING))
+            new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(NORMAL))
                     .animateWithVelocity(velocity);
         }
         if (mReachedOverview) {
@@ -246,6 +258,16 @@
     }
 
     private float dpiFromPx(float pixels) {
-        return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics());
+        return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics().densityDpi);
+    }
+
+    @Override
+    public void onOneHandedModeStateChanged(boolean activated) {
+        if (activated) {
+            mDetector.setTouchSlopMultiplier(ONE_HANDED_ACTIVATED_SLOP_MULTIPLIER);
+        } else {
+            // Reset touch slop multiplier to default 1.0f
+            mDetector.setTouchSlopMultiplier(1f /* default */);
+        }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
similarity index 77%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 5c9fd47..40c3e02 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -15,41 +15,42 @@
  */
 package com.android.launcher3.uioverrides.touchcontrollers;
 
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
+import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
 import static com.android.launcher3.LauncherState.QUICK_SWITCH;
 import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_5;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
 import static com.android.launcher3.logging.StatsLogManager.getLauncherAtomEvent;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_PAUSE_TO_OVERVIEW_ANIM;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.CANCEL;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+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;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
 import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.graphics.PointF;
@@ -60,18 +61,11 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.OverviewScrim;
-import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.BothAxesSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.quickstep.AnimatedFloat;
@@ -79,9 +73,7 @@
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.util.ShelfPeekAnim;
-import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.LauncherRecentsView;
 
 /**
@@ -89,23 +81,24 @@
  * the user as possible, also handles swipe up and hold to go to overview and swiping back home.
  */
 public class NoButtonQuickSwitchTouchController implements TouchController,
-        BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener {
+        BothAxesSwipeDetector.Listener {
 
-    /** The minimum progress of the scale/translationY animation until drag end. */
     private static final float Y_ANIM_MIN_PROGRESS = 0.25f;
-    private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_5;
+    private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_3;
     private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75;
     private static final Interpolator SCALE_DOWN_INTERPOLATOR = LINEAR;
+    private static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
 
     private final BaseQuickstepLauncher mLauncher;
     private final BothAxesSwipeDetector mSwipeDetector;
-    private final ShelfPeekAnim mShelfPeekAnim;
     private final float mXRange;
     private final float mYRange;
     private final float mMaxYProgress;
     private final MotionPauseDetector mMotionPauseDetector;
     private final float mMotionPauseMinDisplacement;
     private final LauncherRecentsView mRecentsView;
+    protected final AnimatorListener mClearStateOnCancelListener =
+            newCancelListener(this::clearState);
 
     private boolean mNoIntercept;
     private LauncherState mStartState;
@@ -121,7 +114,6 @@
     public NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher) {
         mLauncher = launcher;
         mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this);
-        mShelfPeekAnim = mLauncher.getShelfPeekAnim();
         mRecentsView = mLauncher.getOverviewPanel();
         mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
         mYRange = LayoutUtils.getShelfTrackingDistance(
@@ -178,7 +170,7 @@
         if (start) {
             mStartState = mLauncher.getStateManager().getState();
 
-            mMotionPauseDetector.setOnMotionPauseListener(this);
+            mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
 
             // We have detected horizontal drag start, now allow swipe up as well.
             mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP,
@@ -188,28 +180,8 @@
         }
     }
 
-    @Override
-    public void onMotionPauseChanged(boolean isPaused) {
+    private void onMotionPauseDetected() {
         VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
-
-        if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
-            return;
-        }
-
-        ShelfAnimState shelfState = isPaused ? PEEK : HIDE;
-        if (shelfState == PEEK) {
-            // Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking.
-            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
-            allAppsController.setAlphas(
-                    NORMAL, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
-
-            if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
-                // Hotseat was hidden, but we need it visible when peeking.
-                mLauncher.getHotseat().setAlpha(1);
-            }
-        }
-        mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR,
-                ShelfPeekAnim.DURATION);
     }
 
     private void setupAnimators() {
@@ -217,7 +189,8 @@
         StateAnimationConfig nonOverviewBuilder = new StateAnimationConfig();
         nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR);
         nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR);
-        nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR);
+        nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_SCALE, FADE_OUT_INTERPOLATOR);
+        nonOverviewBuilder.setInterpolator(ANIM_DEPTH, FADE_OUT_INTERPOLATOR);
         nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR);
         updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder);
         mNonOverviewAnim.dispatchOnStart();
@@ -237,10 +210,10 @@
     /** Create state animation to control non-overview components. */
     private void updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config) {
         config.duration = (long) (Math.max(mXRange, mYRange) * 2);
-        config.animFlags = config.animFlags | SKIP_OVERVIEW;
+        config.animFlags |= SKIP_OVERVIEW | SKIP_SCRIM;
         mNonOverviewAnim = mLauncher.getStateManager()
-                .createAnimationToNewWorkspace(toState, config)
-                .setOnCancelRunnable(this::clearState);
+                .createAnimationToNewWorkspace(toState, config);
+        mNonOverviewAnim.getTarget().addListener(mClearStateOnCancelListener);
     }
 
     private void setupOverviewAnimators() {
@@ -249,20 +222,24 @@
 
         // Set RecentView's initial properties.
         RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]);
-        ADJACENT_PAGE_OFFSET.set(mRecentsView, 1f);
+        ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, 1f);
         mRecentsView.setContentAlpha(1);
         mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
         mLauncher.getActionsView().getVisibilityAlpha().setValue(
-                (fromState.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1f : 0f);
+                (fromState.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 ? 1f : 0f);
 
         float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher);
         // As we drag right, animate the following properties:
         //   - RecentsView translationX
         //   - OverviewScrim
+        //   - RecentsView fade (if it's empty)
         PendingAnimation xAnim = new PendingAnimation((long) (mXRange * 2));
-        xAnim.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], LINEAR);
-        xAnim.setFloat(mLauncher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS,
-                toState.getOverviewScrimAlpha(mLauncher), LINEAR);
+        xAnim.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1], LINEAR);
+        xAnim.setViewBackgroundColor(mLauncher.getScrimView(),
+                toState.getWorkspaceScrimColor(mLauncher), LINEAR);
+        if (mRecentsView.getTaskViewCount() == 0) {
+            xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
+        }
         mXOverviewAnim = xAnim.createPlaybackController();
         mXOverviewAnim.dispatchOnStart();
 
@@ -301,22 +278,9 @@
         mIsHomeScreenVisible = FADE_OUT_INTERPOLATOR.getInterpolation(xProgress)
                 <= 1 - ALPHA_CUTOFF_THRESHOLD;
 
-        if (wasHomeScreenVisible && !mIsHomeScreenVisible) {
-            // Get the shelf all the way offscreen so it pops up when we decide to peek it.
-            mShelfPeekAnim.setShelfState(HIDE, LINEAR, 0);
-        }
-
-        // Only allow motion pause if the home screen is invisible, since some
-        // home screen elements will appear in the shelf on motion pause.
-        mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible
-                || -displacement.y < mMotionPauseMinDisplacement);
+        mMotionPauseDetector.setDisallowPause(-displacement.y < mMotionPauseMinDisplacement);
         mMotionPauseDetector.addPosition(ev);
 
-        if (mIsHomeScreenVisible) {
-            // Cancel the shelf anim so it doesn't clobber mNonOverviewAnim.
-            mShelfPeekAnim.setShelfState(CANCEL, LINEAR, 0);
-        }
-
         if (mXOverviewAnim != null) {
             mXOverviewAnim.setPlayFraction(xProgress);
         }
@@ -331,16 +295,17 @@
         boolean horizontalFling = mSwipeDetector.isFling(velocity.x);
         boolean verticalFling = mSwipeDetector.isFling(velocity.y);
         boolean noFling = !horizontalFling && !verticalFling;
-        int logAction = noFling ? Touch.SWIPE : Touch.FLING;
         if (mMotionPauseDetector.isPaused() && noFling) {
             cancelAnimations();
 
-            Animator overviewAnim = mLauncher.createAtomicAnimationFactory()
-                    .createStateElementAnimation(INDEX_PAUSE_TO_OVERVIEW_ANIM);
+            StateAnimationConfig config = new StateAnimationConfig();
+            config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
+            Animator overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
+                    mStartState, OVERVIEW, config);
             overviewAnim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    onAnimationToStateCompleted(OVERVIEW, logAction);
+                    onAnimationToStateCompleted(OVERVIEW);
                 }
             });
             overviewAnim.start();
@@ -415,18 +380,18 @@
             // animation as from an app.
             StateAnimationConfig config = new StateAnimationConfig();
             // Update mNonOverviewAnim to do nothing so it doesn't interfere.
-            config.animFlags = 0;
+            config.animFlags = SKIP_ALL_ANIMATIONS;
             updateNonOverviewAnim(targetState, config);
             nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
 
-            new StaggeredWorkspaceAnim(mLauncher, velocity.y, false /* animateOverviewScrim */)
-                    .start();
+            new WorkspaceRevealAnim(mLauncher, false /* animateOverviewScrim */).start();
         } else {
             boolean canceled = targetState == NORMAL;
             if (canceled) {
                 // Let the state manager know that the animation didn't go to the target state,
                 // but don't clean up yet (we already clean up when the animation completes).
-                mNonOverviewAnim.dispatchOnCancelWithoutCancelRunnable();
+                mNonOverviewAnim.getTarget().removeListener(mClearStateOnCancelListener);
+                mNonOverviewAnim.dispatchOnCancel();
             }
             float startProgress = mNonOverviewAnim.getProgressFraction();
             float endProgress = canceled ? 0 : 1;
@@ -435,7 +400,7 @@
         }
 
         nonOverviewAnim.setDuration(Math.max(xDuration, yDuration));
-        mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState, logAction));
+        mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState));
 
         cancelAnimations();
         xOverviewAnim.start();
@@ -443,25 +408,15 @@
         nonOverviewAnim.start();
     }
 
-    private void onAnimationToStateCompleted(LauncherState targetState, int logAction) {
-        mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
-                getDirectionForLog(), mSwipeDetector.getDownX(), mSwipeDetector.getDownY(),
-                LauncherLogProto.ContainerType.NAVBAR,
-                mStartState.containerType,
-                targetState.containerType,
-                mLauncher.getWorkspace().getCurrentPage());
+    private void onAnimationToStateCompleted(LauncherState targetState) {
         mLauncher.getStatsLogManager().logger()
                 .withSrcState(LAUNCHER_STATE_HOME)
-                .withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
-                .log(getLauncherAtomEvent(mStartState.containerType, targetState.containerType,
+                .withDstState(targetState.statsLogOrdinal)
+                .log(getLauncherAtomEvent(mStartState.statsLogOrdinal, targetState.statsLogOrdinal,
                         targetState.ordinal > mStartState.ordinal
                                 ? LAUNCHER_UNKNOWN_SWIPEUP
                                 : LAUNCHER_UNKNOWN_SWIPEDOWN));
-        mLauncher.getStateManager().goToState(targetState, false, this::clearState);
-    }
-
-    private int getDirectionForLog() {
-        return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT;
+        mLauncher.getStateManager().goToState(targetState, false, forEndCallback(this::clearState));
     }
 
     private void cancelAnimations() {
@@ -474,7 +429,6 @@
         if (mYOverviewAnim != null) {
             mYOverviewAnim.cancelAnimation();
         }
-        mShelfPeekAnim.setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
         mMotionPauseDetector.clear();
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
similarity index 97%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
index 845699a..4df0f63 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -76,7 +76,7 @@
      * @return the animation
      */
     PendingAnimation createSwipeDownToTaskAppAnimation(long duration, Interpolator interpolator) {
-        mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
+        mRecentsView.setCurrentPage(mRecentsView.getDestinationPage());
         TaskView taskView = mRecentsView.getCurrentPageTaskView();
         if (taskView == null) {
             throw new IllegalStateException("There is no task view to animate to.");
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index a684b9d..3c83d25 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -23,20 +23,10 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.util.Log;
 import android.view.MotionEvent;
-import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
@@ -44,17 +34,13 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
 /**
  * Touch controller for handling various state transitions in portrait UI.
@@ -64,48 +50,45 @@
     private static final String TAG = "PortraitStatesTouchCtrl";
 
     /**
-     * The progress at which all apps content will be fully visible when swiping up from overview.
+     * The progress at which all apps content will be fully visible.
      */
-    protected static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
+    protected static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
 
     /**
-     * The progress at which recents will begin fading out when swiping up from overview.
+     * Minimum clamping progress for fading in all apps content
      */
-    private static final float RECENTS_FADE_THRESHOLD = 0.88f;
+    protected 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;
+
+    /**
+     * Maximum clamping progress for opaque all apps scrim
+     */
+    protected static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
 
     private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
 
-    private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
-
-    private final boolean mAllowDragToOverview;
-
-    // If true, we will finish the current animation instantly on second touch.
-    private boolean mFinishFastOnSecondTouch;
-
-    public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
+    public PortraitStatesTouchController(Launcher l) {
         super(l, SingleAxisSwipeDetector.VERTICAL);
         mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
-        mAllowDragToOverview = allowDragToOverview;
     }
 
     @Override
     protected boolean canInterceptTouch(MotionEvent ev) {
+        // If we are swiping to all apps instead of overview, allow it from anywhere.
+        boolean interceptAnywhere = mLauncher.isInState(NORMAL);
         if (mCurrentAnimation != null) {
-            if (mFinishFastOnSecondTouch) {
-                mCurrentAnimation.getAnimationPlayer().end();
-            }
-
             AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
-            if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) {
+            if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()
+                    || interceptAnywhere) {
                 // If we are already animating from a previous state, we can intercept as long as
                 // the touch is below the current all apps progress (to allow for double swipe).
                 return true;
             }
-            // Otherwise, make sure everything is settled and don't intercept so they can scroll
-            // recents, dismiss a task, etc.
-            if (mAtomicAnim != null) {
-                mAtomicAnim.end();
-            }
+            // Otherwise, don't intercept so they can scroll recents, dismiss a task, etc.
             return false;
         }
         if (mLauncher.isInState(ALL_APPS)) {
@@ -118,9 +101,7 @@
                 return false;
             }
         } else {
-            // If we are swiping to all apps instead of overview, allow it from anywhere.
-            boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview;
-            // For all other states, only listen if the event originated below the hotseat height
+            // For non-normal states, only listen if the event originated below the hotseat height
             if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) {
                 return false;
             }
@@ -133,87 +114,35 @@
 
     @Override
     protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "PortraitStatesTouchController.getTargetState");
-        }
         if (fromState == ALL_APPS && !isDragTowardPositive) {
-            // Should swipe down go to OVERVIEW instead?
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
-                        "PortraitStatesTouchController.getTargetState 1");
-            }
-            if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(mLauncher)) {
-                // Don't allow swiping down to overview.
-                return NORMAL;
-            }
-            return TouchInteractionService.isConnected() ?
-                    mLauncher.getStateManager().getLastState() : NORMAL;
+            return NORMAL;
         } else if (fromState == OVERVIEW) {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
-                        "PortraitStatesTouchController.getTargetState 2");
-            }
-            LauncherState positiveDragTarget = ALL_APPS;
-            if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(mLauncher)) {
-                // Don't allow swiping up to all apps.
-                positiveDragTarget = OVERVIEW;
-            }
-            return isDragTowardPositive ? positiveDragTarget : NORMAL;
+            return isDragTowardPositive ? OVERVIEW : NORMAL;
         } else if (fromState == NORMAL && isDragTowardPositive) {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
-                        "PortraitStatesTouchController.getTargetState 3");
-            }
-            int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
-            return mAllowDragToOverview && TouchInteractionService.isConnected()
-                    && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0
-                    ? OVERVIEW : ALL_APPS;
+            return ALL_APPS;
         }
         return fromState;
     }
 
-    @Override
-    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
-        return isTouchOverHotseat(mLauncher, ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
-    }
-
-    private StateAnimationConfig getNormalToOverviewAnimation() {
-        mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
-
-        StateAnimationConfig builder = new StateAnimationConfig();
-        builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
-        return builder;
-    }
-
-    private static StateAnimationConfig getOverviewToAllAppsAnimation() {
-        StateAnimationConfig builder = new StateAnimationConfig();
-        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
-                0, ALL_APPS_CONTENT_FADE_THRESHOLD));
-        builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(DEACCEL,
-                RECENTS_FADE_THRESHOLD, 1));
-        return builder;
-    }
-
-    private StateAnimationConfig getAllAppsToOverviewAnimation() {
-        StateAnimationConfig builder = new StateAnimationConfig();
-        builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
-                1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
-        builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(ACCEL,
-                0f, 1 - RECENTS_FADE_THRESHOLD));
-        return builder;
-    }
-
     private StateAnimationConfig getNormalToAllAppsAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
         builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
-                0, ALL_APPS_CONTENT_FADE_THRESHOLD));
+                ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD,
+                ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
+        builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(ACCEL,
+                ALL_APPS_SCRIM_VISIBLE_THRESHOLD,
+                ALL_APPS_SCRIM_OPAQUE_THRESHOLD));
         return builder;
     }
 
     private StateAnimationConfig getAllAppsToNormalAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
         builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
-                1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
+                1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
+                1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
+        builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(DEACCEL,
+                1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
+                1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
         return builder;
     }
 
@@ -221,24 +150,18 @@
     protected StateAnimationConfig getConfigForStates(
             LauncherState fromState, LauncherState toState) {
         final StateAnimationConfig config;
-        if (fromState == NORMAL && toState == OVERVIEW) {
-            config = getNormalToOverviewAnimation();
-        } else if (fromState == OVERVIEW && toState == ALL_APPS) {
-            config = getOverviewToAllAppsAnimation();
-        } else if (fromState == ALL_APPS && toState == OVERVIEW) {
-            config = getAllAppsToOverviewAnimation();
-        } else if (fromState == NORMAL && toState == ALL_APPS) {
+        if (fromState == NORMAL && toState == ALL_APPS) {
             config = getNormalToAllAppsAnimation();
         } else if (fromState == ALL_APPS && toState == NORMAL) {
             config = getAllAppsToNormalAnimation();
-        }  else {
+        } else {
             config = new StateAnimationConfig();
         }
         return config;
     }
 
     @Override
-    protected float initCurrentAnimation(@AnimationFlags int animFlags) {
+    protected float initCurrentAnimation() {
         float range = getShiftRange();
         long maxAccuracy = (long) (2 * range);
 
@@ -249,32 +172,31 @@
 
         final StateAnimationConfig config = totalShift == 0 ? new StateAnimationConfig()
                 : getConfigForStates(mFromState, mToState);
-        config.animFlags = updateAnimComponentsOnReinit(animFlags);
         config.duration = maxAccuracy;
 
-        cancelPendingAnim();
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
+            mCurrentAnimation.dispatchOnCancel();
+        }
 
+        mGoingBetweenStates = true;
         if (mFromState == OVERVIEW && mToState == NORMAL
                 && mOverviewPortraitStateTouchHelper.shouldSwipeDownReturnToApp()) {
             // Reset the state manager, when changing the interaction mode
             mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
-            mPendingAnimation = mOverviewPortraitStateTouchHelper
-                    .createSwipeDownToTaskAppAnimation(maxAccuracy, Interpolators.LINEAR);
-            Runnable onCancelRunnable = () -> {
-                cancelPendingAnim();
-                clearState();
-            };
-            mCurrentAnimation = mPendingAnimation.createPlaybackController()
-                    .setOnCancelRunnable(onCancelRunnable);
+            mGoingBetweenStates = false;
+            mCurrentAnimation = mOverviewPortraitStateTouchHelper
+                    .createSwipeDownToTaskAppAnimation(maxAccuracy, Interpolators.LINEAR)
+                    .createPlaybackController();
             mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
             RecentsView recentsView = mLauncher.getOverviewPanel();
             totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
                     mLauncher.getDeviceProfile(), recentsView.getPagedOrientationHandler());
         } else {
             mCurrentAnimation = mLauncher.getStateManager()
-                    .createAnimationToNewWorkspace(mToState, config)
-                    .setOnCancelRunnable(this::clearState);
+                    .createAnimationToNewWorkspace(mToState, config);
         }
+        mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener);
 
         if (totalShift == 0) {
             totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
@@ -283,53 +205,9 @@
         return 1 / totalShift;
     }
 
-    /**
-     * Give subclasses the chance to update the animation when we re-initialize towards a new state.
-     */
-    @AnimationFlags
-    protected int updateAnimComponentsOnReinit(@AnimationFlags int animComponents) {
-        return animComponents;
-    }
-
-    private void cancelPendingAnim() {
-        if (mPendingAnimation != null) {
-            mPendingAnimation.finish(false, Touch.SWIPE);
-            mPendingAnimation = null;
-        }
-    }
-
     @Override
-    protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
-            LauncherState targetState, float velocity, boolean isFling) {
-        super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
-                velocity, isFling);
-        handleFirstSwipeToOverview(animator, expectedDuration, targetState, velocity, isFling);
-    }
-
-    private void handleFirstSwipeToOverview(final ValueAnimator animator,
-            final long expectedDuration, final LauncherState targetState, final float velocity,
-            final boolean isFling) {
-        if (UNSTABLE_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
-                && targetState == OVERVIEW) {
-            mFinishFastOnSecondTouch = true;
-        } else  if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
-            mFinishFastOnSecondTouch = true;
-            if (isFling && expectedDuration != 0) {
-                // Update all apps interpolator to add a bit of overshoot starting from currFraction
-                final float currFraction = mCurrentAnimation.getProgressFraction();
-                mAllAppsInterpolatorWrapper.baseInterpolator = Interpolators.clampToProgress(
-                        Interpolators.overshootInterpolatorForVelocity(velocity), currFraction, 1);
-                animator.setDuration(Math.min(expectedDuration, ATOMIC_DURATION))
-                        .setInterpolator(LINEAR);
-            }
-        } else {
-            mFinishFastOnSecondTouch = false;
-        }
-    }
-
-    @Override
-    protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
-        super.onSwipeInteractionCompleted(targetState, logAction);
+    protected void onSwipeInteractionCompleted(LauncherState targetState) {
+        super.onSwipeInteractionCompleted(targetState);
         if (mStartState == NORMAL && targetState == OVERVIEW) {
             SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
         }
@@ -339,7 +217,7 @@
      * Whether the motion event is over the hotseat.
      *
      * @param launcher the launcher activity
-     * @param ev the event to check
+     * @param ev       the event to check
      * @return true if the event is over the hotseat
      */
     static boolean isTouchOverHotseat(Launcher launcher, MotionEvent ev) {
@@ -352,13 +230,43 @@
         return launcher.getDragLayer().getHeight() - hotseatHeight;
     }
 
-    private static class InterpolatorWrapper implements Interpolator {
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                InteractionJankMonitorWrapper.begin(
+                        mLauncher.getRootView(), InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+                break;
 
-        public TimeInterpolator baseInterpolator = LINEAR;
-
-        @Override
-        public float getInterpolation(float v) {
-            return baseInterpolator.getInterpolation(v);
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                InteractionJankMonitorWrapper.cancel(
+                        InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+                break;
         }
+        return super.onControllerInterceptTouchEvent(ev);
+
+    }
+
+    @Override
+    protected void onReinitToState(LauncherState newToState) {
+        super.onReinitToState(newToState);
+        if (newToState != ALL_APPS) {
+            InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+        }
+    }
+
+    @Override
+    protected void onReachedFinalState(LauncherState toState) {
+        super.onReinitToState(toState);
+        if (toState == ALL_APPS) {
+            InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+        }
+    }
+
+    @Override
+    protected void clearState() {
+        super.clearState();
+        InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
similarity index 83%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index c643858..f0ef9cc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
@@ -28,7 +29,9 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 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.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -41,14 +44,12 @@
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskUtils;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 /**
  * Handles quick switching to a recent task from the home screen.
@@ -92,24 +93,30 @@
     @Override
     public void onDragStart(boolean start, float startDisplacement) {
         super.onDragStart(start, startDisplacement);
-        mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
-        ActivityManagerWrapper.getInstance()
-                .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        mStartContainerType = LAUNCHER_STATE_BACKGROUND;
+        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
     }
 
     @Override
-    protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
-        super.onSwipeInteractionCompleted(targetState, logAction);
+    protected void onSwipeInteractionCompleted(LauncherState targetState) {
+        super.onSwipeInteractionCompleted(targetState);
     }
 
     @Override
-    protected float initCurrentAnimation(int animComponents) {
+    protected float initCurrentAnimation() {
         StateAnimationConfig config = new StateAnimationConfig();
         setupInterpolators(config);
         config.duration = (long) (getShiftRange() * 2);
+
+        // Set RecentView's initial properties for coming in from the side.
+        RECENTS_SCALE_PROPERTY.set(mOverviewPanel,
+                QUICK_SWITCH.getOverviewScaleAndOffset(mLauncher)[0] * 0.85f);
+        ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mOverviewPanel, 1f);
+        mOverviewPanel.setContentAlpha(1);
+
         mCurrentAnimation = mLauncher.getStateManager()
-                .createAnimationToNewWorkspace(mToState, config)
-                .setOnCancelRunnable(this::clearState);
+                .createAnimationToNewWorkspace(mToState, config);
+        mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener);
         mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator ->
                 updateFullscreenProgress((Float) valueAnimator.getAnimatedValue()));
         return 1 / getShiftRange();
@@ -139,28 +146,20 @@
 
     private void updateFullscreenProgress(float progress) {
         mOverviewPanel.setFullscreenProgress(progress);
-        int sysuiFlags = 0;
         if (progress > UPDATE_SYSUI_FLAGS_THRESHOLD) {
+            int sysuiFlags = 0;
             TaskView tv = mOverviewPanel.getTaskViewAt(0);
             if (tv != null) {
                 sysuiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
             }
+            mLauncher.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, sysuiFlags);
+        } else {
+            mLauncher.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
         }
-        mLauncher.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
     }
 
     @Override
     protected float getShiftRange() {
         return mLauncher.getDeviceProfile().widthPx / 2f;
     }
-
-    @Override
-    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
-        return LauncherLogProto.ContainerType.NAVBAR;
-    }
-
-    @Override
-    protected int getDirectionForLog() {
-        return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT;
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 7a7cbb4..fe69c9b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -15,10 +15,11 @@
  */
 package com.android.launcher3.uioverrides.touchcontrollers;
 
+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_UP;
-import static android.view.MotionEvent.ACTION_CANCEL;
+
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN;
 
 import android.graphics.PointF;
@@ -32,12 +33,9 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TouchController;
-
 import com.android.quickstep.SystemUiProxy;
+
 import java.io.PrintWriter;
 
 /**
@@ -134,9 +132,6 @@
         int action = ev.getAction();
         if (action == ACTION_UP || action == ACTION_CANCEL) {
             dispatchTouchEvent(ev);
-            mLauncher.getUserEventDispatcher().logActionOnContainer(action == ACTION_UP ?
-                    Touch.FLING : Touch.SWIPE, Direction.DOWN, ContainerType.WORKSPACE,
-                    mLauncher.getWorkspace().getCurrentPage());
             mLauncher.getStatsLogManager().logger()
                     .log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN);
             setWindowSlippery(false);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
similarity index 70%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 2cfbc2b..180af0b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -16,13 +16,12 @@
 package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.os.SystemClock;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -30,14 +29,15 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 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;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.FlingBlockCheck;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
@@ -52,8 +52,9 @@
         extends AnimatorListenerAdapter implements TouchController,
         SingleAxisSwipeDetector.Listener {
 
-    // Progress after which the transition is assumed to be a success in case user does not fling
-    public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
+    private static final float ANIMATION_PROGRESS_FRACTION_MIDPOINT = 0.5f;
+    private static final long MIN_TASK_DISMISS_ANIMATION_DURATION = 300;
+    private static final long MAX_TASK_DISMISS_ANIMATION_DURATION = 600;
 
     protected final T mActivity;
     private final SingleAxisSwipeDetector mDetector;
@@ -61,9 +62,10 @@
     private final int[] mTempCords = new int[2];
     private final boolean mIsRtl;
 
-    private PendingAnimation mPendingAnimation;
     private AnimatorPlaybackController mCurrentAnimation;
     private boolean mCurrentAnimationIsGoingUp;
+    private boolean mAllowGoingUp;
+    private boolean mAllowGoingDown;
 
     private boolean mNoIntercept;
 
@@ -71,6 +73,7 @@
     private float mProgressMultiplier;
     private float mEndDisplacement;
     private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
+    private Float mOverrideVelocity = null;
 
     private TaskView mTaskBeingDragged;
 
@@ -79,7 +82,7 @@
         mRecentsView = activity.getOverviewPanel();
         mIsRtl = Utilities.isRtl(activity.getResources());
         SingleAxisSwipeDetector.Direction dir =
-            mRecentsView.getPagedOrientationHandler().getOppositeSwipeDirection();
+                mRecentsView.getPagedOrientationHandler().getUpDownSwipeDirection();
         mDetector = new SingleAxisSwipeDetector(activity, this, dir);
     }
 
@@ -153,15 +156,27 @@
                             break;
                         }
                         mTaskBeingDragged = view;
-                        if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
+                        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.
-                            directionsToDetectScroll = DIRECTION_POSITIVE;
+                            // 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.
-                            directionsToDetectScroll = i == mRecentsView.getCurrentPage()
-                                    ? DIRECTION_BOTH : DIRECTION_POSITIVE;
+                            mAllowGoingUp = true;
+                            if (i == mRecentsView.getCurrentPage()) {
+                                mAllowGoingDown = true;
+                                directionsToDetectScroll = DIRECTION_BOTH;
+                            } else {
+                                mAllowGoingDown = false;
+                                directionsToDetectScroll = upDirection;
+                            }
                         }
                         break;
                     }
@@ -194,18 +209,14 @@
             // No need to init
             return;
         }
-        int scrollDirections = mDetector.getScrollDirections();
-        if (goingUp && ((scrollDirections & DIRECTION_POSITIVE) == 0)
-                || !goingUp && ((scrollDirections & DIRECTION_NEGATIVE) == 0)) {
+        if ((goingUp && !mAllowGoingUp) || (!goingUp && !mAllowGoingDown)) {
             // Trying to re-init in an unsupported direction.
             return;
         }
         if (mCurrentAnimation != null) {
             mCurrentAnimation.setPlayFraction(0);
-        }
-        if (mPendingAnimation != null) {
-            mPendingAnimation.finish(false, Touch.SWIPE);
-            mPendingAnimation = null;
+            mCurrentAnimation.getTarget().removeListener(this);
+            mCurrentAnimation.dispatchOnCancel();
         }
 
         PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
@@ -218,15 +229,16 @@
         // The interpolator controlling the most prominent visual movement. We use this to determine
         // whether we passed SUCCESS_TRANSITION_PROGRESS.
         final Interpolator currentInterpolator;
+        PendingAnimation pa;
         if (goingUp) {
             currentInterpolator = Interpolators.LINEAR;
-            mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
+            pa = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
                     true /* animateTaskView */, true /* removeTask */, maxDuration);
 
             mEndDisplacement = -secondaryTaskDimension;
         } else {
             currentInterpolator = Interpolators.ZOOM_IN;
-            mPendingAnimation = mRecentsView.createTaskLaunchAnimation(
+            pa = mRecentsView.createTaskLaunchAnimation(
                     mTaskBeingDragged, maxDuration, currentInterpolator);
 
             // Since the thumbnail is what is filling the screen, based the end displacement on it.
@@ -236,12 +248,8 @@
             mEndDisplacement = secondaryLayerDimension - mTempCords[1];
         }
         mEndDisplacement *= verticalFactor;
+        mCurrentAnimation = pa.createPlaybackController();
 
-        if (mCurrentAnimation != null) {
-            mCurrentAnimation.setOnCancelRunnable(null);
-        }
-        mCurrentAnimation = mPendingAnimation.createPlaybackController()
-                .setOnCancelRunnable(this::clearState);
         // Setting this interpolator doesn't affect the visual motion, but is used to determine
         // whether we successfully reached the target state in onDragEnd().
         mCurrentAnimation.getTarget().setInterpolator(currentInterpolator);
@@ -262,6 +270,7 @@
             mCurrentAnimation.pause();
         }
         mFlingBlockCheck.unblockFling();
+        mOverrideVelocity = null;
     }
 
     @Override
@@ -276,22 +285,55 @@
         } else {
             mFlingBlockCheck.onEvent();
         }
-        mCurrentAnimation.setPlayFraction(Utilities.boundToRange(
-                totalDisplacement * mProgressMultiplier, 0, 1));
 
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (mRecentsView.getCurrentPage() != 0 || isGoingUp) {
-                mRecentsView.redrawLiveTile(true);
+        if (isGoingUp) {
+            if (mCurrentAnimation.getProgressFraction() < ANIMATION_PROGRESS_FRACTION_MIDPOINT) {
+                // Halve the value when dismissing, as we are animating the drag across the full
+                // length for only the first half of the progress
+                mCurrentAnimation.setPlayFraction(
+                        Utilities.boundToRange(totalDisplacement * mProgressMultiplier / 2, 0, 1));
+            } else {
+                // Set mOverrideVelocity to control task dismiss velocity in onDragEnd
+                int velocityDimenId = R.dimen.default_task_dismiss_drag_velocity;
+                if (mRecentsView.showAsGrid()) {
+                    if (mTaskBeingDragged.isFocusedTask()) {
+                        velocityDimenId =
+                                R.dimen.default_task_dismiss_drag_velocity_grid_focus_task;
+                    } else {
+                        velocityDimenId = R.dimen.default_task_dismiss_drag_velocity_grid;
+                    }
+                }
+                mOverrideVelocity = -mTaskBeingDragged.getResources().getDimension(velocityDimenId);
+
+                // Once halfway through task dismissal interpolation, switch from reversible
+                // dragging-task animation to playing the remaining task translation animations
+                final long now = SystemClock.uptimeMillis();
+                MotionEvent upAction = MotionEvent.obtain(now, now,
+                        MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
+                mDetector.onTouchEvent(upAction);
+                upAction.recycle();
             }
+        } else {
+            mCurrentAnimation.setPlayFraction(
+                    Utilities.boundToRange(totalDisplacement * mProgressMultiplier, 0, 1));
         }
+
         return true;
     }
 
     @Override
     public void onDragEnd(float velocity) {
+        if (mOverrideVelocity != null) {
+            velocity = mOverrideVelocity;
+            mOverrideVelocity = null;
+        }
+        // Limit velocity, as very large scalar values make animations play too quickly
+        float maxTaskDismissDragVelocity = mTaskBeingDragged.getResources().getDimension(
+                R.dimen.max_task_dismiss_drag_velocity);
+        velocity = Utilities.boundToRange(velocity, -maxTaskDismissDragVelocity,
+                maxTaskDismissDragVelocity);
         boolean fling = mDetector.isFling(velocity);
         final boolean goingToEnd;
-        final int logAction;
         boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
         if (blockedFling) {
             fling = false;
@@ -300,11 +342,9 @@
         float progress = mCurrentAnimation.getProgressFraction();
         float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
         if (fling) {
-            logAction = Touch.FLING;
             boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl);
             goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
         } else {
-            logAction = Touch.SWIPE;
             goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
         }
         long animationDuration = BaseSwipeDetector.calculateDuration(
@@ -312,25 +352,16 @@
         if (blockedFling && !goingToEnd) {
             animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
         }
+        // Due to very high or low velocity dismissals, animation durations can be inconsistently
+        // long or short. Bound the duration for animation of task translations for a more
+        // standardized feel.
+        animationDuration = Utilities.boundToRange(animationDuration,
+                MIN_TASK_DISMISS_ANIMATION_DURATION, MAX_TASK_DISMISS_ANIMATION_DURATION);
 
-        mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> {
-                if (mRecentsView.getCurrentPage() != 0 || mCurrentAnimationIsGoingUp) {
-                    mRecentsView.redrawLiveTile(true);
-                }
-            });
-        }
+        mCurrentAnimation.setEndAction(this::clearState);
         mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,
-                velocity, mEndDisplacement, animationDuration);
-    }
-
-    private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
-        if (mPendingAnimation != null) {
-            mPendingAnimation.finish(wasSuccess, logAction);
-            mPendingAnimation = null;
-        }
-        clearState();
+                velocity * orientationHandler.getSecondaryTranslationDirectionFactor(),
+                mEndDisplacement, animationDuration);
     }
 
     private void clearState() {
@@ -338,9 +369,5 @@
         mDetector.setDetectableScrollConditions(0, false);
         mTaskBeingDragged = null;
         mCurrentAnimation = null;
-        if (mPendingAnimation != null) {
-            mPendingAnimation.finish(false, Touch.SWIPE);
-            mPendingAnimation = null;
-        }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
similarity index 91%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
index 0ed5291..8f9c014 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
@@ -32,8 +32,8 @@
     }
 
     @Override
-    protected float initCurrentAnimation(int animComponents) {
-        float multiplier = super.initCurrentAnimation(animComponents);
+    protected float initCurrentAnimation() {
+        float multiplier = super.initCurrentAnimation();
         return mLauncher.getDeviceProfile().isSeascape() ? multiplier : -multiplier;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
new file mode 100644
index 0000000..e2747df
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
@@ -0,0 +1,175 @@
+/*
+ * 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.uioverrides.touchcontrollers;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
+import static com.android.launcher3.AbstractFloatingView.getOpenView;
+import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+
+import android.animation.ValueAnimator;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.views.AllAppsEduView;
+
+/**
+ * Touch controller for handling edge swipes in 2-button mode
+ */
+public class TwoButtonNavbarTouchController extends AbstractStateChangeTouchController {
+
+    private static final int MAX_NUM_SWIPES_TO_TRIGGER_EDU = 3;
+
+    private static final String TAG = "2BtnNavbarTouchCtrl";
+
+    private final boolean mIsTransposed;
+
+    // If true, we will finish the current animation instantly on second touch.
+    private boolean mFinishFastOnSecondTouch;
+
+    private int mContinuousTouchCount = 0;
+
+    public TwoButtonNavbarTouchController(Launcher l) {
+        super(l, l.getDeviceProfile().isVerticalBarLayout()
+                ? SingleAxisSwipeDetector.HORIZONTAL : SingleAxisSwipeDetector.VERTICAL);
+        mIsTransposed = l.getDeviceProfile().isVerticalBarLayout();
+    }
+
+    @Override
+    protected boolean canInterceptTouch(MotionEvent ev) {
+        boolean canIntercept = canInterceptTouchInternal(ev);
+        if (!canIntercept) {
+            mContinuousTouchCount = 0;
+        }
+        return canIntercept;
+    }
+
+    private boolean canInterceptTouchInternal(MotionEvent ev) {
+        if (mCurrentAnimation != null) {
+            if (mFinishFastOnSecondTouch) {
+                mCurrentAnimation.getAnimationPlayer().end();
+            }
+
+            // If we are already animating from a previous state, we can intercept.
+            return true;
+        }
+        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+            return false;
+        }
+        if ((ev.getEdgeFlags() & EDGE_NAV_BAR) == 0) {
+            return false;
+        }
+        if (!mIsTransposed && mLauncher.isInState(OVERVIEW)) {
+            return true;
+        }
+        return mLauncher.isInState(NORMAL);
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        boolean intercept = super.onControllerInterceptTouchEvent(ev);
+        return intercept;
+    }
+
+    @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        if (mIsTransposed) {
+            boolean draggingFromNav =
+                    mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
+            return draggingFromNav ? HINT_STATE_TWO_BUTTON : NORMAL;
+        } else {
+            LauncherState startState = mStartState != null ? mStartState : fromState;
+            return isDragTowardPositive ^ (startState == OVERVIEW) ? HINT_STATE_TWO_BUTTON : NORMAL;
+        }
+    }
+
+    @Override
+    protected void onReinitToState(LauncherState newToState) {
+        super.onReinitToState(newToState);
+    }
+
+    @Override
+    protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
+            LauncherState targetState, float velocity, boolean isFling) {
+        super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
+                velocity, isFling);
+        mFinishFastOnSecondTouch = !mIsTransposed && mFromState == NORMAL;
+
+        if (targetState == HINT_STATE_TWO_BUTTON) {
+            // We were going to HINT_STATE_TWO_BUTTON, but end that animation immediately so we go
+            // to OVERVIEW instead.
+            animator.setDuration(0);
+        }
+    }
+
+    @Override
+    protected float getShiftRange() {
+        // Should be in sync with TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT
+        return LayoutUtils.getDefaultSwipeHeight(mLauncher, mLauncher.getDeviceProfile());
+    }
+
+    @Override
+    protected float initCurrentAnimation() {
+        float range = getShiftRange();
+        long maxAccuracy = (long) (2 * range);
+        mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
+                maxAccuracy);
+        return (mLauncher.getDeviceProfile().isSeascape() ? 1 : -1) / range;
+    }
+
+    @Override
+    protected void updateProgress(float fraction) {
+        super.updateProgress(fraction);
+
+        // We have reached HINT_STATE, end the gesture now to go to OVERVIEW.
+        if (fraction >= 1 && mToState == HINT_STATE_TWO_BUTTON) {
+            final long now = SystemClock.uptimeMillis();
+            MotionEvent event = MotionEvent.obtain(now, now,
+                    MotionEvent.ACTION_UP, 0.0f, 0.0f, 0);
+            mDetector.onTouchEvent(event);
+            event.recycle();
+        }
+    }
+
+    @Override
+    protected void onSwipeInteractionCompleted(LauncherState targetState) {
+        super.onSwipeInteractionCompleted(targetState);
+        if (!mIsTransposed) {
+            mContinuousTouchCount++;
+        }
+        if (mStartState == NORMAL && targetState == HINT_STATE_TWO_BUTTON) {
+            SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+        } else if (targetState == NORMAL
+                && mContinuousTouchCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
+            mContinuousTouchCount = 0;
+            if (getOpenView(mLauncher, TYPE_ALL_APPS_EDU) == null) {
+                AllAppsEduView.show(mLauncher);
+            }
+        }
+        mStartState = null;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
new file mode 100644
index 0000000..ac1772c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -0,0 +1,1817 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static android.view.Surface.ROTATION_0;
+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.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
+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.util.DisplayController.getSingleFrameMs;
+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;
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
+import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
+import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
+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.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;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewTreeObserver.OnDrawListener;
+import android.view.ViewTreeObserver.OnScrollChangedListener;
+import android.view.WindowInsets;
+import android.view.animation.Interpolator;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.tracing.InputConsumerProto;
+import com.android.launcher3.tracing.SwipeHandlerProto;
+import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.VibratorWrapper;
+import com.android.launcher3.util.WindowBounds;
+import com.android.quickstep.BaseActivityInterface.AnimationFactory;
+import com.android.quickstep.GestureState.GestureEndTarget;
+import com.android.quickstep.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.MotionPauseDetector;
+import com.android.quickstep.util.ProtoTracer;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.RectFSpringAnim;
+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.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.systemui.shared.system.LatencyTrackerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Handles the navigation gestures when Launcher is the default home activity.
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
+        Q extends RecentsView, S extends BaseState<S>>
+        extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
+        RecentsAnimationCallbacks.RecentsAnimationListener {
+    private static final String TAG = "AbsSwipeUpHandler";
+
+    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[17] : null;
+
+    protected final BaseActivityInterface<S, T> mActivityInterface;
+    protected final InputConsumerProxy mInputConsumerProxy;
+    protected final ActivityInitListener mActivityInitListener;
+    // Callbacks to be made once the recents animation starts
+    private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
+    private final OnScrollChangedListener mOnRecentsScrollListener = this::onRecentsViewScroll;
+
+    // Null if the recents animation hasn't started yet or has been canceled or finished.
+    protected @Nullable RecentsAnimationController mRecentsAnimationController;
+    protected RecentsAnimationTargets mRecentsAnimationTargets;
+    protected T mActivity;
+    protected Q mRecentsView;
+    protected Runnable mGestureEndCallback;
+    protected MultiStateCallback mStateCallback;
+    protected boolean mCanceled;
+    private boolean mRecentsViewScrollLinked = false;
+
+    private static int getFlagForIndex(int index, String name) {
+        if (DEBUG_STATES) {
+            STATE_NAMES[index] = name;
+        }
+        return 1 << index;
+    }
+
+    // Launcher UI related states
+    protected static final int STATE_LAUNCHER_PRESENT =
+            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");
+
+    // Internal initialization states
+    private static final int STATE_APP_CONTROLLER_RECEIVED =
+            getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED");
+
+    // Interaction finish states
+    private static final int STATE_SCALED_CONTROLLER_HOME =
+            getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME");
+    private static final int STATE_SCALED_CONTROLLER_RECENTS =
+            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS");
+
+    protected static final int STATE_HANDLER_INVALIDATED =
+            getFlagForIndex(6, "STATE_HANDLER_INVALIDATED");
+    private static final int STATE_GESTURE_STARTED =
+            getFlagForIndex(7, "STATE_GESTURE_STARTED");
+    private static final int STATE_GESTURE_CANCELLED =
+            getFlagForIndex(8, "STATE_GESTURE_CANCELLED");
+    private static final int STATE_GESTURE_COMPLETED =
+            getFlagForIndex(9, "STATE_GESTURE_COMPLETED");
+
+    private static final int STATE_CAPTURE_SCREENSHOT =
+            getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT");
+    protected static final int STATE_SCREENSHOT_CAPTURED =
+            getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED");
+    private static final int STATE_SCREENSHOT_VIEW_SHOWN =
+            getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN");
+
+    private static final int STATE_RESUME_LAST_TASK =
+            getFlagForIndex(13, "STATE_RESUME_LAST_TASK");
+    private static final int STATE_START_NEW_TASK =
+            getFlagForIndex(14, "STATE_START_NEW_TASK");
+    private static final int STATE_CURRENT_TASK_FINISHED =
+            getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED");
+    private static final int STATE_FINISH_WITH_NO_END =
+            getFlagForIndex(16, "STATE_FINISH_WITH_NO_END");
+
+    private static final int LAUNCHER_UI_STATES =
+            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
+
+    public static final long MAX_SWIPE_DURATION = 350;
+    public static final long HOME_DURATION = StaggeredWorkspaceAnim.DURATION_MS;
+
+    public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
+    private static final float SWIPE_DURATION_MULTIPLIER =
+            Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
+    private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured";
+
+    public static final long RECENTS_ATTACH_DURATION = 300;
+
+    /**
+     * Used as the page index for logging when we return to the last task at the end of the gesture.
+     */
+    private static final int LOG_NO_OP_PAGE_INDEX = -1;
+
+    protected final TaskAnimationManager mTaskAnimationManager;
+
+    // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
+    private RunningWindowAnim mRunningWindowAnim;
+    // Possible second animation running at the same time as mRunningWindowAnim
+    private Animator mParallelRunningAnim;
+    private boolean mIsMotionPaused;
+    private boolean mHasMotionEverBeenPaused;
+
+    private boolean mContinuingLastGesture;
+
+    private ThumbnailData mTaskSnapshot;
+
+    // Used to control launcher components throughout the swipe gesture.
+    private AnimatorControllerWithResistance mLauncherTransitionController;
+    private boolean mHasEndedLauncherTransition;
+
+    private AnimationFactory mAnimationFactory = (t) -> { };
+
+    private boolean mWasLauncherAlreadyVisible;
+
+    private boolean mPassedOverviewThreshold;
+    private boolean mGestureStarted;
+    private boolean mLogDirectionUpOrLeft = true;
+    private PointF mDownPos;
+    private boolean mIsLikelyToStartNewTask;
+
+    private final long mTouchTimeMs;
+    private long mLauncherFrameDrawnTime;
+
+    private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
+
+    private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
+    protected boolean mIsSwipingPipToHome;
+
+    public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
+            TaskAnimationManager taskAnimationManager, GestureState gestureState,
+            long touchTimeMs, boolean continuingLastGesture,
+            InputConsumerController inputConsumer) {
+        super(context, deviceState, gestureState, new TransformParams());
+        mActivityInterface = gestureState.getActivityInterface();
+        mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
+        mInputConsumerProxy =
+                new InputConsumerProxy(inputConsumer, () -> {
+                    endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
+                    endLauncherTransitionController();
+                }, new InputProxyHandlerFactory(mActivityInterface, mGestureState));
+        mTaskAnimationManager = taskAnimationManager;
+        mTouchTimeMs = touchTimeMs;
+        mContinuingLastGesture = continuingLastGesture;
+
+        initAfterSubclassConstructor();
+        initStateCallbacks();
+    }
+
+    private void initStateCallbacks() {
+        mStateCallback = new MultiStateCallback(STATE_NAMES);
+
+        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
+                this::onLauncherPresentAndGestureStarted);
+
+        mStateCallback.runOnceAtState(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
+                this::initializeLauncherAnimationController);
+
+        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
+                this::launcherFrameDrawn);
+
+        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
+                        | STATE_GESTURE_CANCELLED,
+                this::resetStateForAnimationCancel);
+
+        mStateCallback.runOnceAtState(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
+                this::resumeLastTask);
+        mStateCallback.runOnceAtState(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
+                this::startNewTask);
+
+        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+                        | STATE_LAUNCHER_DRAWN | STATE_CAPTURE_SCREENSHOT,
+                this::switchToScreenshot);
+
+        mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+                        | STATE_SCALED_CONTROLLER_RECENTS,
+                this::finishCurrentTransitionToRecents);
+
+        mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+                        | STATE_SCALED_CONTROLLER_HOME,
+                this::finishCurrentTransitionToHome);
+        mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
+                this::reset);
+
+        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+                        | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
+                        | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
+                        | STATE_GESTURE_STARTED,
+                this::setupLauncherUiAfterSwipeUpToRecentsAnimation);
+
+        mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED,
+                this::continueComputingRecentsScrollIfNecessary);
+        mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED
+                        | STATE_RECENTS_SCROLLING_FINISHED,
+                this::onSettledOnEndTarget);
+
+        mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
+        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+                this::invalidateHandlerWithLauncher);
+        mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
+                this::resetStateForAnimationCancel);
+        mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_FINISH_WITH_NO_END,
+                this::resetStateForAnimationCancel);
+
+        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
+                            | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
+                    (b) -> mRecentsView.setRunningTaskHidden(!b));
+        }
+    }
+
+    protected boolean onActivityInit(Boolean alreadyOnHome) {
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return false;
+        }
+
+        T createdActivity = mActivityInterface.getCreatedActivity();
+        if (createdActivity != null) {
+            initTransitionEndpoints(createdActivity.getDeviceProfile());
+        }
+        final T activity = mActivityInterface.getCreatedActivity();
+        if (mActivity == activity) {
+            return true;
+        }
+
+        if (mActivity != null) {
+            if (mStateCallback.hasStates(STATE_GESTURE_COMPLETED)) {
+                // If the activity has restarted between setting the page scroll settling callback
+                // and actually receiving the callback, just mark the gesture completed
+                mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
+                return true;
+            }
+
+            // The launcher may have been recreated as a result of device rotation.
+            int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
+            initStateCallbacks();
+            mStateCallback.setState(oldState);
+        }
+        mWasLauncherAlreadyVisible = alreadyOnHome;
+        mActivity = activity;
+        // Override the visibility of the activity until the gesture actually starts and we swipe
+        // up, or until we transition home and the home animation is composed
+        if (alreadyOnHome) {
+            mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
+        } else {
+            mActivity.addForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
+        }
+
+        mRecentsView = activity.getOverviewPanel();
+        mRecentsView.setOnPageTransitionEndCallback(null);
+
+        mStateCallback.setState(STATE_LAUNCHER_PRESENT);
+        if (alreadyOnHome) {
+            onLauncherStart();
+        } else {
+            activity.runOnceOnStart(this::onLauncherStart);
+        }
+
+        // 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, () -> {
+                        if (mRecentsAnimationController != null) {
+                            mRecentsAnimationController.cleanupScreenshot();
+                        }
+                    });
+                    mRecentsView.onRecentsAnimationComplete();
+                }
+            });
+
+        setupRecentsViewUi();
+        linkRecentsViewScroll();
+
+        return true;
+    }
+
+    /**
+     * Return true if the window should be translated horizontally if the recents view scrolls
+     */
+    protected boolean moveWindowWithRecentsScroll() {
+        return mGestureState.getEndTarget() != HOME;
+    }
+
+    private void onLauncherStart() {
+        final T activity = mActivityInterface.getCreatedActivity();
+        if (mActivity != activity) {
+            return;
+        }
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return;
+        }
+        // RecentsView never updates the display rotation until swipe-up, force update
+        // RecentsOrientedState before passing to TaskViewSimulator.
+        mRecentsView.updateRecentsRotation();
+        mTaskViewSimulator.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.
+        if (mGestureState.getEndTarget() != HOME) {
+            Runnable initAnimFactory = () -> {
+                mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState,
+                        mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
+                maybeUpdateRecentsAttachedState(false /* animate */);
+            };
+            if (mWasLauncherAlreadyVisible) {
+                // Launcher is visible, but might be about to stop. Thus, if we prepare recents
+                // now, it might get overridden by moveToRestState() in onStop(). To avoid this,
+                // wait until the next gesture (and possibly launcher) starts.
+                mStateCallback.runOnceAtState(STATE_GESTURE_STARTED, initAnimFactory);
+            } else {
+                initAnimFactory.run();
+            }
+        }
+        AbstractFloatingView.closeAllOpenViewsExcept(activity, mWasLauncherAlreadyVisible,
+                AbstractFloatingView.TYPE_LISTENER);
+
+        if (mWasLauncherAlreadyVisible) {
+            mStateCallback.setState(STATE_LAUNCHER_DRAWN);
+        } else {
+            Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init");
+            View dragLayer = activity.getDragLayer();
+            dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
+                boolean mHandled = false;
+
+                @Override
+                public void onDraw() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    TraceHelper.INSTANCE.endSection(traceToken);
+                    dragLayer.post(() ->
+                            dragLayer.getViewTreeObserver().removeOnDrawListener(this));
+                    if (activity != mActivity) {
+                        return;
+                    }
+
+                    mStateCallback.setState(STATE_LAUNCHER_DRAWN);
+                }
+            });
+        }
+
+        activity.getRootView().setOnApplyWindowInsetsListener(this);
+        mStateCallback.setState(STATE_LAUNCHER_STARTED);
+    }
+
+    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.
+        setupRecentsViewUi();
+
+        // For the duration of the gesture, in cases where an activity is launched while the
+        // activity is not yet resumed, finish the animation to ensure we get resumed
+        mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
+                mOnDeferredActivityLaunch);
+
+        mGestureState.runOnceAtState(STATE_END_TARGET_SET,
+                () -> {
+                    mDeviceState.getRotationTouchHelper()
+                            .onEndTargetCalculated(mGestureState.getEndTarget(),
+                                    mActivityInterface);
+                });
+
+        notifyGestureStartedAsync();
+    }
+
+    private void onDeferredActivityLaunch() {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mActivityInterface.switchRunningTaskViewToScreenshot(
+                    null, () -> {
+                        mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+                    });
+        } else {
+            mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+        }
+    }
+
+    private void setupRecentsViewUi() {
+        if (mContinuingLastGesture) {
+            updateSysUiFlags(mCurrentShift.value);
+            return;
+        }
+        notifyGestureAnimationStartToRecents();
+    }
+
+    protected void notifyGestureAnimationStartToRecents() {
+        mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask());
+    }
+
+    private void launcherFrameDrawn() {
+        mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
+    }
+
+    private void initializeLauncherAnimationController() {
+        buildAnimationController();
+
+        Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
+                TraceHelper.FLAG_IGNORE_BINDERS);
+        LatencyTrackerCompat.logToggleRecents(
+                mContext, (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
+        TraceHelper.INSTANCE.endSection(traceToken);
+
+        // This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
+        // high-res thumbnail loader here once we are sure that we will end up in an overview state
+        RecentsModel.INSTANCE.get(mContext).getThumbnailCache()
+                .getHighResLoadingState().setVisible(true);
+    }
+
+    public MotionPauseDetector.OnMotionPauseListener getMotionPauseListener() {
+        return new MotionPauseDetector.OnMotionPauseListener() {
+            @Override
+            public void onMotionPauseDetected() {
+                mHasMotionEverBeenPaused = true;
+                maybeUpdateRecentsAttachedState();
+                performHapticFeedback();
+            }
+
+            @Override
+            public void onMotionPauseChanged(boolean isPaused) {
+                mIsMotionPaused = isPaused;
+            }
+        };
+    }
+
+    public void maybeUpdateRecentsAttachedState() {
+        maybeUpdateRecentsAttachedState(true /* animate */);
+    }
+
+    /**
+     * 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.
+     */
+    private void maybeUpdateRecentsAttachedState(boolean animate) {
+        if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) {
+            return;
+        }
+        RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+                ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
+                : null;
+        final boolean recentsAttachedToAppWindow;
+        if (mGestureState.getEndTarget() != null) {
+            recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
+        } else if (mContinuingLastGesture
+                && mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
+            recentsAttachedToAppWindow = true;
+        } else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) {
+            // The window is going away so make sure recents is always visible in this case.
+            recentsAttachedToAppWindow = true;
+        } else {
+            recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
+        }
+        mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
+
+        // Reapply window transform throughout the attach animation, as the animation affects how
+        // much the window is bound by overscroll (vs moving freely).
+        if (animate) {
+            ValueAnimator reapplyWindowTransformAnim = ValueAnimator.ofFloat(0, 1);
+            reapplyWindowTransformAnim.addUpdateListener(anim -> {
+                if (mRunningWindowAnim == null) {
+                    applyWindowTransform();
+                }
+            });
+            reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
+            mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
+                    reapplyWindowTransformAnim::cancel);
+        } else {
+            applyWindowTransform();
+        }
+    }
+
+    public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
+        setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
+    }
+
+    private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) {
+        if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
+            mIsLikelyToStartNewTask = isLikelyToStartNewTask;
+            maybeUpdateRecentsAttachedState(animate);
+        }
+    }
+
+    private void buildAnimationController() {
+        if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
+            return;
+        }
+        initTransitionEndpoints(mActivity.getDeviceProfile());
+        mAnimationFactory.createActivityInterface(mTransitionDragLength);
+    }
+
+    /**
+     * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
+     * (it has its own animation) or if we explicitly ended the controller already.
+     * @return Whether we can create the launcher controller or update its progress.
+     */
+    private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
+        return mGestureState.getEndTarget() != HOME && !mHasEndedLauncherTransition;
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
+        WindowInsets result = view.onApplyWindowInsets(windowInsets);
+        buildAnimationController();
+        return result;
+    }
+
+    private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) {
+        mLauncherTransitionController = anim;
+        mLauncherTransitionController.getNormalController().dispatchOnStart();
+        updateLauncherTransitionProgress();
+    }
+
+    public Intent getLaunchIntent() {
+        return mGestureState.getOverviewIntent();
+    }
+
+    /**
+     * Called when the value of {@link #mCurrentShift} changes
+     */
+    @UiThread
+    @Override
+    public void updateFinalShift() {
+        final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
+        if (passed != mPassedOverviewThreshold) {
+            mPassedOverviewThreshold = passed;
+            if (mDeviceState.isTwoButtonNavMode() && !mGestureState.isHandlingAtomicEvent()) {
+                performHapticFeedback();
+            }
+        }
+
+        updateSysUiFlags(mCurrentShift.value);
+        applyWindowTransform();
+
+        updateLauncherTransitionProgress();
+    }
+
+    private void updateLauncherTransitionProgress() {
+        if (mLauncherTransitionController == null
+                || !canCreateNewOrUpdateExistingLauncherTransitionController()) {
+            return;
+        }
+        mLauncherTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
+    }
+
+    /**
+     * @param windowProgress 0 == app, 1 == overview
+     */
+    private void updateSysUiFlags(float windowProgress) {
+        if (mRecentsAnimationController != null && mRecentsView != null) {
+            TaskView runningTask = mRecentsView.getRunningTaskView();
+            TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen();
+            int centermostTaskFlags = centermostTask == null ? 0
+                    : centermostTask.getThumbnail().getSysUiStatusNavFlags();
+            boolean swipeUpThresholdPassed = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
+            boolean quickswitchThresholdPassed = centermostTask != runningTask;
+
+            // We will handle the sysui flags based on the centermost task view.
+            mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
+                    ||  (quickswitchThresholdPassed && centermostTaskFlags != 0));
+            mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
+            // Provide a hint to WM the direction that we will be settling in case the animation
+            // needs to be canceled
+            mRecentsAnimationController.setWillFinishToHome(swipeUpThresholdPassed);
+
+            if (swipeUpThresholdPassed) {
+                mActivity.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
+            } else {
+                mActivity.getSystemUiController().updateUiState(
+                        UI_STATE_FULLSCREEN_TASK, centermostTaskFlags);
+            }
+        }
+    }
+
+    @Override
+    public void onRecentsAnimationStart(RecentsAnimationController controller,
+            RecentsAnimationTargets targets) {
+        ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
+        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) {
+                Rect overviewStackBounds = mActivityInterface
+                        .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
+                dp = dp.getMultiWindowProfile(mContext,
+                        new WindowBounds(overviewStackBounds, targets.homeContentInsets));
+            } else {
+                // If we are not in multi-window mode, home insets should be same as system insets.
+                dp = dp.copy(mContext);
+            }
+            dp.updateInsets(targets.homeContentInsets);
+            dp.updateIsSeascape(mContext);
+            initTransitionEndpoints(dp);
+            mTaskViewSimulator.getOrientationState().setMultiWindowMode(dp.isMultiWindowMode);
+        }
+
+        // Notify when the animation starts
+        if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+            for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+                action.run();
+            }
+            mRecentsAnimationStartCallbacks.clear();
+        }
+
+        // Only add the callback to enable the input consumer after we actually have the controller
+        mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
+                mRecentsAnimationController::enableInputConsumer);
+        mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
+
+        mPassedOverviewThreshold = false;
+    }
+
+    @Override
+    public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+        ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation");
+        mActivityInitListener.unregister();
+        mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
+
+        // Defer clearing the controller and the targets until after we've updated the state
+        mRecentsAnimationController = null;
+        mRecentsAnimationTargets = null;
+        if (mRecentsView != null) {
+            mRecentsView.setRecentsAnimationTargets(null, null);
+        }
+    }
+
+    @UiThread
+    public void onGestureStarted(boolean isLikelyToStartNewTask) {
+        mActivityInterface.closeOverlay();
+        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+
+        if (mRecentsView != null) {
+            mRecentsView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
+                boolean mHandled = false;
+
+                @Override
+                public void onDraw() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    InteractionJankMonitorWrapper.begin(mRecentsView,
+                            InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
+                    InteractionJankMonitorWrapper.begin(mRecentsView,
+                            InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+
+                    mRecentsView.post(() ->
+                            mRecentsView.getViewTreeObserver().removeOnDrawListener(this));
+                }
+            });
+        }
+        notifyGestureStartedAsync();
+        setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
+        mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
+        mGestureStarted = true;
+        SystemUiProxy.INSTANCE.get(mContext).notifySwipeUpGestureStarted();
+    }
+
+    /**
+     * Notifies the launcher that the swipe gesture has started. This can be called multiple times.
+     */
+    @UiThread
+    private void notifyGestureStartedAsync() {
+        final T curActivity = mActivity;
+        if (curActivity != null) {
+            // Once the gesture starts, we can no longer transition home through the button, so
+            // reset the force override of the activity visibility
+            mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
+        }
+    }
+
+    /**
+     * Called as a result on ACTION_CANCEL to return the UI to the start state.
+     */
+    @UiThread
+    public void onGestureCancelled() {
+        updateDisplacement(0);
+        mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
+        handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
+    }
+
+    /**
+     * @param endVelocity The velocity in the direction of the nav bar to the middle of the screen.
+     * @param velocity The x and y components of the velocity when the gesture ends.
+     * @param downPos The x and y value of where the gesture started.
+     */
+    @UiThread
+    public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
+        float flingThreshold = mContext.getResources()
+                .getDimension(R.dimen.quickstep_fling_threshold_speed);
+        boolean isFling = mGestureStarted && !mIsMotionPaused
+                && Math.abs(endVelocity) > flingThreshold;
+        mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
+        boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x);
+        if (isVelocityVertical) {
+            mLogDirectionUpOrLeft = velocity.y < 0;
+        } else {
+            mLogDirectionUpOrLeft = velocity.x < 0;
+        }
+        mDownPos = downPos;
+        handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
+    }
+
+    private void endRunningWindowAnim(boolean cancel) {
+        if (mRunningWindowAnim != null) {
+            if (cancel) {
+                mRunningWindowAnim.cancel();
+            } else {
+                mRunningWindowAnim.end();
+            }
+        }
+        if (mParallelRunningAnim != null) {
+            // Unlike the above animation, the parallel animation won't have anything to take up
+            // the work if it's canceled, so just end it instead.
+            mParallelRunningAnim.end();
+        }
+    }
+
+    private void onSettledOnEndTarget() {
+        // Fast-finish the attaching animation if it's still running.
+        maybeUpdateRecentsAttachedState(false);
+        final GestureEndTarget endTarget = mGestureState.getEndTarget();
+        if (endTarget != NEW_TASK) {
+            InteractionJankMonitorWrapper.cancel(
+                    InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+        }
+        if (endTarget != HOME) {
+            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();
+                break;
+            case RECENTS:
+                mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
+                        | STATE_SCREENSHOT_VIEW_SHOWN);
+                break;
+            case NEW_TASK:
+                mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT);
+                break;
+            case LAST_TASK:
+                mStateCallback.setState(STATE_RESUME_LAST_TASK);
+                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) {
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return false;
+        }
+        if (mStateCallback.hasStates(STATE_START_NEW_TASK)
+                && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
+            reset();
+            return true;
+        }
+        return false;
+    }
+
+    private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
+            boolean isCancel) {
+        if (mGestureState.isHandlingAtomicEvent()) {
+            // Button mode, this is only used to go to recents
+            return RECENTS;
+        }
+        final GestureEndTarget endTarget;
+        final boolean goingToNewTask;
+        if (mRecentsView != null) {
+            if (!hasTargets()) {
+                // If there are no running tasks, then we can assume that this is a continuation of
+                // the last gesture, but after the recents animation has finished
+                goingToNewTask = true;
+            } else {
+                final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
+                final int taskToLaunch = mRecentsView.getNextPage();
+                goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
+            }
+        } else {
+            goingToNewTask = false;
+        }
+        final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
+        if (!isFling) {
+            if (isCancel) {
+                endTarget = LAST_TASK;
+            } else if (mDeviceState.isFullyGesturalNavMode()) {
+                if (mIsMotionPaused) {
+                    endTarget = RECENTS;
+                } else if (goingToNewTask) {
+                    endTarget = NEW_TASK;
+                } else {
+                    endTarget = !reachedOverviewThreshold ? LAST_TASK : HOME;
+                }
+            } else {
+                endTarget = reachedOverviewThreshold && mGestureStarted
+                        ? RECENTS
+                        : goingToNewTask
+                                ? NEW_TASK
+                                : LAST_TASK;
+            }
+        } else {
+            // If swiping at a diagonal, base end target on the faster velocity.
+            boolean isSwipeUp = endVelocity < 0;
+            boolean willGoToNewTaskOnSwipeUp =
+                    goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
+
+            if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
+                endTarget = HOME;
+            } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) {
+                // If swiping at a diagonal, base end target on the faster velocity.
+                endTarget = NEW_TASK;
+            } else if (isSwipeUp) {
+                endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp
+                        ? NEW_TASK : RECENTS;
+            } else {
+                endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
+            }
+        }
+
+        if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) {
+            return LAST_TASK;
+        }
+        return endTarget;
+    }
+
+    @UiThread
+    private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
+            boolean isCancel) {
+        long duration = MAX_SWIPE_DURATION;
+        float currentShift = mCurrentShift.value;
+        final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
+                isFling, isCancel);
+        // Set the state, but don't notify until the animation completes
+        mGestureState.setEndTarget(endTarget, false /* isAtomic */);
+
+        float endShift = endTarget.isLauncher ? 1 : 0;
+        final float startShift;
+        if (!isFling) {
+            long expectedDuration = Math.abs(Math.round((endShift - currentShift)
+                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
+            duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
+            startShift = currentShift;
+        } else {
+            startShift = Utilities.boundToRange(currentShift - velocity.y
+                    * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
+            if (mTransitionDragLength > 0) {
+                    float distanceToTravel = (endShift - currentShift) * mTransitionDragLength;
+
+                    // we want the page's snap velocity to approximately match the velocity at
+                    // which the user flings, so we scale the duration by a value near to the
+                    // derivative of the scroll interpolator at zero, ie. 2.
+                    long baseDuration = Math.round(Math.abs(distanceToTravel / velocity.y));
+                    duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
+            }
+        }
+        Interpolator interpolator;
+        S state = mActivityInterface.stateFromGestureEndTarget(endTarget);
+        if (state.displayOverviewTasksAsGrid(mDp)) {
+            interpolator = ACCEL_DEACCEL;
+        } else if (endTarget == RECENTS) {
+            interpolator = OVERSHOOT_1_2;
+        } else {
+            interpolator = DEACCEL;
+        }
+
+        if (endTarget.isLauncher) {
+            mInputConsumerProxy.enable();
+        }
+        if (endTarget == HOME) {
+            duration = HOME_DURATION;
+            // Early detach the nav bar once the endTarget is determined as HOME
+            if (mRecentsAnimationController != null) {
+                mRecentsAnimationController.detachNavigationBarFromApp(true);
+            }
+        } else if (endTarget == RECENTS) {
+            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.
+                    mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration));
+                    isScrolling = true;
+                }
+                if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) {
+                    mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION);
+                    isScrolling = true;
+                }
+                if (!mGestureState.isHandlingAtomicEvent() || isScrolling) {
+                    duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+                }
+            }
+        }
+
+        // Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
+        // or resumeLastTask().
+        if (mRecentsView != null) {
+            mRecentsView.setOnPageTransitionEndCallback(
+                    () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED));
+        } else {
+            mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
+        }
+
+        animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocity);
+    }
+
+    private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
+        StatsLogManager.EventEnum event;
+        switch (endTarget) {
+            case HOME:
+                event = LAUNCHER_HOME_GESTURE;
+                break;
+            case RECENTS:
+                event = LAUNCHER_OVERVIEW_GESTURE;
+                break;
+            case LAST_TASK:
+            case NEW_TASK:
+                event = mLogDirectionUpOrLeft ? LAUNCHER_QUICKSWITCH_LEFT
+                        : LAUNCHER_QUICKSWITCH_RIGHT;
+                break;
+            default:
+                event = IGNORE;
+        }
+        StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
+                .withSrcState(LAUNCHER_STATE_BACKGROUND)
+                .withDstState(endTarget.containerType);
+        if (targetTask != null) {
+            logger.withItemInfo(targetTask.getItemInfo());
+        }
+
+        DeviceProfile dp = mDp;
+        if (dp == null || mDownPos == null) {
+            // We probably never received an animation controller, skip logging.
+            return;
+        }
+        int pageIndex = endTarget == LAST_TASK
+                ? LOG_NO_OP_PAGE_INDEX
+                : mRecentsView.getNextPage();
+        // TODO: set correct container using the pageIndex
+        logger.log(event);
+    }
+
+    /** Animates to the given progress, where 0 is the current app and 1 is overview. */
+    @UiThread
+    private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
+            GestureEndTarget target, PointF velocityPxPerMs) {
+        runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration,
+                interpolator, target, velocityPxPerMs));
+    }
+
+    protected abstract HomeAnimationFactory createHomeAnimationFactory(
+            ArrayList<IBinder> launchCookies, long duration, boolean isTargetTranslucent,
+            boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget);
+
+    private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
+        @Override
+        public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+                boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+            if (task.taskId == mGestureState.getRunningTaskId()
+                    && task.configuration.windowConfiguration.getActivityType()
+                    != ACTIVITY_TYPE_HOME) {
+                // Since this is an edge case, just cancel and relaunch with default activity
+                // options (since we don't know if there's an associated app icon to launch from)
+                endRunningWindowAnim(true /* cancel */);
+                TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+                        mActivityRestartListener);
+                ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId, null);
+            }
+        }
+    };
+
+    @UiThread
+    private void animateToProgressInternal(float start, float end, long duration,
+            Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
+        maybeUpdateRecentsAttachedState();
+
+        // If we are transitioning to launcher, then listen for the activity to be restarted while
+        // the transition is in progress
+        if (mGestureState.getEndTarget().isLauncher) {
+            TaskStackChangeListeners.getInstance().registerTaskStackListener(
+                    mActivityRestartListener);
+
+            mParallelRunningAnim = mActivityInterface.getParallelAnimationToLauncher(
+                    mGestureState.getEndTarget(), duration);
+            if (mParallelRunningAnim != null) {
+                mParallelRunningAnim.start();
+            }
+        }
+
+        if (mGestureState.getEndTarget() == HOME) {
+            getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
+            final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+                    ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
+                    : null;
+            final ArrayList<IBinder> cookies = runningTaskTarget != null
+                    ? runningTaskTarget.taskInfo.launchCookies
+                    : new ArrayList<>();
+            boolean isTranslucent = runningTaskTarget != null && runningTaskTarget.isTranslucent;
+            boolean appCanEnterPip = !mDeviceState.isPipActive()
+                    && runningTaskTarget != null
+                    && runningTaskTarget.taskInfo.pictureInPictureParams != null
+                    && runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled();
+            HomeAnimationFactory homeAnimFactory =
+                    createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
+                            runningTaskTarget);
+            mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
+            final RectFSpringAnim windowAnim;
+            if (mIsSwipingPipToHome) {
+                mSwipePipToHomeAnimator = createWindowAnimationToPip(
+                        homeAnimFactory, runningTaskTarget, start);
+                windowAnim = mSwipePipToHomeAnimator;
+            } else {
+                mSwipePipToHomeAnimator = null;
+                windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
+                windowAnim.addAnimatorListener(new AnimationSuccessListener() {
+                    @Override
+                    public void onAnimationSuccess(Animator animator) {
+                        if (mRecentsAnimationController == null) {
+                            // If the recents animation is interrupted, we still end the running
+                            // animation (not canceled) so this is still called. In that case,
+                            // we can skip doing any future work here for the current gesture.
+                            return;
+                        }
+                        // Finalize the state and notify of the change
+                        mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+                    }
+                });
+            }
+            windowAnim.start(mContext, velocityPxPerMs);
+            mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
+            homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y);
+            homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
+            mLauncherTransitionController = null;
+
+            if (mRecentsView != null) {
+                mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget());
+            }
+        } else {
+            AnimatorSet animatorSet = new AnimatorSet();
+            ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
+            windowAnim.addUpdateListener(valueAnimator -> {
+                computeRecentsScrollIfInvisible();
+            });
+            windowAnim.addListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    if (mRecentsAnimationController == null) {
+                        // If the recents animation is interrupted, we still end the running
+                        // animation (not canceled) so this is still called. In that case, we can
+                        // skip doing any future work here for the current gesture.
+                        return;
+                    }
+                    if (mRecentsView != null) {
+                        int taskToLaunch = mRecentsView.getNextPage();
+                        int runningTask = getLastAppearedTaskIndex();
+                        boolean hasStartedNewTask = hasStartedNewTask();
+                        if (target == NEW_TASK && taskToLaunch == runningTask
+                                && !hasStartedNewTask) {
+                            // We are about to launch the current running task, so use LAST_TASK
+                            // state instead of NEW_TASK. This could happen, for example, if our
+                            // scroll is aborted after we determined the target to be NEW_TASK.
+                            mGestureState.setEndTarget(LAST_TASK);
+                        } else if (target == LAST_TASK && hasStartedNewTask) {
+                            // We are about to re-launch the previously running task, but we can't
+                            // just finish the controller like we normally would because that would
+                            // instead resume the last task that appeared, and not ensure that this
+                            // task is restored to the top. To address this, re-launch the task as
+                            // if it were a new task.
+                            mGestureState.setEndTarget(NEW_TASK);
+                        }
+                    }
+                    mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+                }
+            });
+            animatorSet.play(windowAnim);
+            if (mRecentsView != null) {
+                mRecentsView.onPrepareGestureEndAnimation(
+                        animatorSet, mGestureState.getEndTarget());
+            }
+            animatorSet.setDuration(duration).setInterpolator(interpolator);
+            animatorSet.start();
+            mRunningWindowAnim = RunningWindowAnim.wrap(animatorSet);
+        }
+    }
+
+    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 int homeRotation = orientationState.getRecentsActivityRotation();
+
+        final Matrix homeToWindowPositionMap = new Matrix();
+        final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
+        // Move the startRect to Launcher space as floatingIconView runs in Launcher
+        final Matrix windowToHomePositionMap = new Matrix();
+        homeToWindowPositionMap.invert(windowToHomePositionMap);
+        windowToHomePositionMap.mapRect(startRect);
+
+        final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
+                .startSwipePipToHome(taskInfo.topActivity,
+                        taskInfo.topActivityInfo,
+                        runningTaskTarget.taskInfo.pictureInPictureParams,
+                        homeRotation,
+                        mDp.hotseatBarSizePx);
+        final SwipePipToHomeAnimator.Builder builder = new SwipePipToHomeAnimator.Builder()
+                .setContext(mContext)
+                .setTaskId(runningTaskTarget.taskId)
+                .setComponentName(taskInfo.topActivity)
+                .setLeash(runningTaskTarget.leash.getSurfaceControl())
+                .setSourceRectHint(
+                        runningTaskTarget.taskInfo.pictureInPictureParams.getSourceRectHint())
+                .setAppBounds(taskInfo.configuration.windowConfiguration.getBounds())
+                .setHomeToWindowPositionMap(homeToWindowPositionMap)
+                .setStartBounds(startRect)
+                .setDestinationBounds(destinationBounds)
+                .setCornerRadius(mRecentsView.getPipCornerRadius())
+                .setAttachedView(mRecentsView);
+        // We would assume home and app window always in the same rotation While homeRotation
+        // 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,
+                    taskInfo.displayCutoutInsets);
+        }
+        final SwipePipToHomeAnimator swipePipToHomeAnimator = builder.build();
+        AnimatorPlaybackController activityAnimationToHome =
+                homeAnimFactory.createActivityAnimationToHome();
+        swipePipToHomeAnimator.addAnimatorListener(new AnimatorListenerAdapter() {
+            private boolean mHasAnimationEnded;
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (mHasAnimationEnded) return;
+                // Ensure Launcher ends in NORMAL state
+                activityAnimationToHome.dispatchOnStart();
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mHasAnimationEnded) return;
+                mHasAnimationEnded = true;
+                activityAnimationToHome.getAnimationPlayer().end();
+                if (mRecentsAnimationController == null) {
+                    // If the recents animation is interrupted, we still end the running
+                    // animation (not canceled) so this is still called. In that case, we can
+                    // skip doing any future work here for the current gesture.
+                    return;
+                }
+                // Finalize the state and notify of the change
+                mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+            }
+        });
+        setupWindowAnimation(swipePipToHomeAnimator);
+        return swipePipToHomeAnimator;
+    }
+
+    private void computeRecentsScrollIfInvisible() {
+        if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
+            // Views typically don't compute scroll when invisible as an optimization,
+            // but in our case we need to since the window offset depends on the scroll.
+            mRecentsView.computeScroll();
+        }
+    }
+
+    private void continueComputingRecentsScrollIfNecessary() {
+        if (!mGestureState.hasState(STATE_RECENTS_SCROLLING_FINISHED)
+                && !mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)
+                && !mCanceled) {
+            computeRecentsScrollIfInvisible();
+            mRecentsView.postOnAnimation(this::continueComputingRecentsScrollIfNecessary);
+        }
+    }
+
+    /**
+     * Creates an animation that transforms the current app window into the home app.
+     * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
+     * @param homeAnimationFactory The home animation factory.
+     */
+    @Override
+    protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
+            HomeAnimationFactory homeAnimationFactory) {
+        RectFSpringAnim anim =
+                super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
+        setupWindowAnimation(anim);
+        return anim;
+    }
+
+    private void setupWindowAnimation(RectFSpringAnim anim) {
+        anim.addOnUpdateListener((v, r, p) -> {
+            updateSysUiFlags(Math.max(p, mCurrentShift.value));
+        });
+        anim.addAnimatorListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                if (mRecentsView != null) {
+                    mRecentsView.post(mRecentsView::resetTaskVisuals);
+                }
+                // Make sure recents is in its final state
+                maybeUpdateRecentsAttachedState(false);
+                mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
+            }
+        });
+        if (mRecentsAnimationTargets != null) {
+            mRecentsAnimationTargets.addReleaseCheck(anim);
+        }
+    }
+
+    public void onConsumerAboutToBeSwitched() {
+        if (mActivity != null) {
+            // In the off chance that the gesture ends before Launcher is started, we should clear
+            // the callback here so that it doesn't update with the wrong state
+            mActivity.clearRunOnceOnStartCallback();
+            resetLauncherListeners();
+        }
+        if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
+            cancelCurrentAnimation();
+        } else {
+            mStateCallback.setStateOnUiThread(STATE_FINISH_WITH_NO_END);
+            reset();
+        }
+    }
+
+    public boolean isCanceled() {
+        return mCanceled;
+    }
+
+    @UiThread
+    private void resumeLastTask() {
+        if (mRecentsAnimationController != null) {
+            mRecentsAnimationController.finish(false /* toRecents */, null);
+            ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+        }
+        doLogGesture(LAST_TASK, null);
+        reset();
+    }
+
+    @UiThread
+    private void startNewTask() {
+        TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
+        startNewTask(success -> {
+            if (!success) {
+                reset();
+                // We couldn't launch the task, so take user to overview so they can
+                // decide what to do instead of staying in this broken state.
+                endLauncherTransitionController();
+                updateSysUiFlags(1 /* windowProgress == overview */);
+            }
+            doLogGesture(NEW_TASK, taskToLaunch);
+        });
+    }
+
+    /**
+     * Called when we successfully startNewTask() on the task that was previously running. Normally
+     * we call resumeLastTask() when returning to the previously running task, but this handles a
+     * specific edge case: if we switch from A to B, and back to A before B appears, we need to
+     * start A again to ensure it stays on top.
+     */
+    @androidx.annotation.CallSuper
+    protected void onRestartPreviouslyAppearedTask() {
+        // Finish the controller here, since we won't get onTaskAppeared() for a task that already
+        // appeared.
+        if (mRecentsAnimationController != null) {
+            mRecentsAnimationController.finish(false, null);
+        }
+        reset();
+    }
+
+    private void reset() {
+        mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+    }
+
+    /**
+     * Cancels any running animation so that the active target can be overriden by a new swipe
+     * handler (in case of quick switch).
+     */
+    private void cancelCurrentAnimation() {
+        mCanceled = true;
+        mCurrentShift.cancelAnimation();
+
+        // Cleanup when switching handlers
+        mInputConsumerProxy.unregisterCallback();
+        mActivityInitListener.unregister();
+        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
+        mTaskSnapshot = null;
+    }
+
+    private void invalidateHandler() {
+        if (!ENABLE_QUICKSTEP_LIVE_TILE.get() || !mActivityInterface.isInLiveTileMode()
+                || mGestureState.getEndTarget() != RECENTS) {
+            mInputConsumerProxy.destroy();
+            mTaskAnimationManager.setLiveTileCleanUpHandler(null);
+        }
+        mInputConsumerProxy.unregisterCallback();
+        endRunningWindowAnim(false /* cancel */);
+
+        if (mGestureEndCallback != null) {
+            mGestureEndCallback.run();
+        }
+
+        mActivityInitListener.unregister();
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+                mActivityRestartListener);
+        mTaskSnapshot = null;
+    }
+
+    private void invalidateHandlerWithLauncher() {
+        endLauncherTransitionController();
+
+        mRecentsView.onGestureAnimationEnd();
+        resetLauncherListeners();
+    }
+
+    private void endLauncherTransitionController() {
+        mHasEndedLauncherTransition = true;
+
+        if (mLauncherTransitionController != null) {
+            // End the animation, but stay at the same visual progress.
+            mLauncherTransitionController.getNormalController().dispatchSetInterpolator(
+                    t -> Utilities.boundToRange(mCurrentShift.value, 0, 1));
+            mLauncherTransitionController.getNormalController().getAnimationPlayer().end();
+            mLauncherTransitionController = null;
+        }
+
+        if (mRecentsView != null) {
+            mRecentsView.abortScrollerAnimation();
+        }
+    }
+
+    /**
+     * Unlike invalidateHandlerWithLauncher, this is called even when switching consumers, e.g. on
+     * continued quick switch gesture, which cancels the previous handler but doesn't invalidate it.
+     */
+    private void resetLauncherListeners() {
+        // Reset the callback for deferred activity launches
+        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mActivityInterface.setOnDeferredActivityLaunchCallback(null);
+        }
+        mActivity.getRootView().setOnApplyWindowInsetsListener(null);
+
+        mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
+    }
+
+    private void resetStateForAnimationCancel() {
+        boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
+        mActivityInterface.onTransitionCancelled(wasVisible, mGestureState.getEndTarget());
+
+        // Leave the pending invisible flag, as it may be used by wallpaper open animation.
+        if (mActivity != null) {
+            mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+        }
+    }
+
+    protected void switchToScreenshot() {
+        if (!hasTargets()) {
+            // If there are no targets, then we don't need to capture anything
+            mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+        } else {
+            final int runningTaskId = mGestureState.getRunningTaskId();
+            final boolean refreshView = !ENABLE_QUICKSTEP_LIVE_TILE.get() /* refreshView */;
+            boolean finishTransitionPosted = false;
+            if (mRecentsAnimationController != null) {
+                // Update the screenshot of the task
+                if (mTaskSnapshot == null) {
+                    UI_HELPER_EXECUTOR.execute(() -> {
+                        if (mRecentsAnimationController == null) return;
+                        final ThumbnailData taskSnapshot =
+                                mRecentsAnimationController.screenshotTask(runningTaskId);
+                        MAIN_EXECUTOR.execute(() -> {
+                            mTaskSnapshot = taskSnapshot;
+                            if (!updateThumbnail(runningTaskId, refreshView)) {
+                                setScreenshotCapturedState();
+                            }
+                        });
+                    });
+                    return;
+                }
+                finishTransitionPosted = updateThumbnail(runningTaskId, refreshView);
+            }
+            if (!finishTransitionPosted) {
+                setScreenshotCapturedState();
+            }
+        }
+    }
+
+    // Returns whether finish transition was posted.
+    private boolean updateThumbnail(int runningTaskId, boolean refreshView) {
+        boolean finishTransitionPosted = false;
+        final TaskView taskView;
+        if (mGestureState.getEndTarget() == HOME || mGestureState.getEndTarget() == NEW_TASK) {
+            // Capture the screenshot before finishing the transition to home or quickswitching to
+            // ensure it's taken in the correct orientation, but no need to update the thumbnail.
+            taskView = null;
+        } else {
+            taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot, refreshView);
+        }
+        if (taskView != null && refreshView && !mCanceled) {
+            // Defer finishing the animation until the next launcher frame with the
+            // new thumbnail
+            finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
+                    () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
+                    this::isCanceled);
+        }
+        return finishTransitionPosted;
+    }
+
+    private void setScreenshotCapturedState() {
+        // If we haven't posted a draw callback, set the state immediately.
+        Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
+                TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
+        mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+        TraceHelper.INSTANCE.endSection(traceToken);
+    }
+
+    private void finishCurrentTransitionToRecents() {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+            if (mRecentsAnimationController != null) {
+                mRecentsAnimationController.detachNavigationBarFromApp(true);
+            }
+        } else if (!hasTargets() || mRecentsAnimationController == null) {
+            // If there are no targets or the animation not started, then there is nothing to finish
+            mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+        } else {
+            mRecentsAnimationController.finish(true /* toRecents */,
+                    () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+        }
+        ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
+    }
+
+    private void finishCurrentTransitionToHome() {
+        if (!hasTargets() || mRecentsAnimationController == null) {
+            // If there are no targets or the animation not started, then there is nothing to finish
+            mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+        } else {
+            maybeFinishSwipePipToHome();
+            finishRecentsControllerToHome(
+                    () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+        }
+        ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
+        doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
+    }
+
+    /**
+     * Resets the {@link #mIsSwipingPipToHome} and notifies SysUI that transition is finished
+     * if applicable. This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
+     */
+    private void maybeFinishSwipePipToHome() {
+        if (mIsSwipingPipToHome && mSwipePipToHomeAnimator != null) {
+            SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
+                    mSwipePipToHomeAnimator.getComponentName(),
+                    mSwipePipToHomeAnimator.getDestinationBounds(),
+                    mSwipePipToHomeAnimator.getContentOverlay());
+            mRecentsAnimationController.setFinishTaskTransaction(
+                    mSwipePipToHomeAnimator.getTaskId(),
+                    mSwipePipToHomeAnimator.getFinishTransaction(),
+                    mSwipePipToHomeAnimator.getContentOverlay());
+            mIsSwipingPipToHome = false;
+        }
+    }
+
+    protected abstract void finishRecentsControllerToHome(Runnable callback);
+
+    private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return;
+        }
+        endLauncherTransitionController();
+        mRecentsView.onSwipeUpAnimationSuccess();
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mTaskAnimationManager.setLiveTileCleanUpHandler(mInputConsumerProxy::destroy);
+            mTaskAnimationManager.enableLiveTileRestartListener();
+        }
+
+        SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
+        doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
+        reset();
+    }
+
+    private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
+        return app.isNotInRecents
+                || app.activityType == ACTIVITY_TYPE_HOME;
+    }
+
+    /**
+     * To be called at the end of constructor of subclasses. This calls various methods which can
+     * depend on proper class initialization.
+     */
+    protected void initAfterSubclassConstructor() {
+        initTransitionEndpoints(
+                mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
+    }
+
+    protected void performHapticFeedback() {
+        VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
+    }
+
+    public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
+        return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
+    }
+
+    public void setGestureEndCallback(Runnable gestureEndCallback) {
+        mGestureEndCallback = gestureEndCallback;
+    }
+
+    protected void linkRecentsViewScroll() {
+        SurfaceTransactionApplier.create(mRecentsView, applier -> {
+            mTransformParams.setSyncTransactionApplier(applier);
+            runOnRecentsAnimationStart(() ->
+                    mRecentsAnimationTargets.addReleaseCheck(applier));
+        });
+
+        mRecentsView.addOnScrollChangedListener(mOnRecentsScrollListener);
+        runOnRecentsAnimationStart(() ->
+                mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
+                        mRecentsAnimationTargets));
+        mRecentsViewScrollLinked = true;
+    }
+
+    private void onRecentsViewScroll() {
+        if (moveWindowWithRecentsScroll()) {
+            updateFinalShift();
+        }
+    }
+
+    protected void startNewTask(Consumer<Boolean> resultCallback) {
+        // Launch the task user scrolled to (mRecentsView.getNextPage()).
+        if (!mCanceled) {
+            TaskView nextTask = mRecentsView.getNextPageTaskView();
+            if (nextTask != null) {
+                int taskId = nextTask.getTask().key.id;
+                mGestureState.updateLastStartedTaskId(taskId);
+                boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
+                        .contains(taskId);
+                nextTask.launchTask(success -> {
+                    resultCallback.accept(success);
+                    if (success) {
+                        if (hasTaskPreviouslyAppeared) {
+                            onRestartPreviouslyAppearedTask();
+                        }
+                    } else {
+                        mActivityInterface.onLaunchTaskFailed();
+                        if (mRecentsAnimationController != null) {
+                            mRecentsAnimationController.finish(true /* toRecents */, null);
+                        }
+                    }
+                }, true /* freezeTaskList */);
+            } else {
+                mActivityInterface.onLaunchTaskFailed();
+                Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
+                if (mRecentsAnimationController != null) {
+                    mRecentsAnimationController.finish(true /* toRecents */, null);
+                }
+            }
+        }
+        mCanceled = false;
+    }
+
+    /**
+     * Runs the given {@param action} if the recents animation has already started, or queues it to
+     * be run when it is next started.
+     */
+    protected void runOnRecentsAnimationStart(Runnable action) {
+        if (mRecentsAnimationTargets == null) {
+            mRecentsAnimationStartCallbacks.add(action);
+        } else {
+            action.run();
+        }
+    }
+
+    /**
+     * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
+     * @return whether the recents animation has started and there are valid app targets.
+     */
+    protected boolean hasTargets() {
+        return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
+    }
+
+    @Override
+    public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+        mRecentsAnimationController = null;
+        mRecentsAnimationTargets = null;
+        if (mRecentsView != null) {
+            mRecentsView.setRecentsAnimationTargets(null, null);
+        }
+    }
+
+    @Override
+    public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+        if (mRecentsAnimationController != null) {
+            if (handleTaskAppeared(appearedTaskTarget)) {
+                mRecentsAnimationController.finish(false /* toRecents */,
+                        null /* onFinishComplete */);
+                mActivityInterface.onLaunchTaskSuccess();
+                ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+            }
+        }
+    }
+
+    /**
+     * @return The index of the TaskView in RecentsView whose taskId matches the task that will
+     * resume if we finish the controller.
+     */
+    protected int getLastAppearedTaskIndex() {
+        return mGestureState.getLastAppearedTaskId() != -1
+                ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
+                : mRecentsView.getRunningTaskIndex();
+    }
+
+    /**
+     * @return Whether we are continuing a gesture that already landed on a new task,
+     * but before that task appeared.
+     */
+    protected boolean hasStartedNewTask() {
+        return mGestureState.getLastStartedTaskId() != -1;
+    }
+
+    /**
+     * Registers a callback to run when the activity is ready.
+     */
+    public void initWhenReady() {
+        // Preload the plan
+        RecentsModel.INSTANCE.get(mContext).getTasks(null);
+
+        mActivityInitListener.register();
+    }
+
+    /**
+     * Applies the transform on the recents animation
+     */
+    protected void applyWindowTransform() {
+        if (mWindowTransitionController != null) {
+            mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
+        }
+        // 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());
+            }
+            mTaskViewSimulator.apply(mTransformParams);
+        }
+        ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
+    }
+
+    /**
+     * Used for winscope tracing, see launcher_trace.proto
+     * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
+     * @param inputConsumerProto The parent of this proto message.
+     */
+    public void writeToProto(InputConsumerProto.Builder inputConsumerProto) {
+        SwipeHandlerProto.Builder swipeHandlerProto = SwipeHandlerProto.newBuilder();
+
+        mGestureState.writeToProto(swipeHandlerProto);
+
+        swipeHandlerProto.setIsRecentsAttachedToAppWindow(
+                mAnimationFactory.isRecentsAttachedToAppWindow());
+        swipeHandlerProto.setScrollOffset(mRecentsView == null
+                ? 0
+                : mRecentsView.getScrollOffset());
+        swipeHandlerProto.setAppToOverviewProgress(mCurrentShift.value);
+
+        inputConsumerProto.setSwipeHandler(swipeHandlerProto);
+    }
+
+    public interface Factory {
+
+        AbsSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index bdf525d..fac4d52 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -15,29 +15,31 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.SysUINavigationMode.getMode;
-import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
 import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+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_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
 import android.animation.Animator;
+import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
+import android.view.Gravity;
 import android.view.MotionEvent;
-import android.view.animation.Interpolator;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -46,17 +48,20 @@
 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.touch.PagedOrientationHandler;
 import com.android.launcher3.util.WindowBounds;
+import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
-import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.util.SplitScreenBounds;
+import com.android.quickstep.views.OverviewActionsView;
 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.RemoteAnimationTargetCompat;
 
@@ -81,12 +86,22 @@
         mBackgroundState = backgroundState;
     }
 
-    public void onTransitionCancelled(boolean activityVisible) {
+    /**
+     * Called when the current gesture transition is cancelled.
+     * @param activityVisible Whether the user can see the changes we make here, so try to animate.
+     * @param endTarget If the gesture ended before we got cancelled, where we were headed.
+     */
+    public void onTransitionCancelled(boolean activityVisible,
+            @Nullable GestureState.GestureEndTarget endTarget) {
         ACTIVITY_TYPE activity = getCreatedActivity();
         if (activity == null) {
             return;
         }
         STATE_TYPE startState = activity.getStateManager().getRestState();
+        if (endTarget != null) {
+            // We were on our way to this state when we got canceled, end there instead.
+            startState = stateFromGestureEndTarget(endTarget);
+        }
         activity.getStateManager().goToState(startState, activityVisible);
     }
 
@@ -94,19 +109,14 @@
             DeviceProfile dp, Context context, Rect outRect,
             PagedOrientationHandler orientationHandler);
 
-    public void onSwipeUpToRecentsComplete() {
-        // Re apply state in case we did something funky during the transition.
-        ACTIVITY_TYPE activity = getCreatedActivity();
-        if (activity == null) {
-            return;
-        }
-        activity.getStateManager().reapplyState();
-    }
-
-    public abstract void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState);
+    /** Called when the animation to home has fully settled. */
+    public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {}
 
     public abstract void onAssistantVisibilityChanged(float visibility);
 
+    /** Called when one handed mode activated or deactivated. */
+    public abstract void onOneHandedModeStateChanged(boolean activated);
+
     public abstract AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
             boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback);
 
@@ -152,20 +162,15 @@
         return deviceState.isInDeferredGestureRegion(ev);
     }
 
-    public abstract void onExitOverview(RotationTouchHelper deviceState,
-            Runnable exitRunnable);
-
     /**
-     * Updates the prediction state to the overview state.
+     * @return Whether the gesture in progress should be cancelled.
      */
-    public void updateOverviewPredictionState() {
-        // By public overview predictions are not supported
+    public boolean shouldCancelCurrentGesture() {
+        return false;
     }
 
-    /**
-     * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
-     */
-    public abstract int getContainerType();
+    public abstract void onExitOverview(RotationTouchHelper deviceState,
+            Runnable exitRunnable);
 
     public abstract boolean isInLiveTileMode();
 
@@ -197,124 +202,203 @@
     }
 
     /**
-     * Calculates the taskView size for the provided device configuration
+     * Calculates the taskView size for the provided device configuration.
      */
     public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
             PagedOrientationHandler orientedState) {
-        calculateTaskSize(context, dp, getExtraSpace(context, dp, orientedState),
-                outRect, orientedState);
-    }
-
-    protected abstract float getExtraSpace(Context context, DeviceProfile dp,
-            PagedOrientationHandler orientedState);
-
-    private void calculateTaskSize(
-            Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect,
-            PagedOrientationHandler orientationHandler) {
         Resources res = context.getResources();
-        final boolean showLargeTaskSize = showOverviewActions(context) ||
-                hideShelfInTwoButtonLandscape(context, orientationHandler);
+        if (dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+            Rect gridRect = new Rect();
+            calculateGridSize(context, dp, gridRect);
 
-        final int paddingResId;
-        if (dp.isMultiWindowMode) {
-            paddingResId = R.dimen.multi_window_task_card_horz_space;
-        } else if (dp.isVerticalBarLayout()) {
-            paddingResId = R.dimen.landscape_task_card_horz_space;
-        } else if (showLargeTaskSize) {
-            paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
+            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);
+            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;
+            Gravity.apply(gravity, outWidth, outHeight, gridRect, outRect);
         } else {
-            paddingResId = R.dimen.portrait_task_card_horz_space;
+            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),
+                    res.getDimensionPixelSize(R.dimen.overview_minimum_next_prev_size) + taskMargin,
+                    outRect);
         }
-        float paddingHorz = res.getDimension(paddingResId);
-        float paddingVert = showLargeTaskSize
-                ? 0 : res.getDimension(R.dimen.task_card_vert_space);
-
-        calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
-                res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
     }
 
     private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
-            float extraVerticalSpace, float paddingHorz, float paddingVert, float topIconMargin,
+            int claimedSpaceAbove, int claimedSpaceBelow, int minimumHorizontalPadding,
             Rect outRect) {
-        float taskWidth, taskHeight;
+        PointF taskDimension = getTaskDimension(context, dp);
         Rect insets = dp.getInsets();
+
+        Rect potentialTaskRect = new Rect(0, 0, dp.widthPx, dp.heightPx);
+        potentialTaskRect.inset(insets.left, insets.top, insets.right, insets.bottom);
+        potentialTaskRect.inset(
+                minimumHorizontalPadding,
+                claimedSpaceAbove,
+                minimumHorizontalPadding,
+                claimedSpaceBelow);
+
+        float scale = Math.min(
+                potentialTaskRect.width() / taskDimension.x,
+                potentialTaskRect.height() / taskDimension.y);
+        int outWidth = Math.round(scale * taskDimension.x);
+        int outHeight = Math.round(scale * taskDimension.y);
+
+        Gravity.apply(Gravity.CENTER, outWidth, outHeight, potentialTaskRect, outRect);
+    }
+
+    private static PointF getTaskDimension(Context context, DeviceProfile dp) {
+        PointF dimension = new PointF();
+        getTaskDimension(context, dp, dimension);
+        return dimension;
+    }
+
+    /**
+     * Gets the dimension of the task in the current system state.
+     */
+    public static void getTaskDimension(Context context, DeviceProfile dp, PointF out) {
         if (dp.isMultiWindowMode) {
             WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
-            taskWidth = bounds.availableSize.x;
-            taskHeight = bounds.availableSize.y;
+            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;
+            }
+        } else if (TaskView.CLIP_STATUS_AND_NAV_BARS) {
+            out.x = dp.availableWidthPx;
+            out.y = dp.availableHeightPx;
         } else {
-            taskWidth = dp.availableWidthPx;
-            taskHeight = dp.availableHeightPx;
+            out.x = dp.widthPx;
+            out.y = dp.heightPx;
         }
+    }
 
-        // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
-        // we override the insets ourselves.
-        int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
-        int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
+    /**
+     * 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);
 
-        float availableHeight = launcherVisibleHeight
-                - topIconMargin - extraVerticalSpace - paddingVert;
-        float availableWidth = launcherVisibleWidth - paddingHorz;
+        Rect insets = dp.getInsets();
+        outRect.set(0, 0, dp.widthPx, dp.heightPx);
+        outRect.inset(Math.max(insets.left, sideMargin), Math.max(insets.top, topMargin),
+                Math.max(insets.right, sideMargin), Math.max(insets.bottom, bottomMargin));
+    }
 
-        float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
-        float outWidth = scale * taskWidth;
-        float outHeight = scale * taskHeight;
+    /**
+     * Calculates the overview grid non-focused task size for the provided device configuration.
+     */
+    public final void calculateGridTaskSize(Context context, DeviceProfile dp, Rect outRect,
+            PagedOrientationHandler orientedState) {
+        Resources res = context.getResources();
+        Rect gridRect = new Rect();
+        calculateGridSize(context, dp, gridRect);
 
-        // Center in the visible space
-        float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
-        float y = insets.top + Math.max(topIconMargin,
-                (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
-        outRect.set(Math.round(x), Math.round(y),
-                Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
+        int rowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
+        float rowHeight = (gridRect.height() - rowSpacing) / 2f;
+
+        PointF taskDimension = getTaskDimension(context, dp);
+        float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / Math.max(
+                taskDimension.x, 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);
     }
 
     /**
      * Calculates the modal taskView size for the provided device configuration
      */
     public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
-        float paddingHorz = context.getResources().getDimension(dp.isMultiWindowMode
-                ? R.dimen.multi_window_task_card_horz_space
-                : dp.isVerticalBarLayout()
-                        ? R.dimen.landscape_task_card_horz_space
-                        : R.dimen.portrait_modal_task_card_horz_space);
-        float extraVerticalSpace = getOverviewActionsHeight(context);
-        float paddingVert = 0;
-        float topIconMargin = 0;
-        calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
-                topIconMargin, outRect);
+        calculateTaskSizeInternal(
+                context, dp,
+                dp.overviewTaskMarginPx,
+                getOverviewActionsHeight(context, dp),
+                dp.overviewTaskMarginPx,
+                outRect);
     }
 
-    /** Gets the space that the overview actions will take, including margins. */
-    public final float getOverviewActionsHeight(Context context) {
+    /** Gets the space that the overview actions will take, including bottom margin. */
+    private int getOverviewActionsHeight(Context context, DeviceProfile dp) {
         Resources res = context.getResources();
-        float actionsBottomMargin = 0;
-        if (getMode(context) == Mode.THREE_BUTTONS) {
-            actionsBottomMargin = res.getDimensionPixelSize(
-                    R.dimen.overview_actions_bottom_margin_three_button);
-        } else {
-            actionsBottomMargin = res.getDimensionPixelSize(
-                    R.dimen.overview_actions_bottom_margin_gesture);
-        }
-        float overviewActionsHeight = actionsBottomMargin
+        return OverviewActionsView.getOverviewActionsBottomMarginPx(getMode(context), dp)
+                + OverviewActionsView.getOverviewActionsTopMarginPx(getMode(context), dp)
                 + res.getDimensionPixelSize(R.dimen.overview_actions_height);
-        return overviewActionsHeight;
     }
 
+    /**
+     * Called when the gesture ends and the animation starts towards the given target. Used to add
+     * an optional additional animation with the same duration.
+     */
+    public @Nullable Animator getParallelAnimationToLauncher(
+            GestureState.GestureEndTarget endTarget, long duration) {
+        if (endTarget == RECENTS) {
+            ACTIVITY_TYPE activity = getCreatedActivity();
+            if (activity == null) {
+                return null;
+            }
+            STATE_TYPE state = stateFromGestureEndTarget(endTarget);
+            ScrimView scrimView = activity.getScrimView();
+            ObjectAnimator anim = ObjectAnimator.ofArgb(scrimView, VIEW_BACKGROUND_COLOR,
+                    getOverviewScrimColorForState(activity, state));
+            anim.setDuration(duration);
+            return anim;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the color of the scrim behind overview when at rest in this state.
+     * Return {@link Color#TRANSPARENT} for no scrim.
+     */
+    protected abstract int getOverviewScrimColorForState(ACTIVITY_TYPE activity, STATE_TYPE state);
+
+    /**
+     * Returns the expected STATE_TYPE from the provided GestureEndTarget.
+     */
+    public abstract STATE_TYPE stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget);
+
     public interface AnimationFactory {
 
         void createActivityInterface(long transitionLength);
 
-        default void onTransitionCancelled() { }
-
-        default void setShelfState(ShelfPeekAnim.ShelfAnimState animState,
-                Interpolator interpolator, long duration) { }
-
         /**
          * @param attached Whether to show RecentsView alongside the app window. If false, recents
          *                 will be hidden by some property we can animate, e.g. alpha.
          * @param animate Whether to animate recents to/from its new attached state.
          */
         default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
+
+        default boolean isRecentsAttachedToAppWindow() {
+            return false;
+        }
     }
 
     class DefaultAnimationFactory implements AnimationFactory {
@@ -371,11 +455,6 @@
         }
 
         @Override
-        public void onTransitionCancelled() {
-            mActivity.getStateManager().goToState(mStartState, false /* animate */);
-        }
-
-        @Override
         public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
             if (mIsAttachedToWindow == attached && animate) {
                 return;
@@ -390,12 +469,12 @@
             mActivity.getStateManager()
                     .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
             if (!recentsView.isShown() && animate) {
-                ADJACENT_PAGE_OFFSET.set(recentsView, fromTranslation);
+                ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, fromTranslation);
             } else {
-                fromTranslation = ADJACENT_PAGE_OFFSET.get(recentsView);
+                fromTranslation = ADJACENT_PAGE_HORIZONTAL_OFFSET.get(recentsView);
             }
             if (!animate) {
-                ADJACENT_PAGE_OFFSET.set(recentsView, toTranslation);
+                ADJACENT_PAGE_HORIZONTAL_OFFSET.set(recentsView, toTranslation);
             } else {
                 mActivity.getStateManager().createStateElementAnimation(
                         INDEX_RECENTS_TRANSLATE_X_ANIM,
@@ -406,6 +485,11 @@
             fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
         }
 
+        @Override
+        public boolean isRecentsAttachedToAppWindow() {
+            return mIsAttachedToWindow;
+        }
+
         protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
             //  Scale down recents from being full screen to being in overview.
             RecentsView recentsView = activity.getOverviewPanel();
@@ -415,7 +499,8 @@
         }
     }
 
-    protected static boolean showOverviewActions(Context context) {
-        return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
+    /** Called when OverviewService is bound to this process */
+    void onOverviewServiceBound() {
+        // Do nothing
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
similarity index 67%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
rename to quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 3898f0b..7fb8e16 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -15,9 +15,11 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
 import static com.android.quickstep.fallback.RecentsState.DEFAULT;
+import static com.android.quickstep.fallback.RecentsState.HOME;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -26,9 +28,8 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -59,20 +60,12 @@
         calculateTaskSize(context, dp, outRect, orientationHandler);
         if (dp.isVerticalBarLayout()
                 && SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
-            Rect targetInsets = dp.getInsets();
-            int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
-            return dp.hotseatBarSizePx + hotseatInset;
+            return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
         } else {
             return dp.heightPx - outRect.bottom;
         }
     }
 
-    /** 4 */
-    @Override
-    public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {
-        onSwipeUpToRecentsComplete();
-    }
-
     /** 5 */
     @Override
     public void onAssistantVisibilityChanged(float visibility) {
@@ -81,10 +74,16 @@
         // set to zero prior to this class becoming active.
     }
 
+    @Override
+    public void onOneHandedModeStateChanged(boolean activated) {
+        // Do nothing for FallbackActivityInterface
+    }
+
     /** 6 */
     @Override
     public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
             boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
+        notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
         DefaultAnimationFactory factory = new DefaultAnimationFactory(callback);
         factory.initUI();
         return factory;
@@ -107,8 +106,11 @@
     @Override
     public RecentsView getVisibleRecentsView() {
         RecentsActivity activity = getCreatedActivity();
-        if (activity != null && activity.hasWindowFocus()) {
-            return activity.getOverviewPanel();
+        if (activity != null) {
+            if (activity.hasBeenResumed()
+                    || (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode())) {
+                return activity.getOverviewPanel();
+            }
         }
         return null;
     }
@@ -141,21 +143,32 @@
 
     @Override
     public void onExitOverview(RotationTouchHelper deviceState, Runnable exitRunnable) {
-        // no-op, fake landscape not supported for 3P
-    }
+        final StateManager<RecentsState> stateManager = getCreatedActivity().getStateManager();
+        if (stateManager.getState() == HOME) {
+            exitRunnable.run();
+            notifyRecentsOfOrientation(deviceState);
+            return;
+        }
 
-    @Override
-    public int getContainerType() {
-        RecentsActivity activity = getCreatedActivity();
-        boolean visible = activity != null && activity.isStarted() && activity.hasWindowFocus();
-        return visible
-                ? LauncherLogProto.ContainerType.OTHER_LAUNCHER_APP
-                : LauncherLogProto.ContainerType.APP;
+        stateManager.addStateListener(
+                new StateManager.StateListener<RecentsState>() {
+                    @Override
+                    public void onStateTransitionComplete(RecentsState toState) {
+                        // Are we going from Recents to Workspace?
+                        if (toState == HOME) {
+                            exitRunnable.run();
+                            notifyRecentsOfOrientation(deviceState);
+                            stateManager.removeStateListener(this);
+                        }
+                    }
+                });
     }
 
     @Override
     public boolean isInLiveTileMode() {
-        return false;
+        RecentsActivity activity = getCreatedActivity();
+        return activity != null && activity.getStateManager().getState() == DEFAULT &&
+                activity.isStarted();
     }
 
     @Override
@@ -169,10 +182,28 @@
     }
 
     @Override
-    protected float getExtraSpace(Context context, DeviceProfile dp,
-            PagedOrientationHandler orientationHandler) {
-        return showOverviewActions(context)
-                ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
-                : 0;
+    public RecentsState stateFromGestureEndTarget(GestureState.GestureEndTarget endTarget) {
+        switch (endTarget) {
+            case RECENTS:
+                return DEFAULT;
+            case NEW_TASK:
+            case LAST_TASK:
+                return BACKGROUND_APP;
+            case HOME:
+            default:
+                return HOME;
+        }
+    }
+
+    private void notifyRecentsOfOrientation(RotationTouchHelper rotationTouchHelper) {
+        // reset layout on swipe to home
+        RecentsView recentsView = getCreatedActivity().getOverviewPanel();
+        recentsView.setLayoutRotation(rotationTouchHelper.getCurrentActiveRotation(),
+                rotationTouchHelper.getDisplayRotation());
+    }
+
+    @Override
+    protected int getOverviewScrimColorForState(RecentsActivity activity, RecentsState state) {
+        return state.getScrimColor(activity);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
similarity index 90%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
rename to quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index f60a50b..fd44e02 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -22,12 +22,14 @@
 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
 import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
+import static com.android.launcher3.Utilities.createHomeIntent;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Matrix;
@@ -36,6 +38,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
@@ -46,6 +49,7 @@
 import android.view.SurfaceControl.Transaction;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
@@ -53,6 +57,8 @@
 import com.android.launcher3.anim.PendingAnimation;
 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;
@@ -63,6 +69,7 @@
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.UUID;
 import java.util.function.Consumer;
 
@@ -71,7 +78,7 @@
  */
 @TargetApi(Build.VERSION_CODES.R)
 public class FallbackSwipeHandler extends
-        BaseSwipeUpHandlerV2<RecentsActivity, FallbackRecentsView> {
+        AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView, RecentsState> {
 
     /**
      * Message used for receiving gesture nav contract information. We use a static messenger to
@@ -121,12 +128,18 @@
     }
 
     @Override
-    protected HomeAnimationFactory createHomeAnimationFactory(long duration) {
+    protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
+            long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
+            RemoteAnimationTargetCompat runningTaskTarget) {
         mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
         ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
         Intent intent = new Intent(mGestureState.getHomeIntent());
         mActiveAnimationFactory.addGestureContract(intent);
-        mContext.startActivity(intent, options.toBundle());
+        try {
+            mContext.startActivity(intent, options.toBundle());
+        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+            mContext.startActivity(createHomeIntent());
+        }
         return mActiveAnimationFactory;
     }
 
@@ -240,7 +253,8 @@
             if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
                 RemoteAnimationTargets targets = new RemoteAnimationTargets(
                         new RemoteAnimationTargetCompat[] {appearedTaskTarget},
-                        new RemoteAnimationTargetCompat[0], appearedTaskTarget.mode);
+                        new RemoteAnimationTargetCompat[0], new RemoteAnimationTargetCompat[0],
+                        appearedTaskTarget.mode);
                 mHomeAlphaParams.setTargetSet(targets);
                 updateHomeAlpha();
                 return true;
@@ -290,12 +304,17 @@
         }
 
         @Override
-        public void update(RectF currentRect, float progress, float radius) {
+        public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
+                 float radius) {
             if (mSurfaceControl != null) {
                 currentRect.roundOut(mTempRect);
                 Transaction t = new Transaction();
-                t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
-                t.apply();
+                try {
+                    t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
+                    t.apply();
+                } catch (RuntimeException e) {
+                    // Ignore
+                }
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 00b5eb9..e3ae361 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -15,6 +15,9 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 
 import android.annotation.TargetApi;
@@ -22,8 +25,10 @@
 import android.content.Intent;
 import android.os.Build;
 
+import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.tracing.GestureStateProto;
+import com.android.launcher3.tracing.SwipeHandlerProto;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -44,19 +49,22 @@
      * Defines the end targets of a gesture and the associated state.
      */
     public enum GestureEndTarget {
-        HOME(true, ContainerType.WORKSPACE, false),
+        HOME(true, LAUNCHER_STATE_HOME, false, GestureStateProto.GestureEndTarget.HOME),
 
-        RECENTS(true, ContainerType.TASKSWITCHER, true),
+        RECENTS(true, LAUNCHER_STATE_OVERVIEW, true, GestureStateProto.GestureEndTarget.RECENTS),
 
-        NEW_TASK(false, ContainerType.APP, true),
+        NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
+                GestureStateProto.GestureEndTarget.NEW_TASK),
 
-        LAST_TASK(false, ContainerType.APP, true);
+        LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
+                GestureStateProto.GestureEndTarget.LAST_TASK);
 
-        GestureEndTarget(boolean isLauncher, int containerType,
-                boolean recentsAttachedToAppWindow) {
+        GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
+                GestureStateProto.GestureEndTarget protoEndTarget) {
             this.isLauncher = isLauncher;
             this.containerType = containerType;
             this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
+            this.protoEndTarget = protoEndTarget;
         }
 
         /** Whether the target is in the launcher activity. Implicitly, if the end target is going
@@ -66,6 +74,8 @@
         public final int containerType;
         /** Whether RecentsView should be attached to the window as we animate to this target */
         public final boolean recentsAttachedToAppWindow;
+        /** The GestureStateProto enum value, used for winscope tracing. See launcher_trace.proto */
+        public final GestureStateProto.GestureEndTarget protoEndTarget;
     }
 
     private static final String TAG = "GestureState";
@@ -132,6 +142,13 @@
     private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
     private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
     private int mLastStartedTaskId = -1;
+    private RecentsAnimationController mRecentsAnimationController;
+    private ThumbnailData mRecentsAnimationCanceledSnapshot;
+
+    /** The time when the swipe up gesture is triggered. */
+    private long mSwipeUpStartTimeMs;
+
+    private boolean mHandlingAtomicEvent;
 
     public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
         mHomeIntent = componentObserver.getHomeIntent();
@@ -201,7 +218,8 @@
     /**
      * @return the interface to the activity handing the UI updates for this gesture.
      */
-    public <T extends StatefulActivity<?>> BaseActivityInterface<?, T> getActivityInterface() {
+    public <S extends BaseState<S>,
+            T extends StatefulActivity<S>> BaseActivityInterface<S, T> getActivityInterface() {
         return mActivityInterface;
     }
 
@@ -301,6 +319,22 @@
     }
 
     /**
+     * Indicates if the gesture is handling an atomic event like a click and not a
+     * user controlled gesture.
+     */
+    public void setHandlingAtomicEvent(boolean handlingAtomicEvent) {
+        mHandlingAtomicEvent = true;
+    }
+
+    /**
+     * Returns true if the gesture is handling an atomic event like a click and not a
+     * user controlled gesture.
+     */
+    public boolean isHandlingAtomicEvent() {
+        return mHandlingAtomicEvent;
+    }
+
+    /**
      * @return whether the current gesture is still running a recents animation to a state in the
      *         Launcher or Recents activity.
      */
@@ -319,13 +353,22 @@
     @Override
     public void onRecentsAnimationStart(RecentsAnimationController controller,
             RecentsAnimationTargets targets) {
+        mRecentsAnimationController = controller;
         mStateCallback.setState(STATE_RECENTS_ANIMATION_STARTED);
     }
 
     @Override
     public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+        mRecentsAnimationCanceledSnapshot = thumbnailData;
         mStateCallback.setState(STATE_RECENTS_ANIMATION_CANCELED);
         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
+        if (mRecentsAnimationCanceledSnapshot != null) {
+            // Clean up the screenshot to finalize the recents animation cancel
+            if (mRecentsAnimationController != null) {
+                mRecentsAnimationController.cleanupScreenshot();
+            }
+            mRecentsAnimationCanceledSnapshot = null;
+        }
     }
 
     @Override
@@ -334,6 +377,25 @@
         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
     }
 
+    /**
+     * Returns and clears the canceled animation thumbnail data. This call only returns a value
+     * 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;
+    }
+
+    void setSwipeUpStartTimeMs(long uptimeMs) {
+        mSwipeUpStartTimeMs = uptimeMs;
+    }
+
+    long getSwipeUpStartTimeMs() {
+        return mSwipeUpStartTimeMs;
+    }
+
     public void dump(PrintWriter pw) {
         pw.println("GestureState:");
         pw.println("  gestureID=" + mGestureId);
@@ -343,4 +405,17 @@
         pw.println("  lastStartedTaskId=" + mLastStartedTaskId);
         pw.println("  isRecentsAnimationRunning=" + isRecentsAnimationRunning());
     }
+
+    /**
+     * Used for winscope tracing, see launcher_trace.proto
+     * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
+     * @param swipeHandlerProto The parent of this proto message.
+     */
+    public void writeToProto(SwipeHandlerProto.Builder swipeHandlerProto) {
+        GestureStateProto.Builder gestureStateProto = GestureStateProto.newBuilder();
+        gestureStateProto.setEndTarget(mEndTarget == null
+                ? GestureStateProto.GestureEndTarget.UNSET
+                : mEndTarget.protoEndTarget);
+        swipeHandlerProto.setGestureState(gestureStateProto);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java
similarity index 73%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java
rename to quickstep/src/com/android/quickstep/ImageActionsApi.java
index ba8ba33..8cb64c2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java
+++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java
@@ -22,11 +22,14 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.util.ImageActionUtils.persistBitmapAndStartActivity;
 
+import android.app.prediction.AppTarget;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.util.Log;
 
 import androidx.annotation.Nullable;
@@ -61,6 +64,20 @@
      */
     @UiThread
     public void shareWithExplicitIntent(@Nullable Rect crop, Intent intent) {
+        addImageAndSendIntent(crop, intent, false);
+    }
+
+    /**
+     * Share the image this api was constructed with using the provided intent. The implementation
+     * should set the intent's data field to the URI pointing to the image.
+     */
+    @UiThread
+    public void shareAsDataWithExplicitIntent(@Nullable Rect crop, Intent intent) {
+        addImageAndSendIntent(crop, intent, true);
+    }
+
+    @UiThread
+    private void addImageAndSendIntent(@Nullable Rect crop, Intent intent, boolean setData) {
         if (mBitmapSupplier.get() == null) {
             Log.e(TAG, "No snapshot available, not starting share.");
             return;
@@ -68,20 +85,22 @@
 
         UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(mContext,
                 mBitmapSupplier.get(), crop, intent, (uri, intentForUri) -> {
-                    intentForUri
-                            .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
-                            .putExtra(EXTRA_STREAM, uri);
+                    intentForUri.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+                    if (setData) {
+                        intentForUri.setData(uri);
+                    } else {
+                        intentForUri.putExtra(EXTRA_STREAM, uri);
+                    }
                     return new Intent[]{intentForUri};
                 }, TAG));
-
     }
 
     /**
      * Share the image this api was constructed with.
      */
     @UiThread
-    public void startShareActivity() {
-        ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, null, null, TAG);
+    public void startShareActivity(Rect crop) {
+        ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, crop, null, TAG);
     }
 
     /**
@@ -96,4 +115,12 @@
         ImageActionUtils.saveScreenshot(mSystemUiProxy, screenshot, screenshotBounds, visibleInsets,
                 task);
     }
+
+    /**
+     * Share the image when user taps on overview share targets.
+     */
+    @UiThread
+    public void shareImage(RectF rectF, ShortcutInfo shortcutInfo, AppTarget appTarget) {
+        ImageActionUtils.shareImage(mContext, mBitmapSupplier, rectF, shortcutInfo, appTarget, TAG);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index ec720d5..0b2a057 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -21,6 +21,9 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import com.android.launcher3.tracing.InputConsumerProto;
+import com.android.launcher3.tracing.TouchInteractionServiceProto;
+
 @TargetApi(Build.VERSION_CODES.O)
 public interface InputConsumer {
 
@@ -35,6 +38,7 @@
     int TYPE_RESET_GESTURE = 1 << 8;
     int TYPE_OVERSCROLL = 1 << 9;
     int TYPE_SYSUI_OVERLAY = 1 << 10;
+    int TYPE_ONE_HANDED = 1 << 11;
 
     String[] NAMES = new String[] {
            "TYPE_NO_OP",                    // 0
@@ -47,7 +51,8 @@
             "TYPE_OVERVIEW_WITHOUT_FOCUS",  // 7
             "TYPE_RESET_GESTURE",           // 8
             "TYPE_OVERSCROLL",              // 9
-            "TYPE_SYSUI_OVERLAY"         // 10
+            "TYPE_SYSUI_OVERLAY",           // 10
+            "TYPE_ONE_HANDED",              // 11
     };
 
     InputConsumer NO_OP = () -> TYPE_NO_OP;
@@ -114,4 +119,21 @@
         }
         return name.toString();
     }
+
+    /**
+     * Used for winscope tracing, see launcher_trace.proto
+     * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
+     * @param serviceProto The parent of this proto message.
+     */
+    default void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
+        InputConsumerProto.Builder inputConsumerProto = InputConsumerProto.newBuilder();
+        inputConsumerProto.setName(getName());
+        writeToProtoInternal(inputConsumerProto);
+        serviceProto.setInputConsumer(inputConsumerProto);
+    }
+
+    /**
+     * @see #writeToProto - allows subclasses to write additional info to the proto.
+     */
+    default void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {}
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
similarity index 63%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
rename to quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index cd4be18..fb1391a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -16,18 +16,19 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.QUICK_SWITCH;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
-import static com.android.quickstep.SysUINavigationMode.getMode;
-import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
-import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Rect;
-import android.util.Log;
-import android.view.animation.Interpolator;
+import android.view.MotionEvent;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -37,21 +38,17 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherInitListener;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
 import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
 import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.plugins.shared.LauncherOverlayManager;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -76,34 +73,25 @@
             PagedOrientationHandler orientationHandler) {
         calculateTaskSize(context, dp, outRect, orientationHandler);
         if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
-            Rect targetInsets = dp.getInsets();
-            int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
-            return dp.hotseatBarSizePx + hotseatInset;
+            return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
         } else {
             return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler);
         }
     }
 
     @Override
-    public void onSwipeUpToRecentsComplete() {
-        super.onSwipeUpToRecentsComplete();
-        Launcher launcher = getCreatedActivity();
-        if (launcher != null) {
-            RecentsView recentsView = launcher.getOverviewPanel();
-            DiscoveryBounce.showForOverviewIfNeeded(launcher,
-                    recentsView.getPagedOrientationHandler());
-        }
-    }
-
-    @Override
     public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {
         Launcher launcher = getCreatedActivity();
         if (launcher == null) {
             return;
         }
-        // Ensure recents is at the correct position for NORMAL state. For example, when we detach
-        // recents, we assume the first task is invisible, making translation off by one task.
-        launcher.getStateManager().reapplyState();
+        // When going to home, the state animator we use has SKIP_OVERVIEW because we assume that
+        // setRecentsAttachedToAppWindow() will handle animating Overview instead. Thus, at the end
+        // of the animation, we should ensure recents is at the correct position for NORMAL state.
+        // For example, when doing a long swipe to home, RecentsView may be scaled down. This is
+        // relatively expensive, so do it on the next frame instead of critical path.
+        MAIN_EXECUTOR.getHandler().post(launcher.getStateManager()::reapplyState);
+
         launcher.getRootView().setForceHideBackArrow(false);
         notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
     }
@@ -118,31 +106,24 @@
     }
 
     @Override
+    public void onOneHandedModeStateChanged(boolean activated) {
+        Launcher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return;
+        }
+        launcher.onOneHandedStateChanged(activated);
+    }
+
+    @Override
     public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
             boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
         notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
         DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
             @Override
-            public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
-                    long duration) {
-                mActivity.getShelfPeekAnim().setShelfState(shelfState, interpolator, duration);
-            }
-
-            @Override
             protected void createBackgroundToOverviewAnim(BaseQuickstepLauncher activity,
                     PendingAnimation pa) {
                 super.createBackgroundToOverviewAnim(activity, pa);
 
-                if (!activity.getDeviceProfile().isVerticalBarLayout()
-                        && SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) {
-                    // Don't animate the shelf when the mode is NO_BUTTON, because we
-                    // update it atomically.
-                    pa.add(activity.getStateManager().createStateElementAnimation(
-                            INDEX_SHELF_ANIM,
-                            BACKGROUND_APP.getVerticalProgress(activity),
-                            OVERVIEW.getVerticalProgress(activity)));
-                }
-
                 // Animate the blur and wallpaper zoom
                 float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
                 float toDepthRatio = OVERVIEW.getDepth(activity);
@@ -192,37 +173,55 @@
     }
 
     @Nullable
+    private LauncherTaskbarUIController getTaskbarController() {
+        BaseQuickstepLauncher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return null;
+        }
+        return launcher.getTaskbarUIController();
+    }
+
+    @Nullable
     @Override
     public RecentsView getVisibleRecentsView() {
         Launcher launcher = getVisibleLauncher();
-        return launcher != null && launcher.getStateManager().getState().overviewUi
-                ? launcher.getOverviewPanel() : null;
+        RecentsView recentsView =
+                launcher != null && launcher.getStateManager().getState().overviewUi
+                        ? launcher.getOverviewPanel() : null;
+        if (recentsView == null || (!launcher.hasBeenResumed()
+                && recentsView.getRunningTaskId() == -1)) {
+            // If live tile has ended, return null.
+            return null;
+        }
+        return recentsView;
     }
 
     @Nullable
     @UiThread
     private Launcher getVisibleLauncher() {
         Launcher launcher = getCreatedActivity();
-        return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus()
-                ? launcher : null;
+        return (launcher != null) && launcher.isStarted()
+                && ((ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode())
+                || launcher.hasBeenResumed()) ? launcher : null;
     }
 
     @Override
     public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "switchToRecentsIfVisible");
-        }
         Launcher launcher = getVisibleLauncher();
         if (launcher == null) {
             return false;
         }
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isInLiveTileMode()) {
+            RecentsView recentsView = getVisibleRecentsView();
+            if (recentsView == null) {
+                return false;
+            }
+        }
 
-        launcher.getUserEventDispatcher().logActionCommand(
-                LauncherLogProto.Action.Command.RECENTS_BUTTON,
-                getContainerType(),
-                LauncherLogProto.ContainerType.TASKSWITCHER);
+        closeOverlay();
         launcher.getStateManager().goToState(OVERVIEW,
-                launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
+                launcher.getStateManager().shouldAnimateStateChange(),
+                onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
         return true;
     }
 
@@ -262,23 +261,6 @@
     }
 
     @Override
-    public void updateOverviewPredictionState() {
-        Launcher launcher = getCreatedActivity();
-        if (launcher == null) {
-            return;
-        }
-        PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
-                PredictionUiStateManager.Client.OVERVIEW);
-    }
-
-    @Override
-    public int getContainerType() {
-        final Launcher launcher = getVisibleLauncher();
-        return launcher != null ? launcher.getStateManager().getState().containerType
-                : LauncherLogProto.ContainerType.APP;
-    }
-
-    @Override
     public boolean isInLiveTileMode() {
         Launcher launcher = getCreatedActivity();
         return launcher != null && launcher.getStateManager().getState() == OVERVIEW &&
@@ -309,37 +291,66 @@
     }
 
     @Override
-    protected float getExtraSpace(Context context, DeviceProfile dp,
-            PagedOrientationHandler orientationHandler) {
-        if ((dp.isVerticalBarLayout() && !showOverviewActions(context))
-                || hideShelfInTwoButtonLandscape(context, orientationHandler)) {
-            return 0;
+    void onOverviewServiceBound() {
+        final BaseQuickstepLauncher activity = getCreatedActivity();
+        if (activity == null) return;
+        activity.getAppTransitionManager().registerRemoteTransitions();
+    }
+
+    @Override
+    public @Nullable Animator getParallelAnimationToLauncher(GestureEndTarget endTarget,
+            long duration) {
+        LauncherTaskbarUIController uiController = getTaskbarController();
+        Animator superAnimator = super.getParallelAnimationToLauncher(endTarget, duration);
+        if (uiController == null) {
+            return superAnimator;
+        }
+        LauncherState toState = stateFromGestureEndTarget(endTarget);
+        Animator taskbarAnimator = uiController.createAnimToLauncher(toState, duration);
+        if (superAnimator == null) {
+            return taskbarAnimator;
         } else {
-            Resources res = context.getResources();
-            if (showOverviewActions(context)) {
-                //TODO: this needs to account for the swipe gesture height and accessibility
-                // UI when shown.
-                float actionsBottomMargin = 0;
-                if (!dp.isVerticalBarLayout()) {
-                    if (getMode(context) == Mode.THREE_BUTTONS) {
-                        actionsBottomMargin = res.getDimensionPixelSize(
-                            R.dimen.overview_actions_bottom_margin_three_button);
-                    } else {
-                        actionsBottomMargin = res.getDimensionPixelSize(
-                            R.dimen.overview_actions_bottom_margin_gesture);
-                    }
-                }
-                float actionsHeight = actionsBottomMargin
-                        + res.getDimensionPixelSize(R.dimen.overview_actions_height);
-                return actionsHeight;
-            } else {
-                return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
-                        + res.getDimensionPixelSize(
-                        R.dimen.dynamic_grid_hotseat_extra_vertical_size)
-                        + res.getDimensionPixelSize(
-                        R.dimen.dynamic_grid_hotseat_bottom_padding);
-            }
+            AnimatorSet animatorSet = new AnimatorSet();
+            animatorSet.playTogether(superAnimator, taskbarAnimator);
+            return animatorSet;
         }
     }
 
-}
\ No newline at end of file
+    @Override
+    protected int getOverviewScrimColorForState(BaseQuickstepLauncher launcher,
+            LauncherState state) {
+        return state.getWorkspaceScrimColor(launcher);
+    }
+
+    @Override
+    public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+        LauncherTaskbarUIController uiController = getTaskbarController();
+        if (uiController == null) {
+            return super.deferStartingActivity(deviceState, ev);
+        }
+        return uiController.isEventOverAnyTaskbarItem(ev);
+    }
+
+    @Override
+    public boolean shouldCancelCurrentGesture() {
+        LauncherTaskbarUIController uiController = getTaskbarController();
+        if (uiController == null) {
+            return super.shouldCancelCurrentGesture();
+        }
+        return uiController.isDraggingItem();
+    }
+
+    @Override
+    public LauncherState stateFromGestureEndTarget(GestureEndTarget endTarget) {
+        switch (endTarget) {
+            case RECENTS:
+                return OVERVIEW;
+            case NEW_TASK:
+            case LAST_TASK:
+                return QUICK_SWITCH;
+            case HOME:
+            default:
+                return NORMAL;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
new file mode 100644
index 0000000..19cad53
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -0,0 +1,432 @@
+/*
+ * 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.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;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.util.Size;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DynamicResource;
+import com.android.launcher3.util.ObjectWrapper;
+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;
+import com.android.systemui.plugins.ResourceProvider;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.ArrayList;
+
+/**
+ * Temporary class to allow easier refactoring
+ */
+public class LauncherSwipeHandlerV2 extends
+        AbsSwipeUpHandler<BaseQuickstepLauncher, RecentsView, LauncherState> {
+
+    public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
+            TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
+            boolean continuingLastGesture, InputConsumerController inputConsumer) {
+        super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs,
+                continuingLastGesture, inputConsumer);
+    }
+
+
+    @Override
+    protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
+            long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
+            RemoteAnimationTargetCompat runningTaskTarget) {
+        if (mActivity == null) {
+            mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+                    isPresent -> mRecentsView.startHome());
+            return new HomeAnimationFactory() {
+                @Override
+                public AnimatorPlaybackController createActivityAnimationToHome() {
+                    return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
+                }
+            };
+        }
+
+        final View workspaceView = findWorkspaceView(launchCookies,
+                mRecentsView.getRunningTaskView());
+        boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
+
+        mActivity.getRootView().setForceHideBackArrow(true);
+        mActivity.setHintUserWillBeActive();
+
+        if (!canUseWorkspaceView || appCanEnterPip) {
+            return new LauncherHomeAnimationFactory();
+        }
+        if (workspaceView instanceof LauncherAppWidgetHostView) {
+            return createWidgetHomeAnimationFactory((LauncherAppWidgetHostView) workspaceView,
+                    isTargetTranslucent, runningTaskTarget);
+        }
+        return createIconHomeAnimationFactory(workspaceView);
+    }
+
+    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 */);
+
+        // 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.
+        float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
+
+        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;
+
+            @Override
+            public RectF getWindowTargetRect() {
+                super.getWindowTargetRect();
+                return iconLocation;
+            }
+
+            @Override
+            public void setAnimation(RectFSpringAnim anim) {
+                super.setAnimation(anim);
+                anim.addAnimatorListener(floatingIconView);
+                floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+                floatingIconView.setFastFinishRunnable(anim::end);
+            }
+
+            @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,
+                        windowAlphaThreshold, radius, false);
+            }
+        };
+    }
+
+    private HomeAnimationFactory createWidgetHomeAnimationFactory(
+            LauncherAppWidgetHostView hostView, boolean isTargetTranslucent,
+            RemoteAnimationTargetCompat runningTaskTarget) {
+        final float floatingWidgetAlpha = isTargetTranslucent ? 0 : 1;
+        RectF backgroundLocation = new RectF();
+        Rect crop = new Rect();
+        mTaskViewSimulator.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);
+
+        return new FloatingViewHomeAnimationFactory(floatingWidgetView) {
+
+            @Override
+            @Nullable
+            protected View getViewIgnoredInWorkspaceRevealAnimation() {
+                return hostView;
+            }
+
+            @Override
+            public RectF getWindowTargetRect() {
+                super.getWindowTargetRect();
+                return backgroundLocation;
+            }
+
+            @Override
+            public float getEndRadius(RectF cropRectF) {
+                return floatingWidgetView.getInitialCornerRadius();
+            }
+
+            @Override
+            public void setAnimation(RectFSpringAnim anim) {
+                super.setAnimation(anim);
+
+                anim.addAnimatorListener(floatingWidgetView);
+                floatingWidgetView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+                floatingWidgetView.setFastFinishRunnable(anim::end);
+            }
+
+            @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);
+                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);
+                floatingWidgetView.update(currentRect, floatingWidgetAlpha, foregroundAlpha,
+                        fallbackBackgroundAlpha, 1 - progress);
+            }
+
+            @Override
+            protected float getWindowAlpha(float progress) {
+                return 1 - mapBoundToRange(progress, 0, 0.5f, 0, 1, LINEAR);
+            }
+        };
+    }
+
+    /**
+     * Returns the associated view on the workspace matching one of the launch cookies, or the app
+     * associated with the running task.
+     */
+    @Nullable
+    private View findWorkspaceView(ArrayList<IBinder> launchCookies, TaskView runningTaskView) {
+        if (mIsSwipingPipToHome) {
+            // Disable if swiping to PIP
+            return null;
+        }
+        if (runningTaskView == null || runningTaskView.getTask() == null
+                || runningTaskView.getTask().key.getComponent() == null) {
+            // Disable if it's an invalid task
+            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
+        int launchCookieItemId = NO_MATCHING_ID;
+        for (IBinder cookie : launchCookies) {
+            Integer itemId = ObjectWrapper.unwrap(cookie);
+            if (itemId != null) {
+                launchCookieItemId = itemId;
+                break;
+            }
+        }
+
+        return mActivity.getWorkspace().getFirstMatchForAppClose(launchCookieItemId,
+                runningTaskView.getTask().key.getComponent().getPackageName(),
+                UserHandle.of(runningTaskView.getTask().key.userId));
+    }
+
+    @Override
+    protected void finishRecentsControllerToHome(Runnable callback) {
+        mRecentsAnimationController.finish(
+                true /* toRecents */, callback, true /* sendUserLeaveHint */);
+    }
+
+    private class FloatingViewHomeAnimationFactory extends LauncherHomeAnimationFactory {
+
+        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
+        public boolean shouldPlayAtomicWorkspaceReveal() {
+            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;
+            // Ensures the velocity is always aligned with the direction.
+            float pixelPerSecond = Math.abs(mSwipeVelocity) * Math.signum(endValue - mTransY);
+
+            DragLayer dl = mActivity.getDragLayer();
+            Workspace workspace = mActivity.getWorkspace();
+            Hotseat hotseat = mActivity.getHotseat();
+
+            ResourceProvider rp = DynamicResource.provider(mActivity);
+            ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext())
+                    .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness))
+                    .setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping))
+                    .setMinimumVisibleChange(1f)
+                    .setStartValue(startValue)
+                    .setEndValue(endValue)
+                    .setStartVelocity(pixelPerSecond)
+                    .build(dl, VIEW_TRANSLATE_Y);
+            springTransY.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    dl.setTranslationY(0f);
+                    dl.setAlpha(1f);
+                    SCALE_PROPERTY.set(workspace, 1f);
+                    SCALE_PROPERTY.set(hotseat, 1f);
+                }
+            });
+
+            mBounceBackAnimator = springTransY;
+            mBounceBackAnimator.start();
+        }
+
+        @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) {
+                mBounceBackAnimator.cancel();
+            }
+        }
+    }
+
+    private class LauncherHomeAnimationFactory extends HomeAnimationFactory {
+
+        /**
+         * Returns a view which should be excluded from the Workspace animation, or null if there
+         * is no view to exclude.
+         */
+        @Nullable
+        protected View getViewIgnoredInWorkspaceRevealAnimation() {
+            return null;
+        }
+
+        @NonNull
+        @Override
+        public AnimatorPlaybackController createActivityAnimationToHome() {
+            // Return an empty APC here since we have an non-user controlled animation
+            // to home.
+            long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx);
+            return mActivity.getStateManager().createAnimationToNewWorkspace(
+                    NORMAL, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS);
+        }
+
+        @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();
+            }
+        }
+
+        @Override
+        public boolean supportSwipePipToHome() {
+            return true;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 1081548..35a851a 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -30,16 +30,17 @@
 import android.graphics.Point;
 import android.graphics.RectF;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.MotionEvent;
 import android.view.Surface;
 
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController.Info;
 
 import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
 
 /**
  * Maintains state for supporting nav bars and tracking their gestures in multiple orientations.
@@ -51,19 +52,60 @@
  */
 class OrientationTouchTransformer {
 
+    private static class CurrentDisplay {
+        public Point size;
+        public int rotation;
+
+        CurrentDisplay() {
+            this.size = new Point(0, 0);
+            this.rotation = 0;
+        }
+
+        CurrentDisplay(Point size, int rotation) {
+            this.size = size;
+            this.rotation = rotation;
+        }
+
+        @Override
+        public String toString() {
+            return "CurrentDisplay:"
+                    + " rotation: " + rotation
+                    + " size: " + size;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            CurrentDisplay display = (CurrentDisplay) o;
+            if (rotation != display.rotation) return false;
+
+            return Objects.equals(size, display.size);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(size, rotation);
+        }
+    };
+
     private static final String TAG = "OrientationTouchTransformer";
     private static final boolean DEBUG = false;
-    private static final int MAX_ORIENTATIONS = 4;
 
     private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
 
     private final Matrix mTmpMatrix = new Matrix();
     private final float[] mTmpPoint = new float[2];
 
-    private SparseArray<OrientationRectF> mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS);
+    private final Map<CurrentDisplay, OrientationRectF> mSwipeTouchRegions =
+            new HashMap<CurrentDisplay, OrientationRectF>();
     private final RectF mAssistantLeftRegion = new RectF();
     private final RectF mAssistantRightRegion = new RectF();
-    private int mCurrentDisplayRotation;
+    private final RectF mOneHandedModeRegion = new RectF();
+    private CurrentDisplay mCurrentDisplay = new CurrentDisplay();
+    private int mNavBarGesturalHeight;
+    private final int mNavBarLargerGesturalHeight;
     private boolean mEnableMultipleRegions;
     private Resources mResources;
     private OrientationRectF mLastRectTouched;
@@ -83,7 +125,7 @@
      * QUICKSTEP_ROTATION_UNINITIALIZED, then user has not tapped on an active nav region.
      * Otherwise it will be the rotation of the display when the user first interacted with the
      * active nav bar region.
-     * The "session" ends when {@link #enableMultipleRegions(boolean, DefaultDisplay.Info)} is
+     * The "session" ends when {@link #enableMultipleRegions(boolean, Info)} is
      * called - usually from a timeout or if user starts interacting w/ the foreground app.
      *
      * This is different than {@link #mLastRectTouched} as it can get reset by the system whereas
@@ -103,18 +145,38 @@
         mResources = resources;
         mMode = mode;
         mContractInfo = contractInfo;
+        mNavBarGesturalHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
+        mNavBarLargerGesturalHeight = ResourceUtils.getDimenByName(
+                ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, resources,
+                mNavBarGesturalHeight);
     }
 
-    void setNavigationMode(SysUINavigationMode.Mode newMode, DefaultDisplay.Info info) {
+    private void refreshTouchRegion(Info info, Resources newRes) {
+        // Swipe touch regions are independent of nav mode, so we have to clear them explicitly
+        // here to avoid, for ex, a nav region for 2-button rotation 0 being used for 3-button mode
+        // It tries to cache and reuse swipe regions whenever possible based only on rotation
+        mResources = newRes;
+        mSwipeTouchRegions.clear();
+        resetSwipeRegions(info);
+    }
+
+    void setNavigationMode(SysUINavigationMode.Mode newMode, Info info, Resources newRes) {
+        if (DEBUG) {
+            Log.d(TAG, "setNavigationMode new: " + newMode + " oldMode: " + mMode + " " + this);
+        }
         if (mMode == newMode) {
             return;
         }
         this.mMode = newMode;
-        // Swipe touch regions are independent of nav mode, so we have to clear them explicitly
-        // here to avoid, for ex, a nav region for 2-button rotation 0 being used for 3-button mode
-        // It tries to cache and reuse swipe regions whenever possible based only on rotation
-        mSwipeTouchRegions.clear();
-        resetSwipeRegions(info);
+        refreshTouchRegion(info, newRes);
+    }
+
+    void setGesturalHeight(int newGesturalHeight, Info info, Resources newRes) {
+        if (mNavBarGesturalHeight == newGesturalHeight) {
+            return;
+        }
+        mNavBarGesturalHeight = newGesturalHeight;
+        refreshTouchRegion(info, newRes);
     }
 
     /**
@@ -123,24 +185,25 @@
      * alongside other regions.
      * Ok to call multiple times
      *
-     * @see #enableMultipleRegions(boolean, DefaultDisplay.Info)
+     * @see #enableMultipleRegions(boolean, Info)
      */
-    void createOrAddTouchRegion(DefaultDisplay.Info info) {
-        mCurrentDisplayRotation = info.rotation;
+    void createOrAddTouchRegion(Info info) {
+        mCurrentDisplay = new CurrentDisplay(info.currentSize, info.rotation);
+
         if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
-                && mCurrentDisplayRotation == mQuickStepStartingRotation) {
+                && mCurrentDisplay.rotation == mQuickStepStartingRotation) {
             // User already was swiping and the current screen is same rotation as the starting one
             // Remove active nav bars in other rotations except for the one we started out in
             resetSwipeRegions(info);
             return;
         }
-        OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplayRotation);
+        OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplay);
         if (region != null) {
             return;
         }
 
         if (mEnableMultipleRegions) {
-            mSwipeTouchRegions.put(mCurrentDisplayRotation, createRegionForDisplay(info));
+            mSwipeTouchRegions.put(mCurrentDisplay, createRegionForDisplay(info));
         } else {
             resetSwipeRegions(info);
         }
@@ -153,7 +216,7 @@
      * @param enableMultipleRegions Set to true to start tracking multiple nav bar regions
      * @param info The current displayInfo which will be the start of the quickswitch gesture
      */
-    void enableMultipleRegions(boolean enableMultipleRegions, DefaultDisplay.Info info) {
+    void enableMultipleRegions(boolean enableMultipleRegions, Info info) {
         mEnableMultipleRegions = enableMultipleRegions &&
                 mMode != SysUINavigationMode.Mode.TWO_BUTTONS;
         if (mEnableMultipleRegions) {
@@ -174,7 +237,7 @@
      *
      * @param displayInfo The display whos rotation will be used as the current active rotation
      */
-    void setSingleActiveRegion(DefaultDisplay.Info displayInfo) {
+    void setSingleActiveRegion(Info displayInfo) {
         mActiveTouchRotation = displayInfo.rotation;
         resetSwipeRegions(displayInfo);
     }
@@ -185,60 +248,64 @@
      * To be called whenever we want to stop tracking more than one swipe region.
      * Ok to call multiple times.
      */
-    private void resetSwipeRegions(DefaultDisplay.Info region) {
+    private void resetSwipeRegions(Info region) {
         if (DEBUG) {
-            Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplayRotation);
+            Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation);
         }
 
-        mCurrentDisplayRotation = region.rotation;
-        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation);
+        mCurrentDisplay = new CurrentDisplay(region.currentSize, region.rotation);
+        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
         if (regionToKeep == null) {
             regionToKeep = createRegionForDisplay(region);
         }
         mSwipeTouchRegions.clear();
-        mSwipeTouchRegions.put(mCurrentDisplayRotation, regionToKeep);
+        mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
         updateAssistantRegions(regionToKeep);
     }
 
     private void resetSwipeRegions() {
-        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation);
+        OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
         mSwipeTouchRegions.clear();
         if (regionToKeep != null) {
-            mSwipeTouchRegions.put(mCurrentDisplayRotation, regionToKeep);
+            mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
             updateAssistantRegions(regionToKeep);
         }
     }
 
-    private OrientationRectF createRegionForDisplay(DefaultDisplay.Info display) {
+    private OrientationRectF createRegionForDisplay(Info display) {
         if (DEBUG) {
-            Log.d(TAG, "creating rotation region for: " + mCurrentDisplayRotation);
+            Log.d(TAG, "creating rotation region for: " + mCurrentDisplay.rotation
+            + " with mode: " + mMode + " displayRotation: " + display.rotation);
         }
 
-        Point size = display.realSize;
+        Point size = display.currentSize;
         int rotation = display.rotation;
+        int touchHeight = mNavBarGesturalHeight;
         OrientationRectF orientationRectF =
                 new OrientationRectF(0, 0, size.x, size.y, rotation);
         if (mMode == SysUINavigationMode.Mode.NO_BUTTON) {
-            int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
             orientationRectF.top = orientationRectF.bottom - touchHeight;
             updateAssistantRegions(orientationRectF);
         } else {
             mAssistantLeftRegion.setEmpty();
             mAssistantRightRegion.setEmpty();
+            int navbarSize = getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
             switch (rotation) {
                 case Surface.ROTATION_90:
                     orientationRectF.left = orientationRectF.right
-                            - getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
+                            - navbarSize;
                     break;
                 case Surface.ROTATION_270:
                     orientationRectF.right = orientationRectF.left
-                            + getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
+                            + navbarSize;
                     break;
                 default:
-                    orientationRectF.top = orientationRectF.bottom
-                            - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
+                    orientationRectF.top = orientationRectF.bottom - touchHeight;
             }
         }
+        // One handed gestural only active on portrait mode
+        mOneHandedModeRegion.set(0, orientationRectF.bottom - mNavBarLargerGesturalHeight,
+                size.x, size.y);
 
         return orientationRectF;
     }
@@ -264,14 +331,18 @@
 
     }
 
+    boolean touchInOneHandedModeRegion(MotionEvent ev) {
+        return mOneHandedModeRegion.contains(ev.getX(), ev.getY());
+    }
+
     private int getNavbarSize(String resName) {
         return ResourceUtils.getNavbarSize(resName, mResources);
     }
 
     boolean touchInValidSwipeRegions(float x, float y) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_SWIPE_TO_HOME, "touchInValidSwipeRegions " + x + "," + y + " in "
-                    + mLastRectTouched);
+        if (DEBUG) {
+            Log.d(TAG, "touchInValidSwipeRegions " + x + "," + y + " in "
+                    + mLastRectTouched + " this: " + this);
         }
         if (mLastRectTouched != null) {
             return mLastRectTouched.contains(x, y);
@@ -312,22 +383,15 @@
                     return;
                 }
 
-                for (int i = 0; i < MAX_ORIENTATIONS; i++) {
-                    OrientationRectF rect = mSwipeTouchRegions.get(i);
-                    if (TestProtocol.sDebugTracing) {
-                        Log.d(TestProtocol.NO_SWIPE_TO_HOME, "transform:DOWN, rect=" + rect);
-                    }
+                for (OrientationRectF rect : mSwipeTouchRegions.values()) {
                     if (rect == null) {
                         continue;
                     }
                     if (rect.applyTransform(event, false)) {
-                        if (TestProtocol.sDebugTracing) {
-                            Log.d(TestProtocol.NO_SWIPE_TO_HOME, "setting mLastRectTouched");
-                        }
                         mLastRectTouched = rect;
                         mActiveTouchRotation = rect.mRotation;
                         if (mEnableMultipleRegions
-                                && mCurrentDisplayRotation == mActiveTouchRotation) {
+                                && 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;
@@ -350,11 +414,14 @@
         pw.println("  lastTouchedRegion=" + mLastRectTouched);
         pw.println("  multipleRegionsEnabled=" + mEnableMultipleRegions);
         StringBuilder regions = new StringBuilder("  currentTouchableRotations=");
-        for(int i = 0; i < mSwipeTouchRegions.size(); i++) {
-            OrientationRectF rectF = mSwipeTouchRegions.get(mSwipeTouchRegions.keyAt(i));
-            regions.append(rectF.mRotation).append(" ");
+        for (CurrentDisplay key: mSwipeTouchRegions.keySet()) {
+            OrientationRectF rectF = mSwipeTouchRegions.get(key);
+            regions.append(rectF).append(" ");
         }
         pw.println(regions.toString());
+        pw.println("  mNavBarGesturalHeight=" + mNavBarGesturalHeight);
+        pw.println("  mNavBarLargerGesturalHeight=" + mNavBarLargerGesturalHeight);
+        pw.println("  mOneHandedModeRegion=" + mOneHandedModeRegion);
     }
 
     private class OrientationRectF extends RectF {
@@ -386,13 +453,14 @@
 
         boolean applyTransform(MotionEvent event, boolean forceTransform) {
             mTmpMatrix.reset();
-            postDisplayRotation(deltaRotation(mCurrentDisplayRotation, mRotation),
+            postDisplayRotation(deltaRotation(mCurrentDisplay.rotation, mRotation),
                     mHeight, mWidth, mTmpMatrix);
             if (forceTransform) {
                 if (DEBUG) {
                     Log.d(TAG, "Transforming rotation due to forceTransform, "
-                            + "mCurrentRotation: " + mCurrentDisplayRotation
-                            + "mRotation: " + mRotation);
+                            + "mCurrentRotation: " + mCurrentDisplay.rotation
+                            + "mRotation: " + mRotation
+                            + " this: " + this);
                 }
                 event.transform(mTmpMatrix);
                 return true;
@@ -403,9 +471,10 @@
 
             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]));
+                        + " new: " + mTmpPoint[0] + ", " + mTmpPoint[1]
+                        + " rect: " + this + " forceTransform: " + forceTransform
+                        + " contains: " + contains(mTmpPoint[0], mTmpPoint[1])
+                        + " this: " + this);
             }
 
             if (contains(mTmpPoint[0], mTmpPoint[1])) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverscrollPluginFactory.java b/quickstep/src/com/android/quickstep/OverscrollPluginFactory.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/OverscrollPluginFactory.java
rename to quickstep/src/com/android/quickstep/OverscrollPluginFactory.java
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
new file mode 100644
index 0000000..5d1f908
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.graphics.PointF;
+import android.os.Build;
+import android.os.SystemClock;
+import android.os.Trace;
+
+import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
+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.util.ArrayList;
+
+/**
+ * Helper class to handle various atomic commands for switching between Overview.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class OverviewCommandHelper {
+
+    public static final int TYPE_SHOW = 1;
+    public static final int TYPE_SHOW_NEXT_FOCUS = 2;
+    public static final int TYPE_HIDE = 3;
+    public static final int TYPE_TOGGLE = 4;
+
+    private static final String TRANSITION_NAME = "Transition:toOverview";
+
+    private final TouchInteractionService mService;
+    private final OverviewComponentObserver mOverviewComponentObserver;
+    private final TaskAnimationManager mTaskAnimationManager;
+    private final ArrayList<CommandInfo> mPendingCommands = new ArrayList<>();
+
+    public OverviewCommandHelper(TouchInteractionService service,
+            OverviewComponentObserver observer,
+            TaskAnimationManager taskAnimationManager) {
+        mService = service;
+        mOverviewComponentObserver = observer;
+        mTaskAnimationManager = taskAnimationManager;
+    }
+
+    /**
+     * Called when the command finishes execution.
+     */
+    private void scheduleNextTask(CommandInfo command) {
+        if (!mPendingCommands.isEmpty() && mPendingCommands.get(0) == command) {
+            mPendingCommands.remove(0);
+            executeNext();
+        }
+    }
+
+    /**
+     * Executes the next command from the queue. If the command finishes immediately (returns true),
+     * it continues to execute the next command, until the queue is empty of a command defer's its
+     * completion (returns false).
+     */
+    @UiThread
+    private void executeNext() {
+        if (mPendingCommands.isEmpty()) {
+            return;
+        }
+        CommandInfo cmd = mPendingCommands.get(0);
+        if (executeCommand(cmd)) {
+            scheduleNextTask(cmd);
+        }
+    }
+
+    @UiThread
+    private void addCommand(CommandInfo cmd) {
+        boolean wasEmpty = mPendingCommands.isEmpty();
+        mPendingCommands.add(cmd);
+        if (wasEmpty) {
+            executeNext();
+        }
+    }
+
+    /**
+     * Adds a command to be executed next, after all pending tasks are completed
+     */
+    @BinderThread
+    public void addCommand(int type) {
+        CommandInfo cmd = new CommandInfo(type);
+        MAIN_EXECUTOR.execute(() -> addCommand(cmd));
+    }
+
+    @UiThread
+    public void clearPendingCommands() {
+        mPendingCommands.clear();
+    }
+
+    private TaskView getNextTask(RecentsView view) {
+        final TaskView runningTaskView = view.getRunningTaskView();
+
+        if (runningTaskView == null) {
+            return view.getTaskViewCount() > 0 ? view.getTaskViewAt(0) : null;
+        } else {
+            final TaskView nextTask = view.getNextTaskView();
+            return nextTask != null ? nextTask : runningTaskView;
+        }
+    }
+
+    private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
+        RunnableList callbackList = null;
+        if (taskView != null) {
+            taskView.setEndQuickswitchCuj(true);
+            callbackList = taskView.launchTaskAnimated();
+        }
+
+        if (callbackList != null) {
+            callbackList.add(() -> scheduleNextTask(cmd));
+            return false;
+        } else {
+            recents.startHome();
+            return true;
+        }
+    }
+
+    /**
+     * Executes the task and returns true if next task can be executed. If false, then the next
+     * task is deferred until {@link #scheduleNextTask} is called
+     */
+    private <T extends StatefulActivity<?>> boolean executeCommand(CommandInfo cmd) {
+        BaseActivityInterface<?, T> activityInterface =
+                mOverviewComponentObserver.getActivityInterface();
+        RecentsView recents = activityInterface.getVisibleRecentsView();
+        if (recents == null) {
+            if (cmd.type == TYPE_HIDE) {
+                // already hidden
+                return true;
+            }
+        } else {
+            switch (cmd.type) {
+                case TYPE_SHOW:
+                    // already visible
+                    return true;
+                case TYPE_HIDE: {
+                    int currentPage = recents.getNextPage();
+                    TaskView tv = (currentPage >= 0 && currentPage < recents.getTaskViewCount())
+                            ? (TaskView) recents.getPageAt(currentPage)
+                            : null;
+                    return launchTask(recents, tv, cmd);
+                }
+                case TYPE_TOGGLE:
+                    return launchTask(recents, getNextTask(recents), cmd);
+            }
+        }
+
+        if (activityInterface.switchToRecentsIfVisible(() -> scheduleNextTask(cmd))) {
+            // If successfully switched, wait until animation finishes
+            return false;
+        }
+
+        final T activity = activityInterface.getCreatedActivity();
+        if (activity != null) {
+            InteractionJankMonitorWrapper.begin(
+                    activity.getRootView(),
+                    InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+        }
+
+        GestureState gestureState = mService.createGestureState(GestureState.DEFAULT_STATE);
+        gestureState.setHandlingAtomicEvent(true);
+        AbsSwipeUpHandler interactionHandler = mService.getSwipeUpHandlerFactory()
+                .newHandler(gestureState, cmd.createTime);
+        interactionHandler.setGestureEndCallback(
+                () -> onTransitionComplete(cmd, interactionHandler));
+        interactionHandler.initWhenReady();
+
+        RecentsAnimationListener recentAnimListener = new RecentsAnimationListener() {
+            @Override
+            public void onRecentsAnimationStart(RecentsAnimationController controller,
+                    RecentsAnimationTargets targets) {
+                interactionHandler.onGestureEnded(0, new PointF(), new PointF());
+                cmd.removeListener(this);
+            }
+
+            @Override
+            public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+                interactionHandler.onGestureCancelled();
+                cmd.removeListener(this);
+
+                RecentsView createdRecents =
+                        activityInterface.getCreatedActivity().getOverviewPanel();
+                if (createdRecents != null) {
+                    createdRecents.onRecentsAnimationComplete();
+                }
+            }
+        };
+
+        if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+            cmd.mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(gestureState);
+            cmd.mActiveCallbacks.addListener(interactionHandler);
+            mTaskAnimationManager.notifyRecentsAnimationState(interactionHandler);
+            interactionHandler.onGestureStarted(true /*isLikelyToStartNewTask*/);
+
+            cmd.mActiveCallbacks.addListener(recentAnimListener);
+            mTaskAnimationManager.notifyRecentsAnimationState(recentAnimListener);
+        } else {
+            Intent intent = new Intent(interactionHandler.getLaunchIntent());
+            intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
+            cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
+                    gestureState, intent, interactionHandler);
+            interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
+            cmd.mActiveCallbacks.addListener(recentAnimListener);
+        }
+
+        Trace.beginAsyncSection(TRANSITION_NAME, 0);
+        return false;
+    }
+
+    private void onTransitionComplete(CommandInfo cmd, AbsSwipeUpHandler handler) {
+        cmd.removeListener(handler);
+        Trace.endAsyncSection(TRANSITION_NAME, 0);
+
+        if (cmd.type == TYPE_SHOW_NEXT_FOCUS) {
+            RecentsView rv =
+                    mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
+            if (rv != null) {
+                // 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.requestFocus();
+                    } else {
+                        rv.requestFocus();
+                    }
+                } else {
+                    taskView.requestFocus();
+                }
+            }
+        }
+        scheduleNextTask(cmd);
+    }
+
+    private static class CommandInfo {
+        public final long createTime = SystemClock.elapsedRealtime();
+        public final int type;
+        RecentsAnimationCallbacks mActiveCallbacks;
+
+        CommandInfo(int type) {
+            this.type = type;
+        }
+
+        void removeListener(RecentsAnimationListener listener) {
+            if (mActiveCallbacks != null) {
+                mActiveCallbacks.removeListener(listener);
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 07f838b..0efe666 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -20,10 +20,10 @@
 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 
+import static com.android.launcher3.Utilities.createHomeIntent;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -35,6 +35,8 @@
 import android.content.pm.ResolveInfo;
 import android.util.SparseIntArray;
 
+import com.android.launcher3.tracing.OverviewComponentObserverProto;
+import com.android.launcher3.tracing.TouchInteractionServiceProto;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 
@@ -73,9 +75,7 @@
     public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
         mContext = context;
         mDeviceState = deviceState;
-        mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mCurrentHomeIntent = createHomeIntent();
         mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
         ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
         ComponentName myHomeComponent =
@@ -111,16 +111,17 @@
         if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
             updateOverviewTargets();
         }
+
+        // Notify ALL_APPS touch controller when one handed mode state activated or deactivated
+        if (mDeviceState.isOneHandedModeEnabled()) {
+            mActivityInterface.onOneHandedModeStateChanged(mDeviceState.isOneHandedModeActive());
+        }
     }
 
     private void updateOverviewTargets(Intent unused) {
         updateOverviewTargets();
     }
 
-    public boolean assistantGestureIsConstrained() {
-        return (mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
-    }
-
     /**
      * Update overview intent and {@link BaseActivityInterface} based off the current launcher home
      * component.
@@ -262,4 +263,17 @@
         pw.println("  overviewIntent=" + mOverviewIntent);
         pw.println("  homeIntent=" + mCurrentHomeIntent);
     }
+
+    /**
+     * Used for winscope tracing, see launcher_trace.proto
+     * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
+     * @param serviceProto The parent of this proto message.
+     */
+    public void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
+        OverviewComponentObserverProto.Builder overviewComponentObserver =
+                OverviewComponentObserverProto.newBuilder();
+        overviewComponentObserver.setOverviewActivityStarted(mActivityInterface.isStarted());
+        overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
+        serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 32268a4..65847f1 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,21 +15,32 @@
  */
 package com.android.quickstep;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.UserManager;
 import android.util.Log;
 
 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;
 
 @SuppressWarnings("unused")
+@TargetApi(Build.VERSION_CODES.R)
 public class QuickstepProcessInitializer extends MainProcessInitializer {
 
     private static final String TAG = "QuickstepProcessInitializer";
+    private static final int SETUP_DELAY_MILLIS = 5000;
 
-    public QuickstepProcessInitializer(Context context) { }
+    public QuickstepProcessInitializer(Context context) {
+        // Fake call to create an instance of InteractionJankMonitor to avoid binder calls during
+        // its initialization during transitions.
+        InteractionJankMonitorWrapper.cancel(-1);
+    }
 
     @Override
     protected void init(Context context) {
@@ -51,5 +62,9 @@
         // 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/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
rename to quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 5026f36..39af0db 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -53,17 +53,17 @@
                         Bundle::putInt, PortraitStatesTouchController::getHotseatTop);
             }
 
-            case TestProtocol.REQUEST_OVERVIEW_ACTIONS_ENABLED: {
-                response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
-                        FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get());
-                return response;
-            }
-
             case TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED: {
                 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
                         FeatureFlags.ENABLE_OVERVIEW_SHARE.get());
                 return response;
             }
+
+            case TestProtocol.REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED: {
+                response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+                        FeatureFlags.ENABLE_OVERVIEW_CONTENT_PUSH.get());
+                return response;
+            }
         }
 
         return super.call(method);
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 70b4f20..3080f04 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -22,15 +22,18 @@
 import android.app.ActivityManager;
 import android.os.Build;
 import android.os.Process;
+import android.util.Log;
 import android.util.SparseBooleanArray;
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.LooperExecutor;
 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 java.util.ArrayList;
 import java.util.Collections;
@@ -51,6 +54,9 @@
 
     // The list change id, increments as the task list changes in the system
     private int mChangeId;
+    // Whether we are currently updating the tasks in the background (up to when the result is
+    // posted back on the main thread)
+    private boolean mLoadingTasksInBackground;
 
     private TaskLoadResult mResultsBg = INVALID_RESULT;
     private TaskLoadResult mResultsUi = INVALID_RESULT;
@@ -61,7 +67,12 @@
         mKeyguardManager = keyguardManager;
         mChangeId = 1;
         mActivityManagerWrapper = activityManagerWrapper;
-        mActivityManagerWrapper.registerTaskStackListener(this);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
+    }
+
+    @VisibleForTesting
+    public boolean isLoadingTasksInBackground() {
+        return mLoadingTasksInBackground;
     }
 
     /**
@@ -90,19 +101,23 @@
             if (callback != null) {
                 // Copy synchronously as the changeId might change by next frame
                 ArrayList<Task> result = copyOf(mResultsUi);
-                mMainThreadExecutor.post(() -> callback.accept(result));
+                mMainThreadExecutor.post(() -> {
+                    callback.accept(result);
+                });
             }
 
             return requestLoadId;
         }
 
         // Kick off task loading in the background
+        mLoadingTasksInBackground = true;
         UI_HELPER_EXECUTOR.execute(() -> {
             if (!mResultsBg.isValidForRequest(requestLoadId, loadKeysOnly)) {
                 mResultsBg = loadTasksInBackground(Integer.MAX_VALUE, requestLoadId, loadKeysOnly);
             }
             TaskLoadResult loadResult = mResultsBg;
             mMainThreadExecutor.execute(() -> {
+                mLoadingTasksInBackground = false;
                 mResultsUi = loadResult;
                 if (callback != null) {
                     ArrayList<Task> result = copyOf(mResultsUi);
@@ -191,6 +206,7 @@
             } else {
                 task = new Task(taskKey);
             }
+            task.setLastSnapshotData(rawTask);
             allTasks.add(task);
         }
 
@@ -200,9 +216,7 @@
     private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
         ArrayList<Task> newTasks = new ArrayList<>();
         for (int i = 0; i < tasks.size(); i++) {
-            Task t = tasks.get(i);
-            newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable,
-                    t.isLocked, t.taskDescription, t.topActivity));
+            newTasks.add(new Task(tasks.get(i)));
         }
         return newTasks;
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
similarity index 62%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
rename to quickstep/src/com/android/quickstep/RecentsActivity.java
index 6f2f9fb..9dfcd12 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -18,55 +18,72 @@
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
+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.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.app.ActivityOptions;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.window.SplashScreen;
+
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAnimationRunner;
+import com.android.launcher3.LauncherAnimationRunner.AnimationResult;
+import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.fallback.FallbackRecentsStateController;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsDragLayer;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.RecentsAtomicAnimationFactory;
+import com.android.quickstep.util.SplitSelectStateController;
 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;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 
 /**
  * A recents activity that shows the recently launched tasks as swipable task cards.
@@ -79,7 +96,10 @@
 
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
 
+    private static final long HOME_APPEAR_DURATION = 250;
+
     private RecentsDragLayer mDragLayer;
+    private ScrimView mScrimView;
     private FallbackRecentsView mFallbackRecentsView;
     private OverviewActionsView mActionsView;
 
@@ -87,6 +107,9 @@
 
     private StateManager<RecentsState> mStateManager;
 
+    // Strong refs to runners which are cleared when the activity is destroyed
+    private RemoteAnimationFactory mActivityLaunchAnimationRunner;
+
     /**
      * Init drag layer and overview panel views.
      */
@@ -94,11 +117,18 @@
         inflateRootView(R.layout.fallback_recents_activity);
         setContentView(getRootView());
         mDragLayer = findViewById(R.id.drag_layer);
+        mScrimView = findViewById(R.id.scrim_view);
         mFallbackRecentsView = findViewById(R.id.overview_panel);
         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))
+        );
 
         mDragLayer.recreateControllers();
-        mFallbackRecentsView.init(mActionsView);
+        mFallbackRecentsView.init(mActionsView, splitPlaceholderView);
     }
 
     @Override
@@ -110,7 +140,7 @@
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
-        ACTIVITY_TRACKER.handleNewIntent(this, intent);
+        ACTIVITY_TRACKER.handleNewIntent(this);
     }
 
     /**
@@ -118,7 +148,6 @@
          * etc.)
          */
     protected void onHandleConfigChanged() {
-        mUserEventDispatcher = null;
         initDeviceProfile();
 
         AbstractFloatingView.closeOpenViews(this, true,
@@ -148,6 +177,10 @@
         return mDragLayer;
     }
 
+    public ScrimView getScrimView() {
+        return mScrimView;
+    }
+
     @Override
     public <T extends View> T getOverviewPanel() {
         return (T) mFallbackRecentsView;
@@ -164,41 +197,57 @@
     }
 
     @Override
-    public ActivityOptions getActivityLaunchOptions(final View v) {
+    public ActivityOptionsWrapper getActivityLaunchOptions(final View v, @Nullable ItemInfo item) {
         if (!(v instanceof TaskView)) {
-            return null;
+            return super.getActivityLaunchOptions(v, item);
         }
 
         final TaskView taskView = (TaskView) v;
-        RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler,
-                true /* startAtFrontOfQueue */) {
+        RunnableList onEndCallback = new RunnableList();
+
+        mActivityLaunchAnimationRunner = new RemoteAnimationFactory() {
+            @Override
+            public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
+                    RemoteAnimationTargetCompat[] wallpaperTargets,
+                    RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+                AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
+                        wallpaperTargets, nonAppTargets);
+                anim.addListener(resetStateListener());
+                result.setAnimation(anim, RecentsActivity.this, onEndCallback::executeAllAndDestroy,
+                        true /* skipFirstFrame */);
+            }
 
             @Override
-            public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
-                AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
-                        wallpaperTargets);
-                anim.addListener(resetStateListener());
-                result.setAnimation(anim, RecentsActivity.this);
+            public void onAnimationCancelled() {
+                onEndCallback.executeAllAndDestroy();
             }
         };
-        return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
-                runner, RECENTS_LAUNCH_DURATION,
+
+        final LauncherAnimationRunner wrapper = new LauncherAnimationRunner(
+                mUiHandler, mActivityLaunchAnimationRunner, true /* startAtFrontOfQueue */);
+        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);
+        final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(
+                ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
+                onEndCallback);
+        activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+        return activityOptions;
     }
 
     /**
      * Composes the animations for a launch from the recents list if possible.
      */
-    private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
+    private AnimatorSet  composeRecentsLaunchAnimator(TaskView taskView,
             RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets) {
         AnimatorSet target = new AnimatorSet();
         boolean activityClosing = taskIsATargetWithMode(appTargets, getTaskId(), MODE_CLOSING);
         PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
         createRecentsWindowAnimator(taskView, !activityClosing, appTargets,
-                wallpaperTargets, null /* depthController */, pa);
+                wallpaperTargets, nonAppTargets, null /* depthController */, pa);
         target.play(pa.buildAnim());
 
         // Found a visible recents task that matches the opening app, lets launch the app from there
@@ -219,6 +268,7 @@
         // onActivityStart callback.
         mFallbackRecentsView.setContentAlpha(1);
         super.onStart();
+        mFallbackRecentsView.updateLocusId();
     }
 
     @Override
@@ -227,6 +277,7 @@
 
         // Workaround for b/78520668, explicitly trim memory once UI is hidden
         onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
+        mFallbackRecentsView.updateLocusId();
     }
 
     @Override
@@ -260,6 +311,16 @@
         super.onConfigurationChanged(newConfig);
     }
 
+    @Override
+    public void onStateSetEnd(RecentsState state) {
+        super.onStateSetEnd(state);
+
+        if (state == RecentsState.DEFAULT) {
+            AccessibilityManagerCompat.sendStateEventToTest(getBaseContext(),
+                    OVERVIEW_STATE_ORDINAL);
+        }
+    }
+
     /**
      * Initialize/update the device profile.
      */
@@ -288,6 +349,7 @@
     protected void onDestroy() {
         super.onDestroy();
         ACTIVITY_TRACKER.onActivityDestroyed(this);
+        mActivityLaunchAnimationRunner = null;
     }
 
     @Override
@@ -297,14 +359,51 @@
     }
 
     public void startHome() {
-        startActivity(new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            RecentsView recentsView = getOverviewPanel();
+            recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
+                    this::startHomeInternal));
+        } else {
+            startHomeInternal();
+        }
     }
 
+    private void startHomeInternal() {
+        LauncherAnimationRunner runner = new LauncherAnimationRunner(
+                getMainThreadHandler(), mAnimationToHomeFactory, true);
+        RemoteAnimationAdapterCompat adapterCompat =
+                new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0);
+        startActivity(createHomeIntent(),
+                ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
+    }
+
+    private final RemoteAnimationFactory mAnimationToHomeFactory =
+            new RemoteAnimationFactory() {
+        @Override
+        public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
+                RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+            AnimatorPlaybackController controller = getStateManager()
+                    .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
+            controller.dispatchOnStart();
+
+            RemoteAnimationTargets targets = new RemoteAnimationTargets(
+                    appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
+            for (RemoteAnimationTargetCompat app : targets.apps) {
+                new Transaction().setAlpha(app.leash.getSurfaceControl(), 1).apply();
+            }
+            AnimatorSet anim = new AnimatorSet();
+            anim.play(controller.getAnimationPlayer());
+            anim.setDuration(HOME_APPEAR_DURATION);
+            result.setAnimation(anim, RecentsActivity.this,
+                    () -> getStateManager().goToState(RecentsState.HOME, false),
+                    true /* skipFirstFrame */);
+        }
+    };
+
     @Override
-    protected StateHandler<RecentsState>[] createStateHandlers() {
-        return new StateHandler[] { new FallbackRecentsStateController(this) };
+    protected void collectStateHandlers(List<StateHandler> out) {
+        out.add(new FallbackRecentsStateController(this));
     }
 
     @Override
@@ -321,7 +420,7 @@
 
     @Override
     public AtomicAnimationFactory<RecentsState> createAtomicAnimationFactory() {
-        return new RecentsAtomicAnimationFactory<>(this, 0);
+        return new RecentsAtomicAnimationFactory<>(this);
     }
 
     private AnimatorListenerAdapter resetStateListener() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 51f5e5d..53b6675 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -18,11 +18,17 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import android.view.IRecentsAnimationController;
+import android.view.SurfaceControl;
+import android.window.PictureInPictureSurfaceTransaction;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.RunnableList;
 import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
@@ -39,6 +45,8 @@
 
     private boolean mUseLauncherSysBarFlags = false;
     private boolean mSplitScreenMinimized = false;
+    private boolean mFinishRequested = false;
+    private RunnableList mPendingFinishCallbacks = new RunnableList();
 
     public RecentsAnimationController(RecentsAnimationControllerCompat controller,
             boolean allowMinimizeSplitScreen,
@@ -89,30 +97,12 @@
     }
 
     /**
-     * Notifies the controller that we want to defer cancel until the next app transition starts.
-     * If {@param screenshot} is set, then we will receive a screenshot on the next
-     * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call
-     * {@link #cleanupScreenshot()} when that screenshot is no longer used.
-     */
-    public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
-        mController.setDeferCancelUntilNextTransition(defer, screenshot);
-    }
-
-    /**
-     * Cleans up the screenshot previously returned from
-     * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}.
-     */
-    public void cleanupScreenshot() {
-        UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
-    }
-
-    /**
      * Remove task remote animation target from
      * {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
      */
     @UiThread
-    public boolean removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
-        return mController.removeTask(target.taskId);
+    public void removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
+        UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId));
     }
 
     @UiThread
@@ -146,16 +136,74 @@
 
     @UiThread
     public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
+        if (mFinishRequested) {
+            // If finishing, add to pending finish callbacks, otherwise, if finished, adding to the
+            // destroyed RunnableList will just trigger the callback to be called immediately
+            mPendingFinishCallbacks.add(callback);
+            return;
+        }
+
+        // Finish not yet requested
+        mFinishRequested = true;
         mOnFinishedListener.accept(this);
+        mPendingFinishCallbacks.add(callback);
         UI_HELPER_EXECUTOR.execute(() -> {
             mController.finish(toRecents, sendUserLeaveHint);
-            if (callback != null) {
-                MAIN_EXECUTOR.execute(callback);
-            }
+            InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+            InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+            MAIN_EXECUTOR.execute(mPendingFinishCallbacks::executeAllAndDestroy);
         });
     }
 
     /**
+     * @see IRecentsAnimationController#cleanupScreenshot()
+     */
+    @UiThread
+    public void cleanupScreenshot() {
+        UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
+    }
+
+    /**
+     * @see RecentsAnimationControllerCompat#detachNavigationBarFromApp
+     */
+    @UiThread
+    public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+        UI_HELPER_EXECUTOR.execute(() -> mController.detachNavigationBarFromApp(moveHomeToTop));
+    }
+
+    /**
+     * @see IRecentsAnimationController#animateNavigationBarToApp(long)
+     */
+    @UiThread
+    public void animateNavigationBarToApp(long duration) {
+        UI_HELPER_EXECUTOR.execute(() -> mController.animateNavigationBarToApp(duration));
+    }
+
+    /**
+     * @see IRecentsAnimationController#setWillFinishToHome(boolean)
+     */
+    @UiThread
+    public void setWillFinishToHome(boolean willFinishToHome) {
+        UI_HELPER_EXECUTOR.execute(() -> mController.setWillFinishToHome(willFinishToHome));
+    }
+
+    /**
+     * Sets the final surface transaction on a Task. This is used by Launcher to notify the system
+     * that animating Activity to PiP has completed and the associated task surface should be
+     * updated accordingly. This should be called before `finish`
+     * @param taskId for which the leash should be updated
+     * @param finishTransaction the transaction to transfer to the task surface control after the
+     *                          leash is removed
+     * @param overlay the surface control for an overlay being shown above the pip (can be null)
+     */
+    public void setFinishTaskTransaction(int taskId,
+            PictureInPictureSurfaceTransaction finishTransaction,
+            SurfaceControl overlay) {
+        UI_HELPER_EXECUTOR.execute(
+                () -> mController.setFinishTaskTransaction(taskId, finishTransaction, overlay));
+    }
+
+    /**
      * Enables the input consumer to start intercepting touches in the app window.
      */
     public void enableInputConsumer() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 7a6bbb4..444d77a 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -15,26 +15,35 @@
  */
 package com.android.quickstep;
 
+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 com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
+import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
+import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
+import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
+import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 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_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -42,24 +51,36 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.graphics.Region;
+import android.net.Uri;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
 import android.view.MotionEvent;
+import android.view.Surface;
 
 import androidx.annotation.BinderThread;
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.launcher3.util.SecureSettingsObserver;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.SettingsCache;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.SysUINavigationMode.OneHandedModeChangeListener;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -71,13 +92,18 @@
  */
 public class RecentsAnimationDeviceState implements
         NavigationModeChangeListener,
-        DefaultDisplay.DisplayInfoChangeListener {
+        DisplayInfoChangeListener,
+        OneHandedModeChangeListener {
+
+    static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
 
     private final Context mContext;
     private final SysUINavigationMode mSysUiNavMode;
-    private final DefaultDisplay mDefaultDisplay;
+    private final DisplayController mDisplayController;
     private final int mDisplayId;
     private final RotationTouchHelper mRotationTouchHelper;
+    private final TaskStackChangeListener mPipListener;
+    private final List<ComponentName> mGestureBlockedActivities;
 
     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
 
@@ -88,6 +114,11 @@
     private final Region mDeferredGestureRegion = new Region();
     private boolean mAssistantAvailable;
     private float mAssistantVisibility;
+    private boolean mIsUserSetupComplete;
+    private boolean mIsOneHandedModeEnabled;
+    private boolean mIsSwipeToNotificationEnabled;
+    private final boolean mIsOneHandedModeSupported;
+    private boolean mPipIsActive;
 
     private boolean mIsUserUnlocked;
     private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
@@ -104,18 +135,28 @@
     private Region mExclusionRegion;
     private SystemGestureExclusionListenerCompat mExclusionListener;
 
-    private final List<ComponentName> mGestureBlockedActivities;
-
-    private boolean mIsUserSetupComplete;
-
     public RecentsAnimationDeviceState(Context context) {
+        this(context, false);
+    }
+
+    /**
+     * @param isInstanceForTouches {@code true} if this is the persistent instance being used for
+     *                                   gesture touch handling
+     */
+    public RecentsAnimationDeviceState(Context context, boolean isInstanceForTouches) {
         mContext = context;
+        mDisplayController = DisplayController.INSTANCE.get(context);
         mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
-        mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
-        mDisplayId = mDefaultDisplay.getInfo().id;
-        runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this));
-        mRotationTouchHelper = new RotationTouchHelper(context);
-        runOnDestroy(mRotationTouchHelper::destroy);
+        mDisplayId = mDisplayController.getInfo().id;
+        mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
+        runOnDestroy(() -> mDisplayController.removeChangeListener(this));
+        mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
+        if (isInstanceForTouches) {
+            // rotationTouchHelper doesn't get initialized after being destroyed, so only destroy
+            // if primary TouchInteractionService instance needs to be destroyed.
+            mRotationTouchHelper.init();
+            runOnDestroy(mRotationTouchHelper::destroy);
+        }
 
         // Register for user unlocked if necessary
         mIsUserUnlocked = context.getSystemService(UserManager.class)
@@ -157,16 +198,54 @@
             }
         }
 
-        SecureSettingsObserver userSetupObserver = new SecureSettingsObserver(
-                context.getContentResolver(),
-                e -> mIsUserSetupComplete = e,
-                Settings.Secure.USER_SETUP_COMPLETE,
-                0);
-        mIsUserSetupComplete = userSetupObserver.getValue();
-        if (!mIsUserSetupComplete) {
-            userSetupObserver.register();
-            runOnDestroy(userSetupObserver::unregister);
+        SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
+        if (mIsOneHandedModeSupported) {
+            Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED);
+            SettingsCache.OnChangeListener onChangeListener =
+                    enabled -> mIsOneHandedModeEnabled = enabled;
+            settingsCache.register(oneHandedUri, onChangeListener);
+            mIsOneHandedModeEnabled = settingsCache.getValue(oneHandedUri);
+            runOnDestroy(() -> settingsCache.unregister(oneHandedUri, onChangeListener));
+        } else {
+            mIsOneHandedModeEnabled = false;
         }
+
+        Uri swipeBottomNotificationUri =
+                Settings.Secure.getUriFor(ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
+        SettingsCache.OnChangeListener onChangeListener =
+                enabled -> mIsSwipeToNotificationEnabled = enabled;
+        settingsCache.register(swipeBottomNotificationUri, onChangeListener);
+        mIsSwipeToNotificationEnabled = settingsCache.getValue(swipeBottomNotificationUri);
+        runOnDestroy(() -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener));
+
+        Uri setupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE);
+        mIsUserSetupComplete = settingsCache.getValue(setupCompleteUri, 0);
+        if (!mIsUserSetupComplete) {
+            SettingsCache.OnChangeListener userSetupChangeListener = e -> mIsUserSetupComplete = e;
+            settingsCache.register(setupCompleteUri, userSetupChangeListener);
+            runOnDestroy(() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
+        }
+
+        try {
+            mPipIsActive = ActivityTaskManager.getService().getRootTaskInfo(
+                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED) != null;
+        } catch (RemoteException e) {
+            // Do nothing
+        }
+        mPipListener = new TaskStackChangeListener() {
+            @Override
+            public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+                mPipIsActive = true;
+            }
+
+            @Override
+            public void onActivityUnpinned() {
+                mPipIsActive = false;
+            }
+        };
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mPipListener);
+        runOnDestroy(() ->
+                TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mPipListener));
     }
 
     private void runOnDestroy(Runnable action) {
@@ -191,11 +270,20 @@
         runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(listener));
     }
 
+    /**
+     * Adds a listener for the one handed mode change,
+     * guaranteed to be called after the device state's mode has changed.
+     */
+    public void addOneHandedModeChangedCallback(OneHandedModeChangeListener listener) {
+        listener.onOneHandedModeChanged(mSysUiNavMode.addOneHandedOverlayChangeListener(listener));
+        runOnDestroy(() -> mSysUiNavMode.removeOneHandedOverlayChangeListener(listener));
+    }
+
     @Override
     public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
-        mDefaultDisplay.removeChangeListener(this);
-        mDefaultDisplay.addChangeListener(this);
-        onDisplayInfoChanged(mDefaultDisplay.getInfo(), CHANGE_ALL);
+        mDisplayController.removeChangeListener(this);
+        mDisplayController.addChangeListener(this);
+        onDisplayInfoChanged(mContext, mDisplayController.getInfo(), CHANGE_ALL);
 
         if (newMode == NO_BUTTON) {
             mExclusionListener.register();
@@ -203,22 +291,20 @@
             mExclusionListener.unregister();
         }
 
-        mNavBarPosition = new NavBarPosition(newMode, mDefaultDisplay.getInfo());
-
+        mNavBarPosition = new NavBarPosition(newMode, mDisplayController.getInfo());
         mMode = newMode;
     }
 
     @Override
-    public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
-        if (info.id != getDisplayId() || flags == CHANGE_FRAME_DELAY) {
-            // ignore displays that aren't running launcher and frame refresh rate changes
-            return;
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        if ((flags & CHANGE_ROTATION) != 0) {
+            mNavBarPosition = new NavBarPosition(mMode, info);
         }
+    }
 
-        if (!mMode.hasGestures) {
-            return;
-        }
-        mNavBarPosition = new NavBarPosition(mMode, info);
+    @Override
+    public void onOneHandedModeChanged(int newGesturalHeight) {
+        mRotationTouchHelper.setGesturalHeight(newGesturalHeight);
     }
 
     /**
@@ -246,7 +332,14 @@
      * @return whether the current nav mode has some gestures (either 2 or 0 button mode).
      */
     public boolean isGesturalNavMode() {
-        return mMode == TWO_BUTTONS || mMode == NO_BUTTON;
+        return mMode.hasGestures;
+    }
+
+    /**
+     * @return whether the current nav mode is 2-button-based.
+     */
+    public boolean isTwoButtonNavMode() {
+        return mMode == TWO_BUTTONS;
     }
 
     /**
@@ -324,7 +417,7 @@
      * @return the system ui state flags.
      */
     // TODO(141886704): See if we can remove this
-    public @SystemUiStateFlags int getSystemUiStateFlags() {
+    public int getSystemUiStateFlags() {
         return mSystemUiStateFlags;
     }
 
@@ -333,10 +426,12 @@
      */
     public boolean canStartSystemGesture() {
         boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+                || (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
                 || mRotationTouchHelper.isTaskListFrozen();
         return canStartWithNavHidden
                 && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
                 && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
+                && (mSystemUiStateFlags & SYSUI_STATE_MAGNIFICATION_OVERLAP) == 0
                 && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
                         || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
     }
@@ -357,6 +452,13 @@
     }
 
     /**
+     * @return whether assistant gesture is constraint
+     */
+    public boolean isAssistantGestureIsConstrained() {
+        return (mSystemUiStateFlags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
+    }
+
+    /**
      * @return whether the bubble stack is expanded
      */
     public boolean isBubblesExpanded() {
@@ -406,6 +508,13 @@
     }
 
     /**
+     * @return whether one-handed mode is enabled and active
+     */
+    public boolean isOneHandedModeActive() {
+        return (mSystemUiStateFlags & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
+    }
+
+    /**
      * Sets the region in screen space where the gestures should be deferred (ie. due to specific
      * nav bar ui).
      */
@@ -467,6 +576,39 @@
                 && !isGestureBlockedActivity(task);
     }
 
+    /**
+     * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode
+     *
+     * @param ev The touch screen motion event.
+     * @return whether the given motion event can trigger the one handed mode.
+     */
+    public boolean canTriggerOneHandedAction(MotionEvent ev) {
+        if (!mIsOneHandedModeSupported) {
+            return false;
+        }
+
+        if (mIsOneHandedModeEnabled) {
+            final Info displayInfo = mDisplayController.getInfo();
+            return (mRotationTouchHelper.touchInOneHandedModeRegion(ev)
+                && displayInfo.rotation != Surface.ROTATION_90
+                && displayInfo.rotation != Surface.ROTATION_270
+                && displayInfo.densityDpi < DisplayMetrics.DENSITY_600);
+        }
+        return false;
+    }
+
+    public boolean isOneHandedModeEnabled() {
+        return mIsOneHandedModeEnabled;
+    }
+
+    public boolean isSwipeToNotificationEnabled() {
+        return mIsSwipeToNotificationEnabled;
+    }
+
+    public boolean isPipActive() {
+        return mPipIsActive;
+    }
+
     public RotationTouchHelper getRotationTouchHelper() {
         return mRotationTouchHelper;
     }
@@ -481,6 +623,10 @@
         pw.println("  assistantDisabled="
                 + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
         pw.println("  isUserUnlocked=" + mIsUserUnlocked);
+        pw.println("  isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
+        pw.println("  isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
+        pw.println("  deferredGestureRegion=" + mDeferredGestureRegion);
+        pw.println("  pipIsActive=" + mPipIsActive);
         mRotationTouchHelper.dump(pw);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 718c5ba..3861bab 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -33,7 +33,7 @@
     public RecentsAnimationTargets(RemoteAnimationTargetCompat[] apps,
             RemoteAnimationTargetCompat[] wallpapers, Rect homeContentInsets,
             Rect minimizedHomeBounds) {
-        super(apps, wallpapers, MODE_CLOSING);
+        super(apps, wallpapers, new RemoteAnimationTargetCompat[0], 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 6f54ba2..1e82c8c 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -18,7 +18,6 @@
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
 
 import android.annotation.TargetApi;
@@ -26,32 +25,41 @@
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.os.Build;
-import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.icons.IconProvider.IconChangeListener;
+import com.android.launcher3.util.Executors.SimpleThreadFactory;
 import com.android.launcher3.util.MainThreadInitializedObject;
 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.KeyguardManagerCompat;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.function.Consumer;
 
 /**
  * Singleton class to load and manage recents model.
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class RecentsModel extends TaskStackChangeListener {
+public class RecentsModel extends TaskStackChangeListener implements IconChangeListener {
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
             new MainThreadInitializedObject<>(RecentsModel::new);
 
+    private static final Executor RECENTS_MODEL_EXECUTOR = Executors.newSingleThreadExecutor(
+            new SimpleThreadFactory("TaskThumbnailIconCache-", THREAD_PRIORITY_BACKGROUND));
+
     private final List<TaskVisualsChangeListener> mThumbnailChangeListeners = new ArrayList<>();
     private final Context mContext;
 
@@ -61,16 +69,15 @@
 
     private RecentsModel(Context context) {
         mContext = context;
-        Looper looper =
-                createAndStartNewLooper("TaskThumbnailIconCache", THREAD_PRIORITY_BACKGROUND);
         mTaskList = new RecentTasksList(MAIN_EXECUTOR,
                 new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
-        mIconCache = new TaskIconCache(context, looper);
-        mThumbnailCache = new TaskThumbnailCache(context, looper);
 
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
-        IconProvider.registerIconChangeListener(context,
-                this::onPackageIconChanged, MAIN_EXECUTOR.getHandler());
+        IconProvider iconProvider = new IconProvider(context);
+        mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
+        mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
+
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
+        iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
     }
 
     public TaskIconCache getIconCache() {
@@ -100,20 +107,27 @@
     }
 
     /**
-     * Finds and returns the task key associated with the given task id.
-     *
-     * @param callback The callback to receive the task key if it is found or null. This is always
-     *                 called on the UI thread.
+     * @return Whether the task list is currently updating in the background
      */
-    public void findTaskWithId(int taskId, Consumer<Task.TaskKey> callback) {
+    @VisibleForTesting
+    public boolean isLoadingTasksInBackground() {
+        return mTaskList.isLoadingTasksInBackground();
+    }
+
+    /**
+     * Checks if a task has been removed or not.
+     *
+     * @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) {
-                    callback.accept(task.key);
+                    callback.accept(false);
                     return;
                 }
             }
-            callback.accept(null);
+            callback.accept(true);
         });
     }
 
@@ -160,9 +174,9 @@
 
     @Override
     public void onTaskRemoved(int taskId) {
-        Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
-        mThumbnailCache.remove(dummyKey);
-        mIconCache.onTaskRemoved(dummyKey);
+        Task.TaskKey stubKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
+        mThumbnailCache.remove(stubKey);
+        mIconCache.onTaskRemoved(stubKey);
     }
 
     public void onTrimMemory(int level) {
@@ -172,17 +186,23 @@
         if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
             // Clear everything once we reach a low-mem situation
             mThumbnailCache.clear();
-            mIconCache.clear();
+            mIconCache.clearCache();
         }
     }
 
-    private void onPackageIconChanged(String pkg, UserHandle user) {
-        mIconCache.invalidateCacheEntries(pkg, user);
+    @Override
+    public void onAppIconChanged(String packageName, UserHandle user) {
+        mIconCache.invalidateCacheEntries(packageName, user);
         for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
-            mThumbnailChangeListeners.get(i).onTaskIconChanged(pkg, user);
+            mThumbnailChangeListeners.get(i).onTaskIconChanged(packageName, user);
         }
     }
 
+    @Override
+    public void onSystemIconStateChanged(String iconState) {
+        mIconCache.clearCache();
+    }
+
     /**
      * Adds a listener for visuals changes
      */
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index ab5e3ba..c032889 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -15,6 +15,10 @@
  */
 package com.android.quickstep;
 
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
+
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.ArrayList;
@@ -30,13 +34,15 @@
     public final RemoteAnimationTargetCompat[] unfilteredApps;
     public final RemoteAnimationTargetCompat[] apps;
     public final RemoteAnimationTargetCompat[] wallpapers;
+    public final RemoteAnimationTargetCompat[] nonApps;
     public final int targetMode;
     public final boolean hasRecents;
 
     private boolean mReleased = false;
 
     public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpapers, int targetMode) {
+            RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
+            int targetMode) {
         ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
         boolean hasRecents = false;
         if (apps != null) {
@@ -55,6 +61,7 @@
         this.wallpapers = wallpapers;
         this.targetMode = targetMode;
         this.hasRecents = hasRecents;
+        this.nonApps = nonApps;
     }
 
     public RemoteAnimationTargetCompat findTask(int taskId) {
@@ -66,6 +73,29 @@
         return null;
     }
 
+    /**
+     * Gets the navigation bar remote animation target if exists.
+     */
+    public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
+        for (RemoteAnimationTargetCompat target : nonApps) {
+            if (target.windowType == TYPE_NAVIGATION_BAR) {
+                return target;
+            }
+        }
+        return null;
+    }
+
+    /** Returns the first opening app target. */
+    public RemoteAnimationTargetCompat getFirstAppTarget() {
+        return apps.length > 0 ? apps[0] : null;
+    }
+
+    /** Returns the task id of the first opening app target, or -1 if none is found. */
+    public int getFirstAppTargetTaskId() {
+        RemoteAnimationTargetCompat target = getFirstAppTarget();
+        return target == null ? -1 : target.taskId;
+    }
+
     public boolean isAnimatingHome() {
         for (RemoteAnimationTargetCompat target : unfilteredApps) {
             if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
@@ -80,6 +110,10 @@
     }
 
     public void release() {
+        if (ENABLE_SHELL_TRANSITIONS) {
+            mReleaseChecks.clear();
+            return;
+        }
         if (mReleased) {
             return;
         }
@@ -98,6 +132,9 @@
         for (RemoteAnimationTargetCompat target : wallpapers) {
             target.release();
         }
+        for (RemoteAnimationTargetCompat target : nonApps) {
+            target.release();
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 5f3c022..678b176 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -17,35 +17,43 @@
 
 import static android.view.Surface.ROTATION_0;
 
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
+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 static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
 
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.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;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
 public class RotationTouchHelper implements
         SysUINavigationMode.NavigationModeChangeListener,
-        DefaultDisplay.DisplayInfoChangeListener {
+        DisplayInfoChangeListener {
 
-    private final OrientationTouchTransformer mOrientationTouchTransformer;
-    private final DefaultDisplay mDefaultDisplay;
-    private final SysUINavigationMode mSysUiNavMode;
-    private final int mDisplayId;
+    public static final MainThreadInitializedObject<RotationTouchHelper> INSTANCE =
+            new MainThreadInitializedObject<>(RotationTouchHelper::new);
+
+    private OrientationTouchTransformer mOrientationTouchTransformer;
+    private DisplayController mDisplayController;
+    private SysUINavigationMode mSysUiNavMode;
+    private int mDisplayId;
     private int mDisplayRotation;
 
     private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
@@ -116,25 +124,39 @@
      */
     private boolean mInOverview;
     private boolean mTaskListFrozen;
-
-
     private final Context mContext;
 
-    public RotationTouchHelper(Context context) {
+    /**
+     * Keeps track of whether destroy has been called for this instance. Mainly used for TAPL tests
+     * where multiple instances of RotationTouchHelper are being created. b/177316094
+     */
+    private boolean mNeedsInit = true;
+
+    private RotationTouchHelper(Context context) {
         mContext = context;
+        if (mNeedsInit) {
+            init();
+        }
+    }
+
+    public void init() {
+        if (!mNeedsInit) {
+            return;
+        }
+        mDisplayController = DisplayController.INSTANCE.get(mContext);
         Resources resources = mContext.getResources();
-        mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
-        mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
-        mDisplayId = mDefaultDisplay.getInfo().id;
+        mSysUiNavMode = SysUINavigationMode.INSTANCE.get(mContext);
+        mDisplayId = mDisplayController.getInfo().id;
 
         mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
                 () -> QuickStepContract.getWindowCornerRadius(resources));
 
         // Register for navigation mode changes
-        onNavigationModeChanged(mSysUiNavMode.addModeChangeListener(this));
+        SysUINavigationMode.Mode newMode = mSysUiNavMode.addModeChangeListener(this);
+        onNavModeChangedInternal(newMode, newMode.hasGestures);
         runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
 
-        mOrientationListener = new OrientationEventListener(context) {
+        mOrientationListener = new OrientationEventListener(mContext) {
             @Override
             public void onOrientationChanged(int degrees) {
                 int newRotation = RecentsOrientedState.getRotationForUserDegreesRotated(degrees,
@@ -153,17 +175,18 @@
                 }
             }
         };
+        mNeedsInit = false;
     }
 
     private void setupOrientationSwipeHandler() {
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
-        mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mFrozenTaskListener);
+        mOnDestroyFrozenTaskRunnable = () -> TaskStackChangeListeners.getInstance()
                 .unregisterTaskStackListener(mFrozenTaskListener);
         runOnDestroy(mOnDestroyFrozenTaskRunnable);
     }
 
     private void destroyOrientationSwipeHandlerCallback() {
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
         mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
     }
 
@@ -178,6 +201,7 @@
         for (Runnable r : mOnDestroyActions) {
             r.run();
         }
+        mNeedsInit = true;
     }
 
     public boolean isTaskListFrozen() {
@@ -188,6 +212,10 @@
         return mOrientationTouchTransformer.touchInAssistantRegion(ev);
     }
 
+    public boolean touchInOneHandedModeRegion(MotionEvent ev) {
+        return mOrientationTouchTransformer.touchInOneHandedModeRegion(ev);
+    }
+
     /**
      * Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
      */
@@ -196,7 +224,7 @@
             return;
         }
 
-        mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
+        mOrientationTouchTransformer.createOrAddTouchRegion(mDisplayController.getInfo());
     }
 
     /**
@@ -218,12 +246,22 @@
 
     @Override
     public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
-        mDefaultDisplay.removeChangeListener(this);
-        mDefaultDisplay.addChangeListener(this);
-        onDisplayInfoChanged(mDefaultDisplay.getInfo(), CHANGE_ALL);
+        onNavModeChangedInternal(newMode, false);
+    }
 
-        mOrientationTouchTransformer.setNavigationMode(newMode, mDefaultDisplay.getInfo());
-        if (!mMode.hasGestures && newMode.hasGestures) {
+    /**
+     * @param forceRegister if {@code true}, this will register {@link #mFrozenTaskListener} via
+     *                      {@link #setupOrientationSwipeHandler()}
+     */
+    private void onNavModeChangedInternal(SysUINavigationMode.Mode newMode, boolean forceRegister) {
+        mDisplayController.removeChangeListener(this);
+        mDisplayController.addChangeListener(this);
+        onDisplayInfoChanged(mContext, mDisplayController.getInfo(), CHANGE_ALL);
+
+        mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
+                mContext.getResources());
+
+        if (forceRegister || (!mMode.hasGestures && newMode.hasGestures)) {
             setupOrientationSwipeHandler();
         } else if (mMode.hasGestures && !newMode.hasGestures){
             destroyOrientationSwipeHandlerCallback();
@@ -237,9 +275,8 @@
     }
 
     @Override
-    public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
-        if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) {
-            // ignore displays that aren't running launcher and frame refresh rate changes
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN)) == 0) {
             return;
         }
 
@@ -267,6 +304,14 @@
     }
 
     /**
+     * Sets the gestural height.
+     */
+    void setGesturalHeight(int newGesturalHeight) {
+        mOrientationTouchTransformer.setGesturalHeight(
+                newGesturalHeight, mDisplayController.getInfo(), mContext.getResources());
+    }
+
+    /**
      * *May* apply a transform on the motion event if it lies in the nav bar region for another
      * orientation that is currently being tracked as a part of quickstep
      */
@@ -279,7 +324,7 @@
     }
 
     private void enableMultipleRegions(boolean enable) {
-        mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
+        mOrientationTouchTransformer.enableMultipleRegions(enable, mDisplayController.getInfo());
         notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
         if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
             // Clear any previous state from sensor manager
@@ -334,7 +379,7 @@
 
     private void notifySysuiOfCurrentRotation(int rotation) {
         UI_HELPER_EXECUTOR.execute(() -> SystemUiProxy.INSTANCE.get(mContext)
-                .onQuickSwitchToNewTask(rotation));
+                .notifyPrioritizedRotation(rotation));
     }
 
     /**
@@ -342,7 +387,7 @@
      * notifies system UI of the primary rotation the user is interacting with
      */
     private void toggleSecondaryNavBarsForRotation() {
-        mOrientationTouchTransformer.setSingleActiveRegion(mDefaultDisplay.getInfo());
+        mOrientationTouchTransformer.setSingleActiveRegion(mDisplayController.getInfo());
         notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
     }
 
@@ -360,4 +405,8 @@
         pw.println("  displayRotation=" + getDisplayRotation());
         mOrientationTouchTransformer.dump(pw);
     }
+
+    public OrientationTouchTransformer getOrientationTouchTransformer() {
+        return mOrientationTouchTransformer;
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
similarity index 75%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
rename to quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 2963b6c..4495455 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.PROTOTYPE_APP_CLOSE;
 
 import android.animation.Animator;
 import android.content.Context;
@@ -26,6 +27,7 @@
 import android.graphics.RectF;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.DeviceProfile;
@@ -35,7 +37,9 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
+import com.android.quickstep.util.AppCloseConfig;
 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;
@@ -76,7 +80,7 @@
         mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
         mTransformParams = transformParams;
 
-        mTaskViewSimulator.setLayoutRotation(
+        mTaskViewSimulator.getOrientationState().update(
                 mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
                 mDeviceState.getRotationTouchHelper().getDisplayRotation());
     }
@@ -88,13 +92,7 @@
         mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
                 dp, mContext, TEMP_RECT,
                 mTaskViewSimulator.getOrientationState().getOrientationHandler());
-
-        if (mDeviceState.isFullyGesturalNavMode()) {
-            // We can drag all the way to the top of the screen.
-            mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
-        } else {
-            mDragLengthFactor = 1 + AnimatorControllerWithResistance.TWO_BUTTON_EXTRA_DRAG_FACTOR;
-        }
+        mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
 
         PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
         mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR);
@@ -131,6 +129,7 @@
     }
 
     protected abstract class HomeAnimationFactory {
+        protected float mSwipeVelocity;
 
         public @NonNull RectF getWindowTargetRect() {
             PagedOrientationHandler orientationHandler = getOrientationHandler();
@@ -147,17 +146,77 @@
                     targetX + halfIconSize, targetY + halfIconSize);
         }
 
+        /** Returns the corner radius of the window at the end of the animation. */
+        public float getEndRadius(RectF cropRectF) {
+            return cropRectF.width() / 2f;
+        }
+
         public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();
 
+        public void setSwipeVelocity(float velocity) {
+            mSwipeVelocity = velocity;
+        }
+
         public void playAtomicAnimation(float velocity) {
             // No-op
         }
 
+        public boolean shouldPlayAtomicWorkspaceReveal() {
+            return true;
+        }
+
         public void setAnimation(RectFSpringAnim anim) { }
 
-        public void update(RectF currentRect, float progress, float radius) { }
+        public boolean keepWindowOpaque() { return false; }
+
+        public void update(@Nullable AppCloseConfig config, RectF currentRect, float progress,
+                float radius) { }
 
         public void onCancel() { }
+
+        /**
+         * @return {@code true} if this factory supports animating an Activity to PiP window on
+         * swiping up to home.
+         */
+        public boolean supportSwipePipToHome() {
+            return false;
+        }
+
+        /**
+         * @param progress The progress of the animation to the home screen.
+         * @return The current alpha to set on the animating app window.
+         */
+        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);
+        }
+    }
+
+    /**
+     * Update with start progress for window animation to home.
+     * @param outMatrix {@link Matrix} to map a rect in Launcher space to window space.
+     * @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) {
+        mCurrentShift.updateValue(startProgress);
+        mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
+        RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+
+        mTaskViewSimulator.applyWindowToHomeRotation(outMatrix);
+
+        final RectF startRect = new RectF(cropRectF);
+        mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
+        return startRect;
     }
 
     /**
@@ -169,22 +228,23 @@
             HomeAnimationFactory homeAnimationFactory) {
         final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
 
-        mCurrentShift.updateValue(startProgress);
-        mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
+        Matrix homeToWindowPositionMap = new Matrix();
+        final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
         RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
 
-        // Matrix to map a rect in Launcher space to window space
-        Matrix homeToWindowPositionMap = new Matrix();
-        mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);
-
-        final RectF startRect = new RectF(cropRectF);
-        mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
         // Move the startRect to Launcher space as floatingIconView runs in Launcher
         Matrix windowToHomePositionMap = new Matrix();
         homeToWindowPositionMap.invert(windowToHomePositionMap);
         windowToHomePositionMap.mapRect(startRect);
 
-        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
+        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);
+        }
         homeAnimationFactory.setAnimation(anim);
 
         SpringAnimationRunner runner = new SpringAnimationRunner(
@@ -194,24 +254,6 @@
         return anim;
     }
 
-    /**
-     * @param progress The progress of the animation to the home screen.
-     * @return The current alpha to set on the animating app window.
-     */
-    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);
-    }
-
     protected class SpringAnimationRunner extends AnimationSuccessListener
             implements RectFSpringAnim.OnUpdateListener, BuilderProxy {
 
@@ -240,22 +282,30 @@
             // 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();
-            mEndRadius = cropRectF.width() / 2f;
+            mEndRadius = factory.getEndRadius(cropRectF);
         }
 
         @Override
-        public void onUpdate(RectF currentRect, float progress) {
+        public void onUpdate(@Nullable AppCloseConfig config, 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
-                    .setTargetAlpha(getWindowAlpha(progress))
+                    .setTargetAlpha(alpha)
                     .setCornerRadius(cornerRadius);
-
             mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
-            mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
+            mAnimationFactory.update(config, currentRect, progress,
+                    mMatrix.mapRadius(cornerRadius));
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index f2675f2..74f4bea 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -16,6 +16,7 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
@@ -24,16 +25,16 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.util.Log;
 
+import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.MainThreadInitializedObject;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * Observer for the resource config that specifies the navigation bar mode.
@@ -64,15 +65,21 @@
             new MainThreadInitializedObject<>(SysUINavigationMode::new);
 
     private static final String TAG = "SysUINavigationMode";
-
     private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
     private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
             "config_navBarInteractionMode";
+    private static final String TARGET_OVERLAY_PACKAGE = "android";
 
     private final Context mContext;
     private Mode mMode;
 
-    private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
+    private int mNavBarGesturalHeight;
+    private int mNavBarLargerGesturalHeight;
+
+    private final List<NavigationModeChangeListener> mChangeListeners =
+            new CopyOnWriteArrayList<>();
+    private final List<OneHandedModeChangeListener> mOneHandedOverlayChangeListeners =
+            new ArrayList<>();
 
     public SysUINavigationMode(Context context) {
         mContext = context;
@@ -82,8 +89,9 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 updateMode();
+                updateGesturalHeight();
             }
-        }, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
+        }, getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED));
     }
 
     /** Updates navigation mode when needed. */
@@ -95,9 +103,49 @@
         }
     }
 
+    private void updateGesturalHeight() {
+        int newGesturalHeight = ResourceUtils.getDimenByName(
+                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mContext.getResources(),
+                INVALID_RESOURCE_HANDLE);
+
+        if (newGesturalHeight == INVALID_RESOURCE_HANDLE) {
+            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+            return;
+        }
+
+        if (mNavBarGesturalHeight != newGesturalHeight) {
+            mNavBarGesturalHeight = newGesturalHeight;
+        }
+
+        int newLargerGesturalHeight = ResourceUtils.getDimenByName(
+                ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, mContext.getResources(),
+                INVALID_RESOURCE_HANDLE);
+        if (newLargerGesturalHeight == INVALID_RESOURCE_HANDLE) {
+            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+            return;
+        }
+        if (mNavBarLargerGesturalHeight != newLargerGesturalHeight) {
+            mNavBarLargerGesturalHeight = newLargerGesturalHeight;
+            dispatchOneHandedOverlayChange();
+        }
+    }
+
     private void initializeMode() {
-        int modeInt = getSystemIntegerRes(mContext, NAV_BAR_INTERACTION_MODE_RES_NAME);
-        for(Mode m : Mode.values()) {
+        int modeInt = ResourceUtils.getIntegerByName(NAV_BAR_INTERACTION_MODE_RES_NAME,
+                mContext.getResources(), INVALID_RESOURCE_HANDLE);
+        mNavBarGesturalHeight = ResourceUtils.getDimenByName(
+                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mContext.getResources(),
+                INVALID_RESOURCE_HANDLE);
+        mNavBarLargerGesturalHeight = ResourceUtils.getDimenByName(
+                ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, mContext.getResources(),
+                mNavBarGesturalHeight);
+
+        if (modeInt == INVALID_RESOURCE_HANDLE) {
+            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+            return;
+        }
+
+        for (Mode m : Mode.values()) {
             if (m.resValue == modeInt) {
                 mMode = m;
             }
@@ -110,6 +158,12 @@
         }
     }
 
+    private void dispatchOneHandedOverlayChange() {
+        for (OneHandedModeChangeListener listener : mOneHandedOverlayChangeListeners) {
+            listener.onOneHandedModeChanged(mNavBarLargerGesturalHeight);
+        }
+    }
+
     public Mode addModeChangeListener(NavigationModeChangeListener listener) {
         mChangeListeners.add(listener);
         return mMode;
@@ -119,40 +173,30 @@
         mChangeListeners.remove(listener);
     }
 
+    public int addOneHandedOverlayChangeListener(OneHandedModeChangeListener listener) {
+        mOneHandedOverlayChangeListeners.add(listener);
+        return mNavBarLargerGesturalHeight;
+    }
+
+    public void removeOneHandedOverlayChangeListener(OneHandedModeChangeListener listener) {
+        mOneHandedOverlayChangeListeners.remove(listener);
+    }
+
     public Mode getMode() {
         return mMode;
     }
 
-    private static int getSystemIntegerRes(Context context, String resName) {
-        Resources res = context.getResources();
-        int resId = res.getIdentifier(resName, "integer", "android");
-
-        if (resId != 0) {
-            return res.getInteger(resId);
-        } else {
-            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
-            return -1;
-        }
-    }
-
-    /** @return Whether we can remove the shelf from overview. */
-    public static boolean removeShelfFromOverview(Context context) {
-        // The shelf is core to the two-button mode model, so we need to continue supporting it.
-        return getMode(context) != Mode.TWO_BUTTONS;
-    }
-
-    public static boolean hideShelfInTwoButtonLandscape(Context context,
-            PagedOrientationHandler pagedOrientationHandler) {
-        return  getMode(context) == Mode.TWO_BUTTONS &&
-                !pagedOrientationHandler.isLayoutNaturalToLauncher();
-    }
-
     public void dump(PrintWriter pw) {
         pw.println("SysUINavigationMode:");
         pw.println("  mode=" + mMode.name());
+        pw.println("  mNavBarGesturalHeight=:" + mNavBarGesturalHeight);
     }
 
     public interface NavigationModeChangeListener {
         void onNavigationModeChanged(Mode newMode);
     }
-}
\ No newline at end of file
+
+    public interface OneHandedModeChangeListener {
+        void onOneHandedModeChanged(int newGesturalHeight);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 299e9e5..d040904 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,7 +17,12 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -25,39 +30,95 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.RemoteTransitionCompat;
+import com.android.systemui.shared.system.smartspace.ISmartspaceCallback;
+import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
+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.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;
 
 /**
  * Holds the reference to SystemUI.
  */
-public class SystemUiProxy implements ISystemUiProxy {
+public class SystemUiProxy implements ISystemUiProxy,
+        SysUINavigationMode.NavigationModeChangeListener {
     private static final String TAG = SystemUiProxy.class.getSimpleName();
 
     public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
             new MainThreadInitializedObject<>(SystemUiProxy::new);
 
     private ISystemUiProxy mSystemUiProxy;
+    private IPip mPip;
+    private ISmartspaceTransitionController mSmartspaceTransitionController;
+    private ISplitScreen mSplitScreen;
+    private IOneHanded mOneHanded;
+    private IShellTransitions mShellTransitions;
+    private IStartingWindow mStartingWindow;
     private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
-        MAIN_EXECUTOR.execute(() -> setProxy(null));
+        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;
+
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
     private boolean mLastShelfVisible;
-    private float mLastBackButtonAlpha;
-    private boolean mLastBackButtonAnimate;
+    private float mLastNavButtonAlpha;
+    private boolean mLastNavButtonAnimate;
+    private boolean mHasNavButtonAlphaBeenSet = false;
 
     // TODO(141886704): Find a way to remove this
     private int mLastSystemUiStateFlags;
 
     public SystemUiProxy(Context context) {
-        // Do nothing
+        SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
+    }
+
+    @Override
+    public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
+        // Whenever the nav mode changes, force reset the nav button alpha
+        setNavBarButtonAlpha(1f, false);
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.onBackPressed();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call onBackPressed", e);
+            }
+        }
+    }
+
+    @Override
+    public void setHomeRotationEnabled(boolean enabled) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.setHomeRotationEnabled(enabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call onBackPressed", e);
+            }
+        }
     }
 
     @Override
@@ -66,10 +127,40 @@
         return null;
     }
 
-    public void setProxy(ISystemUiProxy proxy) {
+    public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
+            IOneHanded oneHanded, IShellTransitions shellTransitions,
+            IStartingWindow startingWindow,
+            ISmartspaceTransitionController smartSpaceTransitionController) {
         unlinkToDeath();
         mSystemUiProxy = proxy;
+        mPip = pip;
+        mSplitScreen = splitScreen;
+        mOneHanded = oneHanded;
+        mShellTransitions = shellTransitions;
+        mStartingWindow = startingWindow;
+        mSmartspaceTransitionController = smartSpaceTransitionController;
         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 (mPendingSplitScreenListener != null && mSplitScreen != null) {
+            registerSplitScreenListener(mPendingSplitScreenListener);
+            mPendingSplitScreenListener = null;
+        }
+        if (mPendingStartingWindowListener != null && mStartingWindow != null) {
+            setStartingWindowListener(mPendingStartingWindowListener);
+            mPendingStartingWindowListener = null;
+        }
+        if (mPendingSmartspaceCallback != null && mSmartspaceTransitionController != null) {
+            setSmartspaceCallback(mPendingSmartspaceCallback);
+            mPendingSmartspaceCallback = null;
+        }
+    }
+
+    public void clearProxy() {
+        setProxy(null, null, null, null, null, null, null);
     }
 
     // TODO(141886704): Find a way to remove this
@@ -114,17 +205,6 @@
     }
 
     @Override
-    public void onSplitScreenInvoked() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onSplitScreenInvoked();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onSplitScreenInvoked", e);
-            }
-        }
-    }
-
-    @Override
     public void onOverviewShown(boolean fromHome) {
         onOverviewShown(fromHome, TAG);
     }
@@ -151,28 +231,19 @@
         return null;
     }
 
-    @Override
-    public void setBackButtonAlpha(float alpha, boolean animate) {
-        boolean changed = Float.compare(alpha, mLastBackButtonAlpha) != 0
-                || animate != mLastBackButtonAnimate;
-        if (mSystemUiProxy != null && changed) {
-            mLastBackButtonAlpha = alpha;
-            mLastBackButtonAnimate = animate;
-            try {
-                mSystemUiProxy.setBackButtonAlpha(alpha, animate);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setBackButtonAlpha", e);
-            }
-        }
-    }
-
-    public float getLastBackButtonAlpha() {
-        return mLastBackButtonAlpha;
+    public float getLastNavButtonAlpha() {
+        return mLastNavButtonAlpha;
     }
 
     @Override
     public void setNavBarButtonAlpha(float alpha, boolean animate) {
-        if (mSystemUiProxy != null) {
+        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) {
@@ -226,18 +297,6 @@
     }
 
     @Override
-    public Bundle monitorGestureInput(String name, int displayId) {
-        if (mSystemUiProxy != null) {
-            try {
-                return mSystemUiProxy.monitorGestureInput(name, displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call monitorGestureInput: " + name, e);
-            }
-        }
-        return null;
-    }
-
-    @Override
     public void notifyAccessibilityButtonClicked(int displayId) {
         if (mSystemUiProxy != null) {
             try {
@@ -271,21 +330,6 @@
     }
 
     @Override
-    public void setShelfHeight(boolean visible, int shelfHeight) {
-        boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
-        if (mSystemUiProxy != null && changed) {
-            mLastShelfVisible = visible;
-            mLastShelfHeight = shelfHeight;
-            try {
-                mSystemUiProxy.setShelfHeight(visible, shelfHeight);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setShelfHeight visible: " + visible
-                        + " height: " + shelfHeight, e);
-            }
-        }
-    }
-
-    @Override
     public void handleImageAsScreenshot(Bitmap bitmap, Rect rect, Insets insets, int i) {
         if (mSystemUiProxy != null) {
             try {
@@ -307,6 +351,17 @@
         }
     }
 
+    @Override
+    public void notifySwipeUpGestureStarted() {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.notifySwipeUpGestureStarted();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call notifySwipeUpGestureStarted", e);
+            }
+        }
+    }
+
     /**
      * Notifies that swipe-to-home action is finished.
      */
@@ -316,32 +371,18 @@
             try {
                 mSystemUiProxy.notifySwipeToHomeFinished();
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setPinnedStackAnimationType", e);
-            }
-        }
-    }
-
-    /**
-     * Sets listener to get pinned stack animation callbacks.
-     */
-    @Override
-    public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.setPinnedStackAnimationListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
+                Log.w(TAG, "Failed call notifySwipeToHomeFinished", e);
             }
         }
     }
 
     @Override
-    public void onQuickSwitchToNewTask(int rotation) {
+    public void notifyPrioritizedRotation(int rotation) {
         if (mSystemUiProxy != null) {
             try {
-                mSystemUiProxy.onQuickSwitchToNewTask(rotation);
+                mSystemUiProxy.notifyPrioritizedRotation(rotation);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onQuickSwitchToNewTask with arg: " + rotation, e);
+                Log.w(TAG, "Failed call notifyPrioritizedRotation with arg: " + rotation, e);
             }
         }
     }
@@ -352,10 +393,280 @@
         if (mSystemUiProxy != null) {
             try {
                 mSystemUiProxy.handleImageBundleAsScreenshot(screenImageBundle, locationInScreen,
-                        visibleInsets, task);
+                    visibleInsets, task);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call handleImageBundleAsScreenshot");
             }
         }
     }
+
+    @Override
+    public void expandNotificationPanel() {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.expandNotificationPanel();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call expandNotificationPanel", e);
+            }
+        }
+    }
+
+    //
+    // Pip
+    //
+
+    /**
+     * Sets the shelf height.
+     */
+    public void setShelfHeight(boolean visible, int shelfHeight) {
+        boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
+        if (mPip != null && changed) {
+            mLastShelfVisible = visible;
+            mLastShelfHeight = shelfHeight;
+            try {
+                mPip.setShelfHeight(visible, shelfHeight);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call setShelfHeight visible: " + visible
+                        + " height: " + shelfHeight, e);
+            }
+        }
+    }
+
+    /**
+     * Sets listener to get pinned stack animation callbacks.
+     */
+    public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
+        if (mPip != null) {
+            try {
+                mPip.setPinnedStackAnimationListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
+            }
+        } else {
+            mPendingPipAnimationListener = listener;
+        }
+    }
+
+    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+            PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) {
+        if (mPip != null) {
+            try {
+                return mPip.startSwipePipToHome(componentName, activityInfo,
+                        pictureInPictureParams, launcherRotation, shelfHeight);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startSwipePipToHome", e);
+            }
+        }
+        return null;
+    }
+
+    public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
+            SurfaceControl overlay) {
+        if (mPip != null) {
+            try {
+                mPip.stopSwipePipToHome(componentName, destinationBounds, overlay);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call stopSwipePipToHome");
+            }
+        }
+    }
+
+    //
+    // Splitscreen
+    //
+
+    public void registerSplitScreenListener(ISplitScreenListener listener) {
+        if (mSplitScreen != null) {
+            try {
+                mSplitScreen.registerSplitScreenListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call registerSplitScreenListener");
+            }
+        } else {
+            mPendingSplitScreenListener = listener;
+        }
+    }
+
+    public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+        if (mSplitScreen != null) {
+            try {
+                mSplitScreen.unregisterSplitScreenListener(listener);
+            } catch (RemoteException e) {
+                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");
+            }
+        }
+    }
+
+    /** Start multiple tasks in split-screen simultaneously. */
+    public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
+            @SplitConfigurationOptions.StagePosition int sidePosition,
+            RemoteTransitionCompat remoteTransition) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSplitScreen.startTasks(mainTaskId, mainOptions, sideTaskId, sideOptions,
+                        sidePosition, remoteTransition.getTransition());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startTask");
+            }
+        }
+    }
+
+    public void startShortcut(String packageName, String shortcutId, int stage, int position,
+            Bundle options, UserHandle user) {
+        if (mSplitScreen != null) {
+            try {
+                mSplitScreen.startShortcut(packageName, shortcutId, stage, position, options,
+                        user);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startShortcut");
+            }
+        }
+    }
+
+    public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
+            Bundle options) {
+        if (mSplitScreen != null) {
+            try {
+                mSplitScreen.startIntent(intent, fillInIntent, stage, position, options);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startIntent");
+            }
+        }
+    }
+
+    public void removeFromSideStage(int taskId) {
+        if (mSplitScreen != null) {
+            try {
+                mSplitScreen.removeFromSideStage(taskId);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call removeFromSideStage");
+            }
+        }
+    }
+
+    //
+    // One handed
+    //
+
+    public void startOneHandedMode() {
+        if (mOneHanded != null) {
+            try {
+                mOneHanded.startOneHanded();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startOneHandedMode", e);
+            }
+        }
+    }
+
+    public void stopOneHandedMode() {
+        if (mOneHanded != null) {
+            try {
+                mOneHanded.stopOneHanded();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call stopOneHandedMode", e);
+            }
+        }
+    }
+
+    //
+    // Remote transitions
+    //
+
+    public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
+        if (mShellTransitions != null) {
+            try {
+                mShellTransitions.registerRemote(remoteTransition.getFilter(),
+                        remoteTransition.getTransition());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call registerRemoteTransition");
+            }
+        }
+    }
+
+    public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
+        if (mShellTransitions != null) {
+            try {
+                mShellTransitions.unregisterRemote(remoteTransition.getTransition());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call registerRemoteTransition");
+            }
+        }
+    }
+
+    //
+    // Starting window
+    //
+
+    /**
+     * Sets listener to get callbacks when launching a task.
+     */
+    public void setStartingWindowListener(IStartingWindowListener listener) {
+        if (mStartingWindow != null) {
+            try {
+                mStartingWindow.setStartingWindowListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call setStartingWindowListener", e);
+            }
+        } else {
+            mPendingStartingWindowListener = listener;
+        }
+    }
+
+
+    //
+    // SmartSpace transitions
+    //
+
+    public void setSmartspaceCallback(ISmartspaceCallback callback) {
+        if (mSmartspaceTransitionController != null) {
+            try {
+                mSmartspaceTransitionController.setSmartspace(callback);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call setStartingWindowListener", e);
+            }
+        } else {
+            mPendingSmartspaceCallback = callback;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index cad51f4..67bd85f 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -15,23 +15,35 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 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 android.app.ActivityManager;
+import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemProperties;
 import android.util.Log;
 
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.quickstep.views.RecentsView;
 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.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.RemoteTransitionCompat;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
+    public static final boolean ENABLE_SHELL_TRANSITIONS =
+            SystemProperties.getBoolean("persist.debug.shell_transit", false);
 
     private RecentsAnimationController mController;
     private RecentsAnimationCallbacks mCallbacks;
@@ -39,14 +51,41 @@
     // Temporary until we can hook into gesture state events
     private GestureState mLastGestureState;
     private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+    private Runnable mLiveTileCleanUpHandler;
+    private Context mCtx;
 
+    private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
+        @Override
+        public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+                boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+            if (mLastGestureState == null) {
+                TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+                        mLiveTileRestartListener);
+                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.launchSideTaskInLiveTileModeForRestartedApp(task.taskId);
+                    TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+                            mLiveTileRestartListener);
+                }
+            }
+        }
+    };
+
+    TaskAnimationManager(Context ctx) {
+        mCtx = ctx;
+    }
     /**
      * Preloads the recents animation.
      */
     public void preloadRecentsAnimation(Intent intent) {
         // Pass null animation handler to indicate this start is for preloading
         UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
-                .startRecentsActivity(intent, null, null, null, null));
+                .startRecentsActivity(intent, 0, null, null, null));
     }
 
     /**
@@ -88,22 +127,30 @@
 
             @Override
             public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
-                if (thumbnailData != null) {
-                    // If a screenshot is provided, switch to the screenshot before cleaning up
-                    activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
-                            () -> cleanUpRecentsAnimation(thumbnailData));
-                } else {
-                    cleanUpRecentsAnimation(null /* canceledThumbnail */);
-                }
+                cleanUpRecentsAnimation();
             }
 
             @Override
             public void onRecentsAnimationFinished(RecentsAnimationController controller) {
-                cleanUpRecentsAnimation(null /* canceledThumbnail */);
+                cleanUpRecentsAnimation();
             }
 
             @Override
             public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+                BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
+                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,
+                                new RemoteAnimationTargetCompat[0] /* wallpaper */,
+                                new RemoteAnimationTargetCompat[0] /* nonApps */);
+                        return;
+                    }
+                }
                 if (mController != null) {
                     if (mLastAppearedTaskTarget == null
                             || appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
@@ -116,10 +163,20 @@
                 }
             }
         });
+        final long eventTime = gestureState.getSwipeUpStartTimeMs();
         mCallbacks.addListener(gestureState);
         mCallbacks.addListener(listener);
-        UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
-                .startRecentsActivity(intent, null, mCallbacks, null, null));
+
+        if (ENABLE_SHELL_TRANSITIONS) {
+            RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
+                    mController != null ? mController.getController() : null);
+            Bundle options = ActivityOptionsCompat.makeRemoteTransition(transition)
+                    .setTransientLaunch().toBundle();
+            mCtx.startActivity(intent, options);
+        } else {
+            UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+                    .startRecentsActivity(intent, eventTime, mCallbacks, null, null));
+        }
         gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
         return mCallbacks;
     }
@@ -137,6 +194,14 @@
         return mCallbacks;
     }
 
+    public void setLiveTileCleanUpHandler(Runnable cleanUpHandler) {
+        mLiveTileCleanUpHandler = cleanUpHandler;
+    }
+
+    public void enableLiveTileRestartListener() {
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mLiveTileRestartListener);
+    }
+
     /**
      * Finishes the running recents animation.
      */
@@ -146,7 +211,7 @@
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
                     ? mController::finishAnimationToHome
                     : mController::finishAnimationToApp);
-            cleanUpRecentsAnimation(null /* canceledThumbnail */);
+            cleanUpRecentsAnimation();
         }
     }
 
@@ -173,11 +238,12 @@
     /**
      * Cleans up the recents animation entirely.
      */
-    private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
-        // Clean up the screenshot if necessary
-        if (mController != null && canceledThumbnail != null) {
-            mController.cleanupScreenshot();
+    private void cleanUpRecentsAnimation() {
+        if (mLiveTileCleanUpHandler != null) {
+            mLiveTileCleanUpHandler.run();
+            mLiveTileCleanUpHandler = null;
         }
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mLiveTileRestartListener);
 
         // Release all the target leashes
         if (mTargets != null) {
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 7ff799e..fa61fff 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -15,49 +15,50 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
 import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.WorkerThread;
 
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconProvider;
-import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.CancellableTask;
 import com.android.quickstep.util.TaskKeyLruCache;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.TaskDescriptionCompat;
 
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
  * Manages the caching of task icons and related data.
  */
-public class TaskIconCache {
+public class TaskIconCache implements DisplayInfoChangeListener {
 
-    private final Handler mBackgroundHandler;
+    private final Executor mBgExecutor;
     private final AccessibilityManager mAccessibilityManager;
 
     private final Context mContext;
@@ -65,15 +66,27 @@
     private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
     private final IconProvider mIconProvider;
 
-    public TaskIconCache(Context context, Looper backgroundLooper) {
+    private BaseIconFactory mIconFactory;
+
+    public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
         mContext = context;
-        mBackgroundHandler = new Handler(backgroundLooper);
+        mBgExecutor = bgExecutor;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+        mIconProvider = iconProvider;
 
         Resources res = context.getResources();
         int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
+
         mIconCache = new TaskKeyLruCache<>(cacheSize);
-        mIconProvider = new IconProvider(context);
+
+        DisplayController.INSTANCE.get(mContext).addChangeListener(this);
+    }
+
+    @Override
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        if ((flags & CHANGE_DENSITY) != 0) {
+            clearCache();
+        }
     }
 
     /**
@@ -83,36 +96,35 @@
      * @param callback The callback to receive the task after its data has been populated.
      * @return A cancelable handle to the request
      */
-    public IconLoadRequest updateIconInBackground(Task task, Consumer<Task> callback) {
+    public CancellableTask updateIconInBackground(Task task, Consumer<Task> callback) {
         Preconditions.assertUIThread();
         if (task.icon != null) {
             // Nothing to load, the icon is already loaded
             callback.accept(task);
             return null;
         }
-
-        IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
+        CancellableTask<TaskCacheEntry> request = new CancellableTask<TaskCacheEntry>() {
             @Override
-            public void run() {
-                TaskCacheEntry entry = getCacheEntry(task);
-                if (isCanceled()) {
-                    // We don't call back to the provided callback in this case
-                    return;
-                }
-                MAIN_EXECUTOR.execute(() -> {
-                    task.icon = entry.icon;
-                    task.titleDescription = entry.contentDescription;
-                    callback.accept(task);
-                    onEnd();
-                });
+            public TaskCacheEntry getResultOnBg() {
+                return getCacheEntry(task);
+            }
+
+            @Override
+            public void handleResult(TaskCacheEntry result) {
+                task.icon = result.icon;
+                task.titleDescription = result.contentDescription;
+                callback.accept(task);
             }
         };
-        Utilities.postAsyncCallback(mBackgroundHandler, request);
+        mBgExecutor.execute(request);
         return request;
     }
 
-    public void clear() {
-        mIconCache.evictAll();
+    /**
+     * Clears the icon cache
+     */
+    public void clearCache() {
+        mBgExecutor.execute(this::resetFactory);
     }
 
     void onTaskRemoved(TaskKey taskKey) {
@@ -120,9 +132,8 @@
     }
 
     void invalidateCacheEntries(String pkg, UserHandle handle) {
-        Utilities.postAsyncCallback(mBackgroundHandler,
-                () -> mIconCache.removeAll(key ->
-                        pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
+        mBgExecutor.execute(() -> mIconCache.removeAll(key ->
+                pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
     }
 
     @WorkerThread
@@ -143,21 +154,22 @@
         // TODO: Load icon resource (b/143363444)
         Bitmap icon = TaskDescriptionCompat.getIcon(desc, key.userId);
         if (icon != null) {
-            entry.icon = new FastBitmapDrawable(getBitmapInfo(
+            /* isInstantApp */
+            entry.icon = getBitmapInfo(
                     new BitmapDrawable(mContext.getResources(), icon),
                     key.userId,
                     desc.getPrimaryColor(),
-                    false /* isInstantApp */));
+                    false /* isInstantApp */).newIcon(mContext);
         } else {
             activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(
                     key.getComponent(), key.userId);
             if (activityInfo != null) {
                 BitmapInfo bitmapInfo = getBitmapInfo(
-                        mIconProvider.getIcon(activityInfo, UserHandle.of(key.userId)),
+                        mIconProvider.getIcon(activityInfo),
                         key.userId,
                         desc.getPrimaryColor(),
                         activityInfo.applicationInfo.isInstantApp());
-                entry.icon = newIcon(mContext, bitmapInfo);
+                entry.icon = bitmapInfo.newIcon(mContext);
             } else {
                 entry.icon = getDefaultIcon(key.userId);
             }
@@ -171,9 +183,8 @@
                         key.getComponent(), key.userId);
             }
             if (activityInfo != null) {
-                entry.contentDescription = ActivityManagerWrapper.getInstance()
-                        .getBadgedContentDescription(activityInfo, task.key.userId,
-                                task.taskDescription);
+                entry.contentDescription = getBadgedContentDescription(
+                        activityInfo, task.key.userId, task.taskDescription);
             }
         }
 
@@ -181,37 +192,62 @@
         return entry;
     }
 
+    private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
+        PackageManager pm = mContext.getPackageManager();
+        String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
+        if (TextUtils.isEmpty(taskLabel)) {
+            taskLabel = Utilities.trim(info.loadLabel(pm));
+        }
+
+        String applicationLabel = Utilities.trim(info.applicationInfo.loadLabel(pm));
+        String badgedApplicationLabel = userId != UserHandle.myUserId()
+                ? pm.getUserBadgedLabel(applicationLabel, UserHandle.of(userId)).toString()
+                : applicationLabel;
+        return applicationLabel.equals(taskLabel)
+                ? badgedApplicationLabel : badgedApplicationLabel + " " + taskLabel;
+    }
+
     @WorkerThread
     private Drawable getDefaultIcon(int userId) {
         synchronized (mDefaultIcons) {
             BitmapInfo info = mDefaultIcons.get(userId);
             if (info == null) {
-                try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
-                    info = la.makeDefaultIcon(UserHandle.of(userId));
+                try (BaseIconFactory bif = getIconFactory()) {
+                    info = bif.makeDefaultIcon(UserHandle.of(userId));
                 }
                 mDefaultIcons.put(userId, info);
             }
-            return new FastBitmapDrawable(info);
+            return info.newIcon(mContext);
         }
     }
 
     @WorkerThread
     private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
             int primaryColor, boolean isInstantApp) {
-        try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
-            la.disableColorExtraction();
-            la.setWrapperBackgroundColor(primaryColor);
+        try (BaseIconFactory bif = getIconFactory()) {
+            bif.disableColorExtraction();
+            bif.setWrapperBackgroundColor(primaryColor);
 
             // User version code O, so that the icon is always wrapped in an adaptive icon container
-            return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+            return bif.createBadgedIconBitmap(drawable, UserHandle.of(userId),
                     Build.VERSION_CODES.O, isInstantApp);
         }
     }
 
-    public static abstract class IconLoadRequest extends HandlerRunnable {
-        IconLoadRequest(Handler handler) {
-            super(handler, null);
+    @WorkerThread
+    private BaseIconFactory getIconFactory() {
+        if (mIconFactory == null) {
+            mIconFactory = new BaseIconFactory(mContext,
+                    DisplayController.INSTANCE.get(mContext).getInfo().densityDpi,
+                    mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size));
         }
+        return mIconFactory;
+    }
+
+    @WorkerThread
+    private void resetFactory() {
+        mIconFactory = null;
+        mIconCache.evictAll();
     }
 
     private static class TaskCacheEntry {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
similarity index 71%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
rename to quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index a595847..e75d751 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -18,6 +18,7 @@
 
 import static android.view.Surface.ROTATION_0;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
 import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
 
@@ -30,17 +31,24 @@
 import android.view.View;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
 import com.android.launcher3.BaseActivity;
 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.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.quickstep.TaskShortcutFactory.SplitSelectSystemShortcut;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.views.OverviewActionsView;
+import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
@@ -54,11 +62,18 @@
  */
 public class TaskOverlayFactory implements ResourceBasedOverride {
 
-    public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
+    public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView,
+            DeviceProfile deviceProfile) {
         final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
         final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
         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);
+                continue;
+            }
+
             if (shortcut != null) {
                 shortcuts.add(shortcut);
             }
@@ -73,6 +88,7 @@
             SystemShortcut screenshotShortcut = TaskShortcutFactory.SCREENSHOT
                     .getShortcut(activity, taskView);
             if (screenshotShortcut != null) {
+                screenshotShortcut.setHasFinishRecentsInAction(true);
                 shortcuts.add(screenshotShortcut);
             }
 
@@ -81,6 +97,7 @@
                 SystemShortcut modalShortcut = TaskShortcutFactory.MODAL
                         .getShortcut(activity, taskView);
                 if (modalShortcut != null) {
+                    modalShortcut.setHasFinishRecentsInAction(true);
                     shortcuts.add(modalShortcut);
                 }
             }
@@ -88,6 +105,18 @@
         return shortcuts;
     }
 
+
+    public static void addSplitOptions(List<SystemShortcut> outShortcuts,
+            BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) {
+        PagedOrientationHandler orientationHandler =
+                taskView.getRecentsView().getPagedOrientationHandler();
+        List<SplitPositionOption> positions =
+                orientationHandler.getSplitPositionOptions(deviceProfile);
+        for (SplitPositionOption option : positions) {
+            outShortcuts.add(new SplitSelectSystemShortcut(activity, taskView, option));
+        }
+    }
+
     public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
         return new TaskOverlay(thumbnailView);
     }
@@ -119,12 +148,11 @@
      */
     public static class TaskOverlay<T extends OverviewActionsView> {
 
-        private final Context mApplicationContext;
+        protected final Context mApplicationContext;
         protected final TaskThumbnailView mThumbnailView;
 
         private T mActionsView;
-        private ImageActionsApi mImageApi;
-        private boolean mIsAllowedByPolicy;
+        protected ImageActionsApi mImageApi;
 
         protected TaskOverlay(TaskThumbnailView taskThumbnailView) {
             mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
@@ -147,27 +175,27 @@
         public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
                 boolean rotated) {
             getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
-            final boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
 
             if (thumbnail != null) {
                 getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+                boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
+                getActionsView().setCallbacks(new OverlayUICallbacksImpl(isAllowedByPolicy, task));
+            }
+        }
 
-                getActionsView().setCallbacks(new OverlayUICallbacks() {
-                    @Override
-                    public void onShare() {
-                        if (isAllowedByPolicy) {
-                            mImageApi.startShareActivity();
-                        } else {
-                            showBlockedByPolicyMessage();
-                        }
-                    }
-
-                    @SuppressLint("NewApi")
-                    @Override
-                    public void onScreenshot() {
-                        saveScreenshot(task);
-                    }
-                });
+        /**
+         * End rendering live tile in Overview.
+         *
+         * @param callback callback to run, after switching to screenshot
+         */
+        public void endLiveTileMode(@NonNull Runnable callback) {
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+                RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
+                recentsView.switchToScreenshot(
+                        () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+                                false /* shouldPip */, callback));
+            } else {
+                callback.run();
             }
         }
 
@@ -175,7 +203,7 @@
          * Called to save screenshot of the task thumbnail.
          */
         @SuppressLint("NewApi")
-        private void saveScreenshot(Task task) {
+        protected void saveScreenshot(Task task) {
             if (mThumbnailView.isRealSnapshot()) {
                 mImageApi.saveScreenshot(mThumbnailView.getThumbnail(),
                         getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
@@ -204,6 +232,12 @@
         }
 
         /**
+         * Sets full screen progress to the task overlay.
+         */
+        public void setFullscreenProgress(float progress) {
+        }
+
+        /**
          * Gets the system shortcut for the screenshot that will be added to the task menu.
          */
         public SystemShortcut getScreenshotShortcut(BaseDraggingActivity activity,
@@ -233,7 +267,13 @@
             return mThumbnailView.getScaledInsets();
         }
 
-        private void showBlockedByPolicyMessage() {
+        /**
+         * Called when the device rotated.
+         */
+        public void updateOrientationState(RecentsOrientedState state) {
+        }
+
+        protected void showBlockedByPolicyMessage() {
             Toast.makeText(
                     mThumbnailView.getContext(),
                     R.string.blocked_by_policy,
@@ -255,6 +295,29 @@
                 dismissTaskMenuView(mActivity);
             }
         }
+
+        protected class OverlayUICallbacksImpl implements OverlayUICallbacks {
+            protected final boolean mIsAllowedByPolicy;
+            protected final Task mTask;
+
+            public OverlayUICallbacksImpl(boolean isAllowedByPolicy, Task task) {
+                mIsAllowedByPolicy = isAllowedByPolicy;
+                mTask = task;
+            }
+
+            public void onShare() {
+                if (mIsAllowedByPolicy) {
+                    endLiveTileMode(() -> mImageApi.startShareActivity(null));
+                } else {
+                    showBlockedByPolicyMessage();
+                }
+            }
+
+            @SuppressLint("NewApi")
+            public void onScreenshot() {
+                endLiveTileMode(() -> saveScreenshot(mTask));
+            }
+        }
     }
 
     /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
similarity index 87%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
rename to quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index ff051b6..a078bf3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -18,11 +18,9 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SELECTIONS;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
 
 import android.app.Activity;
 import android.app.ActivityOptions;
@@ -32,17 +30,18 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.view.View;
+import android.window.SplashScreen;
 
 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;
 import com.android.launcher3.popup.SystemShortcut.AppInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.InstantAppResolver;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
@@ -57,13 +56,11 @@
 
 import java.util.Collections;
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * Represents a system shortcut that can be shown for a recent task.
  */
 public interface TaskShortcutFactory {
-
     SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
 
     TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo());
@@ -98,6 +95,23 @@
         }
     }
 
+    class SplitSelectSystemShortcut extends SystemShortcut {
+        private final TaskView mTaskView;
+        private SplitPositionOption mSplitPositionOption;
+        public SplitSelectSystemShortcut(BaseDraggingActivity target, TaskView taskView,
+                SplitPositionOption option) {
+            super(option.mIconResId, option.mTextResId, target, taskView.getItemInfo());
+            mTaskView = taskView;
+            mSplitPositionOption = option;
+            setEnabled(taskView.getRecentsView().getTaskViewCount() > 1);
+        }
+
+        @Override
+        public void onClick(View view) {
+            mTaskView.initiateSplitSelect(mSplitPositionOption);
+        }
+    }
+
     class MultiWindowSystemShortcut extends SystemShortcut {
 
         private Handler mHandler;
@@ -152,6 +166,9 @@
             dismissTaskMenuView(mTarget);
 
             ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
+            if (options != null) {
+                options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+            }
             if (options != null
                     && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
                             options)) {
@@ -216,6 +233,16 @@
         }
 
         @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(
@@ -229,9 +256,6 @@
 
         @Override
         protected boolean onActivityStarted(BaseDraggingActivity activity) {
-            SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
-            activity.getUserEventDispatcher().logActionOnControl(TAP,
-                    LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
             return true;
         }
     };
@@ -287,15 +311,9 @@
 
         @Override
         public void onClick(View view) {
-            Consumer<Boolean> resultCallback = success -> {
-                if (success) {
-                    SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(
-                            mTaskView.getTask().key.id);
-                } else {
-                    mTaskView.notifyTaskLaunchFailed(TAG);
-                }
-            };
-            mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
+            if (mTaskView.launchTaskAnimated() != null) {
+                SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(mTaskView.getTask().key.id);
+            }
             dismissTaskMenuView(mTarget);
             mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
                     .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP);
@@ -310,16 +328,11 @@
     TaskShortcutFactory WELLBEING = (activity, view) ->
             WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, view.getItemInfo());
 
-    TaskShortcutFactory SCREENSHOT = (activity, tv) -> {
-        if (ENABLE_OVERVIEW_ACTIONS.get()) {
-            return tv.getThumbnail().getTaskOverlay()
-                .getScreenshotShortcut(activity, tv.getItemInfo());
-        }
-        return null;
-    };
+    TaskShortcutFactory SCREENSHOT = (activity, tv) -> tv.getThumbnail().getTaskOverlay()
+            .getScreenshotShortcut(activity, tv.getItemInfo());
 
     TaskShortcutFactory MODAL = (activity, tv) -> {
-        if (ENABLE_OVERVIEW_ACTIONS.get() && ENABLE_OVERVIEW_SELECTIONS.get()) {
+        if (ENABLE_OVERVIEW_SELECTIONS.get()) {
             return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(tv.getItemInfo());
         }
         return null;
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 2b7a8ec..a8a0219 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -15,17 +15,12 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Looper;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.CancellableTask;
 import com.android.quickstep.util.TaskKeyLruCache;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -33,11 +28,12 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 import java.util.ArrayList;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 public class TaskThumbnailCache {
 
-    private final Handler mBackgroundHandler;
+    private final Executor mBgExecutor;
 
     private final int mCacheSize;
     private final TaskKeyLruCache<ThumbnailData> mCache;
@@ -94,8 +90,8 @@
         }
     }
 
-    public TaskThumbnailCache(Context context, Looper backgroundLooper) {
-        mBackgroundHandler = new Handler(backgroundLooper);
+    public TaskThumbnailCache(Context context, Executor bgExecutor) {
+        mBgExecutor = bgExecutor;
         mHighResLoadingState = new HighResLoadingState(context);
 
         Resources res = context.getResources();
@@ -130,7 +126,7 @@
      * @param callback The callback to receive the task after its data has been populated.
      * @return A cancelable handle to the request
      */
-    public ThumbnailLoadRequest updateThumbnailInBackground(
+    public CancellableTask updateThumbnailInBackground(
             Task task, Consumer<ThumbnailData> callback) {
         Preconditions.assertUIThread();
 
@@ -142,14 +138,13 @@
             return null;
         }
 
-
         return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
             task.thumbnail = t;
             callback.accept(t);
         });
     }
 
-    private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean lowResolution,
+    private CancellableTask updateThumbnailInBackground(TaskKey key, boolean lowResolution,
             Consumer<ThumbnailData> callback) {
         Preconditions.assertUIThread();
 
@@ -160,26 +155,20 @@
             return null;
         }
 
-        ThumbnailLoadRequest request = new ThumbnailLoadRequest(mBackgroundHandler,
-                lowResolution) {
+        CancellableTask<ThumbnailData> request = new CancellableTask<ThumbnailData>() {
             @Override
-            public void run() {
-                ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
+            public ThumbnailData getResultOnBg() {
+                return ActivityManagerWrapper.getInstance().getTaskThumbnail(
                         key.id, lowResolution);
+            }
 
-                MAIN_EXECUTOR.execute(() -> {
-                    if (isCanceled()) {
-                        // We don't call back to the provided callback in this case
-                        return;
-                    }
-
-                    mCache.put(key, thumbnail);
-                    callback.accept(thumbnail);
-                    onEnd();
-                });
+            @Override
+            public void handleResult(ThumbnailData result) {
+                mCache.put(key, result);
+                callback.accept(result);
             }
         };
-        Utilities.postAsyncCallback(mBackgroundHandler, request);
+        mBgExecutor.execute(request);
         return request;
     }
 
@@ -218,15 +207,6 @@
         return mEnableTaskSnapshotPreloading && mHighResLoadingState.mVisible;
     }
 
-    public static abstract class ThumbnailLoadRequest extends HandlerRunnable {
-        public final boolean mLowResolution;
-
-        ThumbnailLoadRequest(Handler handler, boolean lowResolution) {
-            super(handler, null);
-            mLowResolution = lowResolution;
-        }
-    }
-
     /**
      * @return Whether device supports low-res thumbnails. Low-res files are an optimization
      * for faster load times of snapshots. Devices can optionally disable low-res files so that
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index 04b488d..c9db153 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -27,6 +29,7 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.List;
@@ -86,4 +89,12 @@
         }
         return false;
     }
+
+    /**
+     * Requests that the system close any open system windows (including other SystemUI).
+     */
+    public static void closeSystemWindowsAsync(String reason) {
+        UI_HELPER_EXECUTOR.execute(
+                () -> ActivityManagerWrapper.getInstance().closeSystemWindows(reason));
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
new file mode 100644
index 0000000..37fda73
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -0,0 +1,545 @@
+/*
+ * 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;
+
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_DELAY_NAV_FADE_IN;
+import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_NAV_FADE_IN_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_NAV_FADE_OUT_DURATION;
+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.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;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.window.TransitionInfo;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.AnimationSuccessListener;
+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.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.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+
+/**
+ * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public final class TaskViewUtils {
+
+    private TaskViewUtils() {}
+
+    /**
+     * Try to find a TaskView that corresponds with the component of the launched view.
+     *
+     * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
+     * Otherwise, we will assume we are using a normal app transition, but it's possible that the
+     * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
+     */
+    public static TaskView findTaskViewToLaunch(
+            RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
+        if (v instanceof TaskView) {
+            TaskView taskView = (TaskView) v;
+            return recentsView.isTaskViewVisible(taskView) ? taskView : null;
+        }
+
+        // It's possible that the launched view can still be resolved to a visible task view, check
+        // the task id of the opening task and see if we can find a match.
+        if (v.getTag() instanceof ItemInfo) {
+            ItemInfo itemInfo = (ItemInfo) v.getTag();
+            ComponentName componentName = itemInfo.getTargetComponent();
+            int userId = itemInfo.user.getIdentifier();
+            if (componentName != null) {
+                for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
+                    TaskView taskView = recentsView.getTaskViewAt(i);
+                    if (recentsView.isTaskViewVisible(taskView)) {
+                        Task.TaskKey key = taskView.getTask().key;
+                        if (componentName.equals(key.getComponent()) && userId == key.userId) {
+                            return taskView;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (targets == null) {
+            return null;
+        }
+        // Resolve the opening task id
+        int openingTaskId = -1;
+        for (RemoteAnimationTargetCompat target : targets) {
+            if (target.mode == MODE_OPENING) {
+                openingTaskId = target.taskId;
+                break;
+            }
+        }
+
+        // If there is no opening task id, fall back to the normal app icon launch animation
+        if (openingTaskId == -1) {
+            return null;
+        }
+
+        // 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);
+        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,
+            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) {
+        boolean isQuickSwitch = v.isEndQuickswitchCuj();
+        v.setEndQuickswitchCuj(false);
+
+        boolean inLiveTileMode =
+                ENABLE_QUICKSTEP_LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
+        final RemoteAnimationTargets targets =
+                new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
+                        inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
+        final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
+
+        if (params == null) {
+            SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+            targets.addReleaseCheck(applier);
+
+            params = new TransformParams()
+                    .setSyncTransactionApplier(applier)
+                    .setTargetSet(targets);
+        }
+
+        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 parallaxCenterAndAdjacentTask =
+                taskIndex != recentsView.getCurrentPage() && !showAsGrid;
+        float gridTranslationSecondary = recentsView.getGridTranslationSecondary(taskIndex);
+        int startScroll = recentsView.getScrollOffset(taskIndex);
+
+        TaskViewSimulator topMostSimulator = null;
+
+        if (tsv == null && targets.apps.length > 0) {
+            tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
+            tsv.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);
+
+            tsv.setPreview(targets.apps[targets.apps.length - 1]);
+            tsv.fullScreenProgress.value = 0;
+            tsv.recentsViewScale.value = 1;
+            if (showAsGrid) {
+                tsv.taskSecondaryTranslation.value = gridTranslationSecondary;
+            }
+            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,
+                    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,
+                    TOUCH_RESPONSE_INTERPOLATOR);
+
+            TaskViewSimulator finalTsv = tsv;
+            TransformParams finalParams = params;
+            out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
+            if (navBarTarget != null) {
+                final Rect cropRect = new Rect();
+                out.addOnFrameListener(new MultiValueUpdateListener() {
+                    FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0,
+                            ANIMATION_NAV_FADE_OUT_DURATION, NAV_FADE_OUT_INTERPOLATOR);
+                    FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,
+                            ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
+
+                    @Override
+                    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);
+                        }
+                        finalParams.applySurfaceParams(navBuilder.build());
+                    }
+                });
+            } else if (inLiveTileMode) {
+                // There is no transition animation for app launch from recent in live tile mode so
+                // we have to trigger the navigation bar animation from system here.
+                final RecentsAnimationController controller =
+                        recentsView.getRecentsAnimationController();
+                if (controller != null) {
+                    controller.animateNavigationBarToApp(RECENTS_LAUNCH_DURATION);
+                }
+            }
+            topMostSimulator = tsv;
+        }
+
+        if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
+            out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
+
+            TaskViewSimulator simulatorToCopy = topMostSimulator;
+            simulatorToCopy.apply(params);
+
+            // Mt represents the overall transformation on the thumbnailView relative to the
+            // Launcher's rootView
+            // K(t) represents transformation on the running window by the taskViewSimulator at
+            // any time t.
+            // at t = 0, we know that the simulator matches the thumbnailView. So if we apply K(0)`
+            // on the Launcher's rootView, the thumbnailView would match the full running task
+            // window. If we apply "K(0)` K(t)" thumbnailView will match the final transformed
+            // window at any time t. This gives the overall matrix on thumbnailView to be:
+            //    Mt K(0)` K(t)
+            // 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]);
+
+            Matrix mt = new Matrix();
+            mt.setRectToRect(tvBounds, tvBoundsInRoot, ScaleToFit.FILL);
+
+            Matrix mti = new Matrix();
+            mt.invert(mti);
+
+            Matrix k0i = new Matrix();
+            simulatorToCopy.getCurrentMatrix().invert(k0i);
+
+            Matrix animationMatrix = new Matrix();
+            out.addOnFrameCallback(() -> {
+                animationMatrix.set(mt);
+                animationMatrix.postConcat(k0i);
+                animationMatrix.postConcat(simulatorToCopy.getCurrentMatrix());
+                animationMatrix.postConcat(mti);
+                ttv.setAnimationMatrix(animationMatrix);
+            });
+
+            out.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    ttv.setAnimationMatrix(null);
+                }
+            });
+        }
+
+        out.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                if (isQuickSwitch) {
+                    InteractionJankMonitorWrapper.end(
+                            InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+                }
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                targets.release();
+                super.onAnimationEnd(animation);
+            }
+        });
+
+        if (depthController != null) {
+            out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(context),
+                    TOUCH_RESPONSE_INTERPOLATOR);
+        }
+    }
+
+    /**
+     * TODO: This doesn't animate at present. Feel free to blow out everyhing in this method
+     * if needed
+     *
+     * We could manually try to animate the just the bounds for the leashes we get back, but we try
+     * to do it through TaskViewSimulator(TVS) since that handles a lot of the recents UI stuff for
+     * us.
+     *
+     * First you have to call TVS#setPreview() to indicate which leash it will operate one
+     * Then operations happen in TVS#apply() on each frame callback.
+     *
+     * TVS uses DeviceProfile to try to figure out things like task height and such based on if the
+     * device is in multiWindowMode or not. It's unclear given the two calls to startTask() when the
+     * 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,
+            SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
+
+        final TransitionInfo.Change[] splitRoots = new TransitionInfo.Change[2];
+        for (int i = 0; i < transitionInfo.getChanges().size(); ++i) {
+            final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
+            final int taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
+            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 (!(mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
+                    throw new IllegalStateException(
+                            "Expected task to be showing, but it is " + mode);
+                }
+                if (change.getParent() == null) {
+                    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] =
+                        transitionInfo.getChange(change.getParent());
+            }
+        }
+
+        // This is where we should animate the split roots. For now, though, just make them visible.
+        for (int i = 0; i < 2; ++i) {
+            t.show(splitRoots[i].getLeash());
+            t.setAlpha(splitRoots[i].getLeash(), 1.f);
+        }
+
+        // This contains the initial state (before animation), so apply this at the beginning of
+        // the animation.
+        t.apply();
+
+        // Once there is an animation, this should be called AFTER the animation completes.
+        finishCallback.run();
+    }
+
+    /** Legacy version (until shell transitions are enabled) */
+    public static void composeRecentsSplitLaunchAnimatorLegacy(@NonNull AnimatorSet anim,
+            @NonNull TaskView v, @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();
+        }
+
+        boolean inLiveTileMode =
+                ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1;
+        final RemoteAnimationTargets targets =
+                new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
+                        inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
+
+        if (params == null) {
+            SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+            targets.addReleaseCheck(applier);
+
+            params = new TransformParams()
+                    .setSyncTransactionApplier(applier)
+                    .setTargetSet(targets);
+        }
+
+        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));
+        }
+
+        TaskViewSimulator topMostSimulator = null;
+
+        if (tvs != null) {
+            out.setFloat(tvs.fullScreenProgress,
+                    AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
+            out.setFloat(tvs.recentsViewScale,
+                    AnimatedFloat.VALUE, tvs.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
+            out.setFloat(tvs.recentsViewScroll,
+                    AnimatedFloat.VALUE, 0, TOUCH_RESPONSE_INTERPOLATOR);
+
+            TaskViewSimulator finalTsv = tvs;
+            TransformParams finalParams = params;
+            out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
+            topMostSimulator = tvs;
+        }
+
+        anim.play(out.buildAnim());
+    }
+
+    public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+            @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
+            @NonNull StateManager stateManager, @NonNull RecentsView recentsView,
+            @NonNull 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);
+
+        Animator childStateAnimation = null;
+        // Found a visible recents task that matches the opening app, lets launch the app from there
+        Animator launcherAnim;
+        final AnimatorListenerAdapter windowAnimEndListener;
+        if (launcherClosing) {
+            Context context = v.getContext();
+            DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
+            launcherAnim = dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()
+                    ? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0)
+                    : recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
+            launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+            launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
+
+            // Make sure recents gets fixed up by resetting task alphas and scales, etc.
+            windowAnimEndListener = new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    recentsView.finishRecentsAnimation(false /* toRecents */, () -> {
+                        recentsView.post(() -> {
+                            stateManager.moveToRestState();
+                            stateManager.reapplyState();
+                        });
+                    });
+                }
+            };
+        } else {
+            AnimatorPlaybackController controller =
+                    stateManager.createAnimationToNewWorkspace(NORMAL, RECENTS_LAUNCH_DURATION);
+            controller.dispatchOnStart();
+            childStateAnimation = controller.getTarget();
+            launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
+            windowAnimEndListener = new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    recentsView.finishRecentsAnimation(false /* toRecents */,
+                            () -> stateManager.goToState(NORMAL, false));
+                }
+            };
+        }
+        pa.add(launcherAnim);
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1) {
+            pa.addOnFrameCallback(recentsView::redrawLiveTile);
+        }
+        anim.play(pa.buildAnim());
+
+        // Set the current animation first, before adding windowAnimEndListener. Setting current
+        // animation adds some listeners which need to be called before windowAnimEndListener
+        // (the ordering of listeners matter in this case).
+        stateManager.setCurrentAnimation(anim, childStateAnimation);
+        anim.addListener(windowAnimEndListener);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
similarity index 70%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
rename to quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7b91001..db5c93c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -20,11 +20,17 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.GestureState.DEFAULT_STATE;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+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;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
+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_TRACING_ENABLED;
 
@@ -38,36 +44,43 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.view.Choreographer;
+import android.view.Display;
 import android.view.InputEvent;
 import android.view.MotionEvent;
+import android.view.Surface;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.BinderThread;
+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;
+import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarManager;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.tracing.nano.LauncherTraceProto;
-import com.android.launcher3.tracing.nano.TouchInteractionServiceProto;
+import com.android.launcher3.tracing.LauncherTraceProto;
+import com.android.launcher3.tracing.TouchInteractionServiceProto;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
@@ -75,6 +88,7 @@
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
 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;
@@ -91,40 +105,29 @@
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
 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.splitscreen.ISplitScreen;
+import com.android.wm.shell.startingsurface.IStartingWindow;
+import com.android.wm.shell.transition.IShellTransitions;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Wrapper around a list for processing arguments.
- */
-class ArgList extends LinkedList<String> {
-    public ArgList(List<String> l) {
-        super(l);
-    }
-
-    public String peekArg() {
-        return peekFirst();
-    }
-
-    public String nextArg() {
-        return pollFirst().toLowerCase();
-    }
-}
 
 /**
  * Service connected by system-UI for handling touch interaction.
  */
 @TargetApi(Build.VERSION_CODES.R)
 public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin>,
-        ProtoTraceable<LauncherTraceProto> {
+        ProtoTraceable<LauncherTraceProto.Builder> {
 
     private static final String TAG = "TouchInteractionService";
 
@@ -139,35 +142,70 @@
      */
     private static final int SYSTEM_ACTION_ID_ALL_APPS = 14;
 
+    public static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION =
+            SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false);
+
     private int mBackGestureNotificationCounter = -1;
     @Nullable
     private OverscrollPlugin mOverscrollPlugin;
 
-    private final IBinder mMyBinder = new IOverviewProxy.Stub() {
+    /**
+     * Local IOverviewProxy implementation with some methods for local components
+     */
+    public class TISBinder extends IOverviewProxy.Stub {
 
         @BinderThread
         public void onInitialize(Bundle bundle) {
             ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
                     bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
+            IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
+            ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
+                    KEY_EXTRA_SHELL_SPLIT_SCREEN));
+            IOneHanded onehanded = IOneHanded.Stub.asInterface(
+                    bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
+            IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
+                    bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS));
+            IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
+                    bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW));
+            ISmartspaceTransitionController smartspaceTransitionController =
+                    ISmartspaceTransitionController.Stub.asInterface(
+                            bundle.getBinder(KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER));
             MAIN_EXECUTOR.execute(() -> {
-                SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy);
+                SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
+                        splitscreen, onehanded, shellTransitions, startingWindow,
+                        smartspaceTransitionController);
                 TouchInteractionService.this.initInputMonitor();
                 preloadOverview(true /* fromInit */);
+                mDeviceState.runOnUserUnlocked(() -> {
+                    final BaseActivityInterface ai =
+                            mOverviewComponentObserver.getActivityInterface();
+                    if (ai == null) return;
+                    ai.onOverviewServiceBound();
+                });
             });
             sIsInitialized = true;
         }
 
         @BinderThread
-        @Override
         public void onOverviewToggle() {
             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
-            mOverviewCommandHelper.onOverviewToggle();
+            // If currently screen pinning, do not enter overview
+            if (mDeviceState.isScreenPinningActive()) {
+                return;
+            }
+            TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+            mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
         }
 
         @BinderThread
         @Override
         public void onOverviewShown(boolean triggeredFromAltTab) {
-            mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
+            if (triggeredFromAltTab) {
+                TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+                mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW_NEXT_FOCUS);
+            } else {
+                mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW);
+            }
         }
 
         @BinderThread
@@ -175,14 +213,14 @@
         public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
             if (triggeredFromAltTab && !triggeredFromHomeKey) {
                 // onOverviewShownFromAltTab hides the overview and ends at the target app
-                mOverviewCommandHelper.onOverviewHidden();
+                mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE);
             }
         }
 
         @BinderThread
         @Override
         public void onTip(int actionType, int viewType) {
-            mOverviewCommandHelper.onTip(actionType, viewType);
+            // Please delete this method from the interface
         }
 
         @BinderThread
@@ -206,25 +244,15 @@
         @BinderThread
         public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
                 boolean gestureSwipeLeft) {
-            if (mOverviewComponentObserver == null) {
-                return;
-            }
-
-            final BaseActivityInterface activityInterface =
-                    mOverviewComponentObserver.getActivityInterface();
-            UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY,
-                    isButton, gestureSwipeLeft, activityInterface.getContainerType());
-
-            if (completed && !isButton && shouldNotifyBackGesture()) {
-                UI_HELPER_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture);
-            }
+            // Remove this method from the interface
         }
 
         @BinderThread
         public void onSystemUiStateChanged(int stateFlags) {
             MAIN_EXECUTOR.execute(() -> {
+                int lastFlags = mDeviceState.getSystemUiStateFlags();
                 mDeviceState.setSystemUiFlags(stateFlags);
-                TouchInteractionService.this.onSystemUiFlagsChanged();
+                TouchInteractionService.this.onSystemUiFlagsChanged(lastFlags);
             });
         }
 
@@ -233,28 +261,27 @@
             MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
         }
 
+        @Override
         public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets)  {
             WindowBounds wb = new WindowBounds(bounds, insets);
             MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
         }
 
-        /** Deprecated methods **/
-        public void onQuickStep(MotionEvent motionEvent) { }
-
-        public void onQuickScrubEnd() { }
-
-        public void onQuickScrubProgress(float progress) { }
-
-        public void onQuickScrubStart() { }
-
-        public void onPreMotionEvent(int downHitTarget) { }
-
-        public void onMotionEvent(MotionEvent ev) {
-            ev.recycle();
+        @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 onBind(ISystemUiProxy iSystemUiProxy) { }
-    };
+        public TaskbarManager getTaskbarManager() {
+            return mTaskbarManager;
+        }
+
+        public OverviewCommandHelper getOverviewCommandHelper() {
+            return mOverviewCommandHelper;
+        }
+    }
 
     private static boolean sConnected = false;
     private static boolean sIsInitialized = false;
@@ -264,13 +291,14 @@
         return sConnected;
     }
 
+
     public static boolean isInitialized() {
         return sIsInitialized;
     }
 
-    private final BaseSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
+    private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
             this::createLauncherSwipeHandler;
-    private final BaseSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
+    private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
             this::createFallbackSwipeHandler;
 
     private ActivityManagerWrapper mAM;
@@ -280,15 +308,19 @@
     private RecentsAnimationDeviceState mDeviceState;
     private TaskAnimationManager mTaskAnimationManager;
 
-    private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
-    private InputConsumer mConsumer = InputConsumer.NO_OP;
+    private @NonNull InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
+    private @NonNull InputConsumer mConsumer = InputConsumer.NO_OP;
     private Choreographer mMainChoreographer;
-    private InputConsumer mResetGestureInputConsumer;
+    private @Nullable ResetGestureInputConsumer mResetGestureInputConsumer;
     private GestureState mGestureState = DEFAULT_STATE;
 
     private InputMonitorCompat mInputMonitorCompat;
     private InputEventReceiver mInputEventReceiver;
 
+    private DisplayManager mDisplayManager;
+
+    private TaskbarManager mTaskbarManager;
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -296,12 +328,16 @@
         // Everything else should be initialized in onUserUnlocked() below.
         mMainChoreographer = Choreographer.getInstance();
         mAM = ActivityManagerWrapper.getInstance();
-        mDeviceState = new RecentsAnimationDeviceState(this);
-        mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
-        mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
-        mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
-        ProtoTracer.INSTANCE.get(this).add(this);
+        mDeviceState = new RecentsAnimationDeviceState(this, true);
+        mDisplayManager = getSystemService(DisplayManager.class);
+        mTaskbarManager = new TaskbarManager(this);
 
+        mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
+        mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
+        mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
+        mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
+        mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
+        ProtoTracer.INSTANCE.get(this).add(this);
         sConnected = true;
     }
 
@@ -318,13 +354,12 @@
 
     private void initInputMonitor() {
         disposeEventHandlers();
-        if (mDeviceState.isButtonNavMode() || !SystemUiProxy.INSTANCE.get(this).isActive()) {
+
+        if (mDeviceState.isButtonNavMode()) {
             return;
         }
 
-        Bundle bundle = SystemUiProxy.INSTANCE.get(this).monitorGestureInput("swipe-up",
-                mDeviceState.getDisplayId());
-        mInputMonitorCompat = InputMonitorCompat.fromBundle(bundle, KEY_EXTRA_INPUT_MONITOR);
+        mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId());
         mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
                 mMainChoreographer, this::onInputEvent);
 
@@ -339,16 +374,23 @@
         resetHomeBounceSeenOnQuickstepEnabledFirstTime();
     }
 
+    /**
+     * Called when the one handed mode overlay package changes, to recreate touch region.
+     */
+    private void onOneHandedModeOverlayChanged(int newGesturalHeight) {
+        initInputMonitor();
+    }
+
     @UiThread
     public void onUserUnlocked() {
-        mTaskAnimationManager = new TaskAnimationManager();
+        mTaskAnimationManager = new TaskAnimationManager(this);
         mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
-        mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
-                mOverviewComponentObserver);
+        mOverviewCommandHelper = new OverviewCommandHelper(this,
+                mOverviewComponentObserver, mTaskAnimationManager);
         mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
         mInputConsumer.registerInputConsumer();
-        onSystemUiFlagsChanged();
+        onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
         onAssistantVisibilityChanged();
 
         // Temporarily disable model preload
@@ -364,6 +406,10 @@
         onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
     }
 
+    public OverviewCommandHelper getOverviewCommandHelper() {
+        return mOverviewCommandHelper;
+    }
+
     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
         if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
             // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
@@ -392,7 +438,7 @@
                     getString(R.string.all_apps_label),
                     getString(R.string.all_apps_label),
                     PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent,
-                            PendingIntent.FLAG_UPDATE_CURRENT));
+                            PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
             am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
         } else {
             am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
@@ -400,17 +446,24 @@
     }
 
     @UiThread
-    private void onSystemUiFlagsChanged() {
+    private void onSystemUiFlagsChanged(int lastSysUIFlags) {
         if (mDeviceState.isUserUnlocked()) {
-            SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(
-                    mDeviceState.getSystemUiStateFlags());
+            int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
+            SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
             mOverviewComponentObserver.onSystemUiStateChanged();
+            mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags);
 
-            // Update the tracing state
-            if ((mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_TRACING_ENABLED) != 0) {
-                ProtoTracer.INSTANCE.get(TouchInteractionService.this).start();
-            } else {
-                ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop();
+            if ((lastSysUIFlags & SYSUI_STATE_TRACING_ENABLED) !=
+                    (systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED)) {
+                // Update the tracing state
+                if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) {
+                    Log.d(TAG, "Starting tracing.");
+                    ProtoTracer.INSTANCE.get(this).start();
+                } else {
+                    Log.d(TAG, "Stopping tracing. Dumping to file="
+                            + ProtoTracer.INSTANCE.get(this).getTraceFile());
+                    ProtoTracer.INSTANCE.get(this).stop();
+                }
             }
         }
     }
@@ -425,6 +478,7 @@
 
     @Override
     public void onDestroy() {
+        Log.d(TAG, "Touch service destroyed: user=" + getUserId());
         sIsInitialized = false;
         if (mDeviceState.isUserUnlocked()) {
             mInputConsumer.unregisterInputConsumer();
@@ -433,21 +487,22 @@
         }
         disposeEventHandlers();
         mDeviceState.destroy();
-        SystemUiProxy.INSTANCE.get(this).setProxy(null);
-        ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop();
+        SystemUiProxy.INSTANCE.get(this).clearProxy();
+        ProtoTracer.INSTANCE.get(this).stop();
         ProtoTracer.INSTANCE.get(this).remove(this);
 
         getSystemService(AccessibilityManager.class)
                 .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
 
+        mTaskbarManager.destroy();
         sConnected = false;
         super.onDestroy();
     }
 
     @Override
     public IBinder onBind(Intent intent) {
-        Log.d(TAG, "Touch service connected");
-        return mMyBinder;
+        Log.d(TAG, "Touch service connected: user=" + getUserId());
+        return new TISBinder();
     }
 
     private void onInputEvent(InputEvent ev) {
@@ -456,6 +511,15 @@
             return;
         }
         MotionEvent event = (MotionEvent) ev;
+        if (ENABLE_PER_WINDOW_INPUT_ROTATION) {
+            final Display display = mDisplayManager.getDisplay(mDeviceState.getDisplayId());
+            int rotation = display.getRotation();
+            Point sz = new Point();
+            display.getRealSize(sz);
+            if (rotation != Surface.ROTATION_0) {
+                event.transform(InputChannelCompat.createRotationMatrix(rotation, sz.x, sz.y));
+            }
+        }
 
         TestLogging.recordMotionEvent(
                 TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
@@ -469,20 +533,15 @@
 
         final int action = event.getAction();
         if (action == ACTION_DOWN) {
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.NO_SWIPE_TO_HOME, "TouchInteractionService.onInputEvent:DOWN");
-            }
             mRotationTouchHelper.setOrientationTransformIfNeeded(event);
 
-            if (mRotationTouchHelper.isInSwipeUpTouchRegion(event)) {
-                if (TestProtocol.sDebugTracing) {
-                    Log.d(TestProtocol.NO_SWIPE_TO_HOME,
-                            "TouchInteractionService.onInputEvent:isInSwipeUpTouchRegion");
-                }
+            if (!mDeviceState.isOneHandedModeActive()
+                    && mRotationTouchHelper.isInSwipeUpTouchRegion(event)) {
                 // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
                 // onConsumerInactive and wipe the previous gesture state
                 GestureState prevGestureState = new GestureState(mGestureState);
                 GestureState newGestureState = createGestureState(mGestureState);
+                newGestureState.setSwipeUpStartTimeMs(SystemClock.uptimeMillis());
                 mConsumer.onConsumerAboutToBeSwitched();
                 mGestureState = newGestureState;
                 mConsumer = newConsumer(prevGestureState, mGestureState, event);
@@ -500,10 +559,19 @@
                             this,
                             mGestureState,
                             InputConsumer.NO_OP, mInputMonitorCompat,
-                            mOverviewComponentObserver.assistantGestureIsConstrained());
+                            mDeviceState,
+                            event);
+                } else if (mDeviceState.canTriggerOneHandedAction(event)) {
+                    // Consume gesture event for triggering one handed feature.
+                    mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
+                        InputConsumer.NO_OP, mInputMonitorCompat);
                 } else {
                     mUncheckedConsumer = InputConsumer.NO_OP;
                 }
+            } else if (mDeviceState.canTriggerOneHandedAction(event)) {
+                // Consume gesture event for triggering one handed feature.
+                mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
+                        InputConsumer.NO_OP, mInputMonitorCompat);
             } else {
                 mUncheckedConsumer = InputConsumer.NO_OP;
             }
@@ -529,18 +597,24 @@
             }
         }
 
-        boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL)
+        boolean cancelGesture = mGestureState.getActivityInterface() != null
+                && mGestureState.getActivityInterface().shouldCancelCurrentGesture();
+        boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture)
                 && mConsumer != null
                 && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
+        if (cancelGesture) {
+            event.setAction(ACTION_CANCEL);
+        }
         mUncheckedConsumer.onMotionEvent(event);
 
         if (cleanUpConsumer) {
             reset();
         }
         TraceHelper.INSTANCE.endFlagsOverride(traceToken);
+        ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
     }
 
-    private GestureState createGestureState(GestureState previousGestureState) {
+    public GestureState createGestureState(GestureState previousGestureState) {
         GestureState gestureState = new GestureState(mOverviewComponentObserver,
                 ActiveGestureLog.INSTANCE.generateAndSetLogId());
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
@@ -557,9 +631,6 @@
 
     private InputConsumer newConsumer(GestureState previousGestureState,
             GestureState newGestureState, MotionEvent event) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_SWIPE_TO_HOME, "newConsumer");
-        }
         boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
 
         if (!mDeviceState.isUserUnlocked()) {
@@ -568,30 +639,23 @@
                 // launched while device is locked even after exiting direct boot mode (e.g. camera).
                 return createDeviceLockedInputConsumer(newGestureState);
             } else {
-                return mResetGestureInputConsumer;
+                return getDefaultInputConsumer();
             }
         }
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_SWIPE_TO_HOME, "newConsumer:user is unlocked");
-        }
 
         // When there is an existing recents animation running, bypass systemState check as this is
         // a followup gesture and the first gesture started in a valid system state.
         InputConsumer base = canStartSystemGesture
                 || previousGestureState.isRecentsAnimationRunning()
                         ? newBaseConsumer(previousGestureState, newGestureState, event)
-                        : mResetGestureInputConsumer;
+                        : getDefaultInputConsumer();
         if (mDeviceState.isGesturalNavMode()) {
             handleOrientationSetup(base);
         }
         if (mDeviceState.isFullyGesturalNavMode()) {
             if (mDeviceState.canTriggerAssistantAction(event, newGestureState.getRunningTask())) {
-                base = new AssistantInputConsumer(
-                    this,
-                    newGestureState,
-                    base,
-                    mInputMonitorCompat,
-                    mOverviewComponentObserver.assistantGestureIsConstrained());
+                base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat,
+                        mDeviceState, event);
             }
 
             if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
@@ -627,23 +691,29 @@
                 base = new ScreenPinnedInputConsumer(this, newGestureState);
             }
 
+            if (mDeviceState.canTriggerOneHandedAction(event)) {
+                base = new OneHandedModeInputConsumer(this, mDeviceState, base,
+                        mInputMonitorCompat);
+            }
+
             if (mDeviceState.isAccessibilityMenuAvailable()) {
                 base = new AccessibilityInputConsumer(this, mDeviceState, base,
                         mInputMonitorCompat);
             }
         } else {
             if (mDeviceState.isScreenPinningActive()) {
-                base = mResetGestureInputConsumer;
+                base = getDefaultInputConsumer();
+            }
+
+            if (mDeviceState.canTriggerOneHandedAction(event)) {
+                base = new OneHandedModeInputConsumer(this, mDeviceState, base,
+                        mInputMonitorCompat);
             }
         }
         return base;
     }
 
     private void handleOrientationSetup(InputConsumer baseInputConsumer) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.1");
-        }
-
         baseInputConsumer.notifyOrientationSetup();
     }
 
@@ -670,40 +740,39 @@
                     runningComponent != null && runningComponent.equals(homeComponent);
         }
 
-        if (gestureState.getRunningTask() == null) {
-            return mResetGestureInputConsumer;
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()
+                && gestureState.getActivityInterface().isInLiveTileMode()) {
+            return createOverviewInputConsumer(
+                    previousGestureState, gestureState, event, forceOverviewInputConsumer);
+        } else if (gestureState.getRunningTask() == null) {
+            return getDefaultInputConsumer();
         } else if (previousGestureState.isRunningAnimationToLauncher()
                 || gestureState.getActivityInterface().isResumed()
                 || forceOverviewInputConsumer) {
             return createOverviewInputConsumer(
                     previousGestureState, gestureState, event, forceOverviewInputConsumer);
-        } else if (ENABLE_QUICKSTEP_LIVE_TILE.get()
-                && gestureState.getActivityInterface().isInLiveTileMode()) {
-            return createOverviewInputConsumer(
-                    previousGestureState, gestureState, event, forceOverviewInputConsumer);
         } else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         } else {
             return createOtherActivityInputConsumer(gestureState, event);
         }
     }
 
+    public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
+        return !mOverviewComponentObserver.isHomeAndOverviewSame()
+                ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory;
+    }
+
     private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
             MotionEvent event) {
 
-        final BaseSwipeUpHandler.Factory factory;
-        if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
-            factory = mFallbackSwipeHandlerFactory;
-        } else {
-            factory = mLauncherSwipeHandlerFactory;
-        }
-
+        final AbsSwipeUpHandler.Factory factory = getSwipeUpHandlerFactory();
         final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame()
                 || gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event);
         final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
         return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
                 gestureState, shouldDefer, this::onConsumerInactive,
-                mInputMonitorCompat, disableHorizontalSwipe, factory);
+                mInputMonitorCompat, mInputEventReceiver, disableHorizontalSwipe, factory);
     }
 
     private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState) {
@@ -711,7 +780,7 @@
             return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
                     gestureState, mInputMonitorCompat);
         } else {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         }
     }
 
@@ -720,13 +789,15 @@
             boolean forceOverviewInputConsumer) {
         StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
         if (activity == null) {
-            return mResetGestureInputConsumer;
+            return getDefaultInputConsumer();
         }
 
         if (activity.getRootView().hasWindowFocus()
                 || previousGestureState.isRunningAnimationToLauncher()
-                || (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
-                    && forceOverviewInputConsumer)) {
+                || (ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
+                    && forceOverviewInputConsumer)
+                || (ENABLE_QUICKSTEP_LIVE_TILE.get()
+                && gestureState.getActivityInterface().isInLiveTileMode())) {
             return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
                     false /* startingInActivityBounds */);
         } else {
@@ -748,8 +819,26 @@
     }
 
     private void reset() {
-        mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
+        mConsumer = mUncheckedConsumer = getDefaultInputConsumer();
         mGestureState = DEFAULT_STATE;
+        // By default, use batching of the input events, but check receiver before using in the rare
+        // case that the monitor was disposed before the swipe settled
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.setBatchingEnabled(true);
+        }
+    }
+
+    /**
+     * Returns the {@link ResetGestureInputConsumer} if user is unlocked, else NO_OP.
+     */
+    private @NonNull InputConsumer getDefaultInputConsumer() {
+        if (mResetGestureInputConsumer != null) {
+            return mResetGestureInputConsumer;
+        } else {
+            // mResetGestureInputConsumer isn't initialized until onUserUnlocked(), so reset to
+            // NO_OP until then (we never want these to be null).
+            return InputConsumer.NO_OP;
+        }
     }
 
     private void preloadOverview(boolean fromInit) {
@@ -772,13 +861,7 @@
                 mOverviewComponentObserver.getActivityInterface();
         final Intent overviewIntent = new Intent(
                 mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
-        if (activityInterface.getCreatedActivity() == null) {
-            // Make sure that UI states will be initialized.
-            activityInterface.createActivityInitListener((wasVisible) -> {
-                AppLaunchTracker.INSTANCE.get(TouchInteractionService.this);
-                return false;
-            }).register(overviewIntent);
-        } else if (fromInit) {
+        if (activityInterface.getCreatedActivity() != null && fromInit) {
             // The activity has been created before the initialization of overview service. It is
             // usually happens when booting or launcher is the top activity, so we should already
             // have the latest state.
@@ -802,6 +885,13 @@
         }
         if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(),
                 activity.getResources().getConfiguration().diff(newConfig))) {
+            // Since navBar gestural height are different between portrait and landscape,
+            // can handle orientation changes and refresh navigation gestural region through
+            // onOneHandedModeChanged()
+            int newGesturalHeight = ResourceUtils.getNavbarSize(
+                    ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
+                    getApplicationContext().getResources());
+            mDeviceState.onOneHandedModeChanged(newGesturalHeight);
             return;
         }
 
@@ -811,10 +901,10 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
         if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) {
-            ArgList args = new ArgList(Arrays.asList(rawArgs));
-            switch (args.nextArg()) {
+            LinkedList<String> args = new LinkedList(Arrays.asList(rawArgs));
+            switch (args.pollFirst()) {
                 case "cmd":
-                    if (args.peekArg() == null) {
+                    if (args.peekFirst() == null) {
                         printAvailableCommands(pw);
                     } else {
                         onCommand(pw, args);
@@ -834,6 +924,9 @@
             if (mGestureState != null) {
                 mGestureState.dump(pw);
             }
+            pw.println("Input state:");
+            pw.println("  mInputMonitorCompat=" + mInputMonitorCompat);
+            pw.println("  mInputEventReceiver=" + mInputEventReceiver);
             SysUINavigationMode.INSTANCE.get(this).dump(pw);
             pw.println("TouchState:");
             BaseDraggingActivity createdOverviewActivity = mOverviewComponentObserver == null ? null
@@ -845,8 +938,7 @@
             pw.println("  mConsumer=" + mConsumer.getName());
             ActiveGestureLog.INSTANCE.dump("", pw);
             pw.println("ProtoTrace:");
-            pw.println("  file="
-                    + ProtoTracer.INSTANCE.get(TouchInteractionService.this).getTraceFile());
+            pw.println("  file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
         }
     }
 
@@ -855,24 +947,26 @@
         pw.println("  clear-touch-log: Clears the touch interaction log");
     }
 
-    private void onCommand(PrintWriter pw, ArgList args) {
-        switch (args.nextArg()) {
+    private void onCommand(PrintWriter pw, LinkedList<String> args) {
+        switch (args.pollFirst()) {
             case "clear-touch-log":
                 ActiveGestureLog.INSTANCE.clear();
                 break;
         }
     }
 
-    private BaseSwipeUpHandler createLauncherSwipeHandler(
-            GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
+    private AbsSwipeUpHandler createLauncherSwipeHandler(
+            GestureState gestureState, long touchTimeMs) {
         return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
-                gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
+                gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+                mInputConsumer);
     }
 
-    private BaseSwipeUpHandler createFallbackSwipeHandler(
-            GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
+    private AbsSwipeUpHandler createFallbackSwipeHandler(
+            GestureState gestureState, long touchTimeMs) {
         return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
-                gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
+                gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+                mInputConsumer);
     }
 
     protected boolean shouldNotifyBackGesture() {
@@ -902,11 +996,16 @@
     }
 
     @Override
-    public void writeToProto(LauncherTraceProto proto) {
-        if (proto.touchInteractionService == null) {
-            proto.touchInteractionService = new TouchInteractionServiceProto();
+    public void writeToProto(LauncherTraceProto.Builder proto) {
+        TouchInteractionServiceProto.Builder serviceProto =
+            TouchInteractionServiceProto.newBuilder();
+        serviceProto.setServiceConnected(true);
+
+        if (mOverviewComponentObserver != null) {
+            mOverviewComponentObserver.writeToProto(serviceProto);
         }
-        proto.touchInteractionService.serviceConnected = true;
-        proto.touchInteractionService.serviceConnected = true;
+        mConsumer.writeToProto(serviceProto);
+
+        proto.setTouchInteractionService(serviceProto);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
new file mode 100644
index 0000000..184ab17
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+import android.os.Handler;
+import android.view.View;
+
+import com.android.launcher3.Utilities;
+import com.android.systemui.shared.system.ViewRootImplCompat;
+
+import java.util.function.BooleanSupplier;
+import java.util.function.LongConsumer;
+
+/**
+ * Utility class for helpful methods related to {@link View} objects.
+ */
+public class ViewUtils {
+
+    /** See {@link #postFrameDrawn(View, Runnable, BooleanSupplier)}} */
+    public static boolean postFrameDrawn(View view, Runnable onFinishRunnable) {
+        return postFrameDrawn(view, onFinishRunnable, () -> false);
+    }
+
+    /**
+     * Inject some addition logic in order to make sure that the view is updated smoothly post
+     * draw, and allow addition task to be run after view update.
+     *
+     * @param onFinishRunnable runnable to be run right after the view finishes drawing.
+     */
+    public static boolean postFrameDrawn(
+            View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
+        return new FrameHandler(view, onFinishRunnable, canceled).schedule();
+    }
+
+    private static class FrameHandler implements LongConsumer {
+
+        final ViewRootImplCompat mViewRoot;
+        final Runnable mFinishCallback;
+        final BooleanSupplier mCancelled;
+        final Handler mHandler;
+
+        int mDeferFrameCount = 1;
+
+        FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
+            mViewRoot = new ViewRootImplCompat(view);
+            mFinishCallback = finishCallback;
+            mCancelled = cancelled;
+            mHandler = new Handler();
+        }
+
+        @Override
+        public void accept(long l) {
+            Utilities.postAsyncCallback(mHandler, this::onFrame);
+        }
+
+        private void onFrame() {
+            if (mCancelled.getAsBoolean()) {
+                return;
+            }
+
+            if (mDeferFrameCount > 0) {
+                mDeferFrameCount--;
+                schedule();
+                return;
+            }
+
+            if (mFinishCallback != null) {
+                mFinishCallback.run();
+            }
+        }
+
+        private boolean schedule() {
+            if (mViewRoot.isValid()) {
+                mViewRoot.registerRtFrameCallback(this);
+                mViewRoot.getView().invalidate();
+                return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
rename to quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
index be3fdde..9d9ef94 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
@@ -21,7 +21,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.SysUINavigationMode;
@@ -43,7 +43,7 @@
         SysUINavigationMode.Mode sysUINavigationMode = SysUINavigationMode.getMode(mActivity);
         if (sysUINavigationMode == SysUINavigationMode.Mode.NO_BUTTON) {
             NavBarPosition navBarPosition = new NavBarPosition(sysUINavigationMode,
-                    DefaultDisplay.INSTANCE.get(mActivity).getInfo());
+                    DisplayController.INSTANCE.get(mActivity).getInfo());
             mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity,
                     true /* disableHorizontalSwipe */, navBarPosition,
                     null /* onInterceptTouch */, this);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
similarity index 79%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
rename to quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 24a7610..f0364eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -20,14 +20,15 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+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_SECONDARY_TRANSLATION;
+import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
 
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
@@ -61,15 +62,11 @@
     @Override
     public void setStateWithAnimation(RecentsState toState, StateAnimationConfig config,
             PendingAnimation setter) {
-        if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
-            // The entire recents animation is played atomically.
-            return;
-        }
         if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
             return;
         }
         // While animating into recents, update the visible task data as needed
-        setter.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
+        setter.addOnFrameCallback(() -> mRecentsView.loadVisibleTaskData(FLAG_UPDATE_ALL));
         mRecentsView.updateEmptyMessage();
 
         setProperties(toState, config, setter);
@@ -77,16 +74,19 @@
 
     private void setProperties(RecentsState state, StateAnimationConfig config,
             PropertySetter setter) {
-        float buttonAlpha = state.hasButtons() ? 1 : 0;
+        float clearAllButtonAlpha = state.hasClearAllButton() ? 1 : 0;
         setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
-                buttonAlpha, LINEAR);
+                clearAllButtonAlpha, LINEAR);
+        float overviewButtonAlpha =
+                state.hasOverviewActions() && mRecentsView.shouldShowOverviewActionsForState(state)
+                        ? 1 : 0;
         setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
-                MultiValueAlpha.VALUE, buttonAlpha, LINEAR);
+                MultiValueAlpha.VALUE, overviewButtonAlpha, LINEAR);
 
         float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
         setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
                 config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
-        setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
+        setter.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1],
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
@@ -94,5 +94,10 @@
         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);
+
+        setter.setViewBackgroundColor(mActivity.getScrimView(), state.getScrimColor(mActivity),
+                config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
similarity index 73%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
rename to quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index ffe9d6a..ac3fb27 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,21 +15,31 @@
  */
 package com.android.quickstep.fallback;
 
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 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 android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.util.Log;
 
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.quickstep.FallbackActivityInterface;
+import com.android.quickstep.GestureState;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.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;
@@ -37,7 +47,7 @@
 import java.util.ArrayList;
 
 @TargetApi(Build.VERSION_CODES.R)
-public class FallbackRecentsView extends RecentsView<RecentsActivity>
+public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
         implements StateListener<RecentsState> {
 
     private RunningTaskInfo mHomeTaskInfo;
@@ -52,8 +62,8 @@
     }
 
     @Override
-    public void init(OverviewActionsView actionsView) {
-        super.init(actionsView);
+    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+        super.init(actionsView, splitPlaceholderView);
         setOverviewStateEnabled(true);
         setOverlayEnabled(true);
     }
@@ -63,12 +73,6 @@
         mActivity.startHome();
     }
 
-    @Override
-    public boolean shouldUseMultiWindowTaskSizeStrategy() {
-        // Just use the activity task size for multi-window as well.
-        return false;
-    }
-
     /**
      * When starting gesture interaction from home, we add a temporary invisible tile corresponding
      * to the home task. This allows us to handle quick-switch similarly to a quick-switching
@@ -80,24 +84,36 @@
     }
 
     /**
-     * When the gesture ends and recents view become interactive, we also remove the temporary
+     * When the gesture ends and we're going to recents view, we also remove the temporary
      * invisible tile added for the home task. This also pushes the remaining tiles back
      * to the center.
      */
     @Override
-    public void onGestureAnimationEnd() {
-        super.onGestureAnimationEnd();
-        if (mHomeTaskInfo != null) {
+    public void onPrepareGestureEndAnimation(
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+        super.onPrepareGestureEndAnimation(animatorSet, endTarget);
+        if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
             TaskView tv = getTaskView(mHomeTaskInfo.taskId);
             if (tv != null) {
                 PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
                 pa.addEndListener(e -> setCurrentTask(-1));
-                runDismissAnimation(pa);
+                AnimatorPlaybackController controller = pa.createPlaybackController();
+                controller.dispatchOnStart();
+                animatorSet.play(controller.getAnimationPlayer());
             }
         }
     }
 
     @Override
+    public void onGestureAnimationEnd() {
+        if (mCurrentGestureEndTarget == GestureState.GestureEndTarget.HOME) {
+            // Clean-up logic that occurs when recents is no longer in use/visible.
+            reset();
+        }
+        super.onGestureAnimationEnd();
+    }
+
+    @Override
     public void setCurrentTask(int runningTaskId) {
         super.setCurrentTask(runningTaskId);
         if (mHomeTaskInfo != null && mHomeTaskInfo.taskId != runningTaskId) {
@@ -107,20 +123,20 @@
     }
 
     @Override
-    protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
+    protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
         if (mHomeTaskInfo != null && runningTaskInfo != null &&
                 mHomeTaskInfo.taskId == runningTaskInfo.taskId
                 && getTaskViewCount() == 0) {
-            // Do not add a dummy task if we are running over home with empty recents, so that we
-            // show the empty recents message instead of showing a dummy task and later removing it.
+            // 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.shouldAddDummyTaskView(runningTaskInfo);
+        return super.shouldAddStubTaskView(runningTaskInfo);
     }
 
     @Override
     protected void applyLoadPlan(ArrayList<Task> tasks) {
-        // When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher
+        // 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()) {
@@ -166,11 +182,17 @@
     @Override
     public void onStateTransitionStart(RecentsState toState) {
         setOverviewStateEnabled(true);
+        setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
+        setOverviewFullscreenEnabled(toState.isFullScreen());
         setFreezeViewVisibility(true);
     }
 
     @Override
     public void onStateTransitionComplete(RecentsState finalState) {
+        if (finalState == HOME) {
+            // Clean-up logic that occurs when recents is no longer in use/visible.
+            reset();
+        }
         setOverlayEnabled(finalState == DEFAULT || finalState == MODAL_TASK);
         setFreezeViewVisibility(false);
     }
@@ -180,7 +202,7 @@
         super.setOverviewStateEnabled(enabled);
         if (enabled) {
             RecentsState state = mActivity.getStateManager().getState();
-            setDisallowScrollToClearAll(!state.hasButtons());
+            setDisallowScrollToClearAll(!state.hasClearAllButton());
         }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsDragLayer.java b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
similarity index 78%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsDragLayer.java
rename to quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
index a00015a..29c3dc8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsDragLayer.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
@@ -16,11 +16,8 @@
 package com.android.quickstep.fallback;
 
 import android.content.Context;
-import android.graphics.Rect;
 import android.util.AttributeSet;
 
-import com.android.launcher3.R;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.RecentsActivity;
@@ -41,12 +38,4 @@
                 new FallbackNavBarTouchController(mActivity),
         };
     }
-
-    @Override
-    public void setInsets(Rect insets) {
-        super.setInsets(insets);
-        setBackground(insets.top == 0  || !mAllowSysuiScrims
-                ? null
-                : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
new file mode 100644
index 0000000..b6cfdce
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -0,0 +1,176 @@
+/*
+ * 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.fallback;
+
+import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
+import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState;
+
+import android.content.Context;
+import android.graphics.Color;
+
+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;
+
+/**
+ * State definition for Fallback recents
+ */
+public class RecentsState implements BaseState<RecentsState> {
+
+    private static final int FLAG_MODAL = BaseState.getFlag(0);
+    private static final int FLAG_CLEAR_ALL_BUTTON = BaseState.getFlag(1);
+    private static final int FLAG_FULL_SCREEN = BaseState.getFlag(2);
+    private static final int FLAG_OVERVIEW_ACTIONS = BaseState.getFlag(3);
+    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);
+
+    public static final RecentsState DEFAULT = new RecentsState(0,
+            FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID | FLAG_SCRIM
+                    | FLAG_LIVE_TILE);
+    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);
+    public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
+            FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN);
+    public static final RecentsState HOME = new RecentsState(3, 0);
+    public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
+
+    public final int ordinal;
+    private final int mFlags;
+
+    private static final float NO_OFFSET = 0;
+    private static final float NO_SCALE = 1;
+
+    public RecentsState(int id, int flags) {
+        this.ordinal = id;
+        this.mFlags = flags;
+    }
+
+
+    @Override
+    public String toString() {
+        return "Ordinal-" + ordinal;
+    }
+
+    @Override
+    public final boolean hasFlag(int mask) {
+        return (mFlags & mask) != 0;
+    }
+
+    @Override
+    public int getTransitionDuration(Context context) {
+        return 250;
+    }
+
+    @Override
+    public RecentsState getHistoryForState(RecentsState previousState) {
+        return DEFAULT;
+    }
+
+    /**
+     * For this state, how modal should over view been shown. 0 modalness means all tasks drawn,
+     * 1 modalness means the current task is show on its own.
+     */
+    public float getOverviewModalness() {
+        return hasFlag(FLAG_MODAL) ? 1 : 0;
+    }
+
+    public boolean isFullScreen() {
+        return hasFlag(FLAG_FULL_SCREEN);
+    }
+
+    /**
+     * For this state, whether clear all button should be shown.
+     */
+    public boolean hasClearAllButton() {
+        return hasFlag(FLAG_CLEAR_ALL_BUTTON);
+    }
+
+    /**
+     * For this state, whether overview actions should be shown.
+     */
+    public boolean hasOverviewActions() {
+        return hasFlag(FLAG_OVERVIEW_ACTIONS);
+    }
+
+    /**
+     * For this state, whether live tile should be shown.
+     */
+    public boolean hasLiveTile() {
+        return hasFlag(FLAG_LIVE_TILE);
+    }
+
+    /**
+     * For this state, what color scrim should be drawn behind overview.
+     */
+    public int getScrimColor(RecentsActivity activity) {
+        return hasFlag(FLAG_SCRIM) ? Themes.getAttrColor(activity, R.attr.overviewScrimColor)
+                : Color.TRANSPARENT;
+    }
+
+    public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+        return new float[] { NO_SCALE, NO_OFFSET };
+    }
+
+    /**
+     * 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);
+    }
+
+    private boolean showAsGrid(DeviceProfile deviceProfile) {
+        return deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
+    }
+
+    private static class ModalState extends RecentsState {
+
+        public ModalState(int id, int flags) {
+            super(id, flags);
+        }
+
+        @Override
+        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+            return getOverviewScaleAndOffsetForModalState(activity);
+        }
+    }
+
+    private static class BackgroundAppState extends RecentsState {
+        public BackgroundAppState(int id, int flags) {
+            super(id, flags);
+        }
+
+        @Override
+        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+            return getOverviewScaleAndOffsetForBackgroundState(activity);
+        }
+    }
+
+    private static class LauncherState extends RecentsState {
+        LauncherState(int id, int flags) {
+            super(id, flags);
+        }
+
+        @Override
+        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+            return new float[] { NO_SCALE, 1 };
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
similarity index 82%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
rename to quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
index d7458d2..eca61bb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep.fallback;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+
 import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.quickstep.RecentsActivity;
 
@@ -26,7 +28,8 @@
 
     @Override
     protected boolean isRecentsInteractive() {
-        return mActivity.hasWindowFocus();
+        return mActivity.hasWindowFocus() || (ENABLE_QUICKSTEP_LIVE_TILE.get()
+                && mActivity.getStateManager().getState().hasLiveTile());
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
similarity index 82%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 89e6931..510820a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -25,13 +25,9 @@
 import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction.UPLEFT;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction.UPRIGHT;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.FLING;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE_NOOP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.NAVBAR;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
@@ -47,13 +43,15 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.SystemUiProxy;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
+import java.util.function.Consumer;
+
 /**
  * Touch consumer for handling events to launch assistant from launcher
  */
@@ -80,7 +78,6 @@
     private float mTimeFraction;
     private long mDragTime;
     private float mLastProgress;
-    private int mDirection;
     private BaseActivityInterface mActivityInterface;
 
     private final float mDragDistThreshold;
@@ -89,19 +86,18 @@
     private final int mAngleThreshold;
     private final float mSquaredSlop;
     private final Context mContext;
-    private final GestureDetector mGestureDetector;
-    private final boolean mIsAssistGestureConstrained;
+    private final Consumer<MotionEvent> mGestureDetector;
 
     public AssistantInputConsumer(
             Context context,
             GestureState gestureState,
             InputConsumer delegate,
             InputMonitorCompat inputMonitor,
-            boolean isAssistGestureConstrained) {
+            RecentsAnimationDeviceState deviceState,
+            MotionEvent startEvent) {
         super(delegate, inputMonitor);
         final Resources res = context.getResources();
         mContext = context;
-        mIsAssistGestureConstrained = isAssistGestureConstrained;
         mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
         mFlingDistThreshold = res.getDimension(R.dimen.gestures_assistant_fling_threshold);
         mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
@@ -112,7 +108,11 @@
         mSquaredSlop = slop * slop;
         mActivityInterface = gestureState.getActivityInterface();
 
-        mGestureDetector = new GestureDetector(context, new AssistantGestureListener());
+        boolean flingDisabled = deviceState.isAssistantGestureIsConstrained()
+                || deviceState.isInDeferredGestureRegion(startEvent);
+        mGestureDetector = flingDisabled
+                ? ev -> { }
+                : new GestureDetector(context, new AssistantGestureListener())::onTouchEvent;
     }
 
     @Override
@@ -197,12 +197,18 @@
                 if (mState != STATE_DELEGATE_ACTIVE && !mLaunchedAssistant) {
                     ValueAnimator animator = ValueAnimator.ofFloat(mLastProgress, 0)
                         .setDuration(RETRACT_ANIMATION_DURATION_MS);
-                    UserEventDispatcher.newInstance(mContext).logActionOnContainer(
-                        SWIPE_NOOP, mDirection, NAVBAR);
                     animator.addUpdateListener(valueAnimator -> {
                         float progress = (float) valueAnimator.getAnimatedValue();
                         SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(progress);
                     });
+                    // Ensure that we always send a zero at the end to clear the invocation state.
+                    animator.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            super.onAnimationEnd(animation);
+                            SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(0f);
+                        }
+                    });
                     animator.setInterpolator(Interpolators.DEACCEL_2);
                     animator.start();
                 }
@@ -211,7 +217,7 @@
                 break;
         }
 
-        mGestureDetector.onTouchEvent(ev);
+        mGestureDetector.accept(ev);
 
         if (mState != STATE_ACTIVE) {
             mDelegate.onMotionEvent(ev);
@@ -223,29 +229,26 @@
             mLastProgress = Math.min(mDistance * 1f / mDragDistThreshold, 1) * mTimeFraction;
             if (mDistance >= mDragDistThreshold && mTimeFraction >= 1) {
                 SystemUiProxy.INSTANCE.get(mContext).onAssistantGestureCompletion(0);
-                startAssistantInternal(SWIPE);
-
-                Bundle args = new Bundle();
-                args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE);
-                args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
-                SystemUiProxy.INSTANCE.get(mContext).startAssistant(args);
-                mLaunchedAssistant = true;
+                startAssistantInternal();
             } else {
                 SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(mLastProgress);
             }
         }
     }
 
-    private void startAssistantInternal(int gestureType) {
-        UserEventDispatcher.newInstance(mContext)
-            .logActionOnContainer(gestureType, mDirection, NAVBAR);
-
+    private void startAssistantInternal() {
         BaseDraggingActivity launcherActivity = mActivityInterface.getCreatedActivity();
         if (launcherActivity != null) {
             launcherActivity.getRootView().performHapticFeedback(
                 13, // HapticFeedbackConstants.GESTURE_END
                 HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
         }
+
+        Bundle args = new Bundle();
+        args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE);
+        args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
+        SystemUiProxy.INSTANCE.get(mContext).startAssistant(args);
+        mLaunchedAssistant = true;
     }
 
     /**
@@ -253,7 +256,6 @@
      */
     private boolean isValidAssistantGestureAngle(float deltaX, float deltaY) {
         float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
-        mDirection = angle > 90 ? UPLEFT : UPRIGHT;
 
         // normalize so that angle is measured clockwise from horizontal in the bottom right corner
         // and counterclockwise from horizontal in the bottom left corner
@@ -264,20 +266,14 @@
     private class AssistantGestureListener extends SimpleOnGestureListener {
         @Override
         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-            if (!mIsAssistGestureConstrained
-                && isValidAssistantGestureAngle(velocityX, -velocityY)
+            if (isValidAssistantGestureAngle(velocityX, -velocityY)
                     && mDistance >= mFlingDistThreshold
                     && !mLaunchedAssistant
                     && mState != STATE_DELEGATE_ACTIVE) {
                 mLastProgress = 1;
                 SystemUiProxy.INSTANCE.get(mContext).onAssistantGestureCompletion(
                     (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY));
-                startAssistantInternal(FLING);
-
-                Bundle args = new Bundle();
-                args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
-                SystemUiProxy.INSTANCE.get(mContext).startAssistant(args);
-                mLaunchedAssistant = true;
+                startAssistantInternal();
             }
             return true;
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
similarity index 88%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 67a15a7..8da2fd3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -4,6 +4,7 @@
 
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.tracing.InputConsumerProto;
 import com.android.quickstep.InputConsumer;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -53,4 +54,9 @@
         mDelegate.onMotionEvent(event);
         event.recycle();
     }
+
+    @Override
+    public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
+        mDelegate.writeToProtoInternal(inputConsumerProto);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
similarity index 93%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index a676390..fcc0217 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -19,9 +19,11 @@
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.launcher3.Utilities.createHomeIntent;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
+import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
@@ -35,13 +37,12 @@
 import android.graphics.PointF;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
 
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
@@ -60,7 +61,7 @@
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
 
 /**
- * A dummy input consumer used when the device is still locked, e.g. from secure camera.
+ * A placeholder input consumer used when the device is still locked, e.g. from secure camera.
  */
 public class DeviceLockedInputConsumer implements InputConsumer,
         RecentsAnimationCallbacks.RecentsAnimationListener, BuilderProxy {
@@ -115,7 +116,7 @@
                 R.dimen.device_locked_y_offset);
 
         // Do not use DeviceProfile as the user data might be locked
-        mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
+        mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
 
         // Init states
         mStateCallback = new MultiStateCallback(STATE_NAMES);
@@ -180,12 +181,11 @@
      */
     private void finishTouchTracking(MotionEvent ev) {
         if (mThresholdCrossed && ev.getAction() == ACTION_UP) {
-            mVelocityTracker.computeCurrentVelocity(1000,
-                    ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
+            mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
 
             float velocityY = mVelocityTracker.getYVelocity();
             float flingThreshold = mContext.getResources()
-                    .getDimension(R.dimen.quickstep_fling_threshold_velocity);
+                    .getDimension(R.dimen.quickstep_fling_threshold_speed);
 
             boolean dismissTask;
             if (Math.abs(velocityY) > flingThreshold) {
@@ -204,9 +204,7 @@
                 public void onAnimationEnd(Animator animation) {
                     if (dismissTask) {
                         // For now, just start the home intent so user is prompted to unlock the device.
-                        mContext.startActivity(new Intent(Intent.ACTION_MAIN)
-                                .addCategory(Intent.CATEGORY_HOME)
-                                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+                        mContext.startActivity(createHomeIntent());
                         mHomeLaunched = true;
                     }
                     mStateCallback.setState(STATE_HANDLER_INVALIDATED);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
new file mode 100644
index 0000000..bc20902
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -0,0 +1,179 @@
+/*
+ * 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_UP;
+
+import static com.android.launcher3.ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE;
+import static com.android.launcher3.Utilities.squaredHypot;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Touch consumer for handling gesture event to launch one handed
+ * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode
+ */
+public class OneHandedModeInputConsumer extends DelegateInputConsumer {
+
+    private static final int ANGLE_MAX = 150;
+    private static final int ANGLE_MIN = 30;
+
+    private final Context mContext;
+    private final Point mDisplaySize;
+    private final RecentsAnimationDeviceState mDeviceState;
+
+    private final float mDragDistThreshold;
+    private final float mSquaredSlop;
+
+    private final int mNavBarSize;
+
+    private final PointF mDownPos = new PointF();
+    private final PointF mLastPos = new PointF();
+
+    private boolean mPassedSlop;
+    private boolean mIsStopGesture;
+
+    public OneHandedModeInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
+            InputConsumer delegate, InputMonitorCompat inputMonitor) {
+        super(delegate, inputMonitor);
+        mContext = context;
+        mDeviceState = deviceState;
+        mDragDistThreshold = context.getResources().getDimensionPixelSize(
+                R.dimen.gestures_onehanded_drag_threshold);
+        mSquaredSlop = Utilities.squaredTouchSlop(context);
+        mDisplaySize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
+        mNavBarSize = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE,
+                mContext.getResources());
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_ONE_HANDED | mDelegate.getType();
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        switch (ev.getActionMasked()) {
+            case ACTION_DOWN: {
+                mDownPos.set(ev.getX(), ev.getY());
+                mLastPos.set(mDownPos);
+                break;
+            }
+            case ACTION_MOVE: {
+                if (mState == STATE_DELEGATE_ACTIVE) {
+                    break;
+                }
+                if (!mDelegate.allowInterceptByParent()) {
+                    mState = STATE_DELEGATE_ACTIVE;
+                    break;
+                }
+
+                mLastPos.set(ev.getX(), ev.getY());
+                if (!mPassedSlop) {
+                    if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
+                            > mSquaredSlop) {
+                        if ((!mDeviceState.isOneHandedModeActive() && isValidStartAngle(
+                                mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y))
+                                || (mDeviceState.isOneHandedModeActive() && isValidExitAngle(
+                                mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y))) {
+                            // To avoid mis-trigger when motion not touch system gesture region.
+                            mPassedSlop = isInSystemGestureRegion(mLastPos);
+                            setActive(ev);
+                        } else {
+                            mState = STATE_DELEGATE_ACTIVE;
+                        }
+                    }
+                } else {
+                    float distance = (float) Math.hypot(mLastPos.x - mDownPos.x,
+                            mLastPos.y - mDownPos.y);
+                    if (distance > mDragDistThreshold && mPassedSlop) {
+                        mIsStopGesture = true;
+                    }
+                }
+                break;
+            }
+            case ACTION_UP: {
+                if (mLastPos.y >= mDownPos.y && mPassedSlop) {
+                    onStartGestureDetected();
+                } else if (mIsStopGesture) {
+                    onStopGestureDetected();
+                }
+                clearState();
+                break;
+            }
+            case ACTION_CANCEL:
+                clearState();
+                break;
+        }
+
+        if (mState != STATE_ACTIVE) {
+            mDelegate.onMotionEvent(ev);
+        }
+    }
+
+    private void clearState() {
+        mPassedSlop = false;
+        mState = STATE_INACTIVE;
+        mIsStopGesture = false;
+    }
+
+    private void onStartGestureDetected() {
+        if (mDeviceState.isSwipeToNotificationEnabled()) {
+            SystemUiProxy.INSTANCE.get(mContext).expandNotificationPanel();
+        } else if (!mDeviceState.isOneHandedModeActive()) {
+            SystemUiProxy.INSTANCE.get(mContext).startOneHandedMode();
+        }
+    }
+
+    private void onStopGestureDetected() {
+        if (!mDeviceState.isOneHandedModeEnabled() || !mDeviceState.isOneHandedModeActive()) {
+            return;
+        }
+
+        SystemUiProxy.INSTANCE.get(mContext).stopOneHandedMode();
+    }
+
+    private boolean isInSystemGestureRegion(PointF lastPos) {
+        final int navBarUpperBound = mDisplaySize.y - mNavBarSize;
+        return mDeviceState.isGesturalNavMode() && lastPos.y > navBarUpperBound;
+    }
+
+    private boolean isValidStartAngle(float deltaX, float deltaY) {
+        final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
+        return angle > -(ANGLE_MAX) && angle < -(ANGLE_MIN);
+    }
+
+    private boolean isValidExitAngle(float deltaX, float deltaY) {
+        final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
+        return angle > ANGLE_MIN && angle < ANGLE_MAX;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
similarity index 89%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 6259f1f..725c7c4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -28,19 +28,21 @@
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 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 static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.graphics.PointF;
+import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
+import android.view.Display;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
@@ -50,22 +52,25 @@
 import com.android.launcher3.R;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.tracing.InputConsumerProto;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.AbsSwipeUpHandler;
+import com.android.quickstep.AbsSwipeUpHandler.Factory;
 import com.android.quickstep.BaseActivityInterface;
-import com.android.quickstep.BaseSwipeUpHandler;
-import com.android.quickstep.BaseSwipeUpHandler.Factory;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.RotationTouchHelper;
 import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
 import java.util.function.Consumer;
@@ -91,9 +96,10 @@
     private RecentsAnimationCallbacks mActiveCallbacks;
     private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
     private final InputMonitorCompat mInputMonitorCompat;
+    private final InputEventReceiver mInputEventReceiver;
     private final BaseActivityInterface mActivityInterface;
 
-    private final BaseSwipeUpHandler.Factory mHandlerFactory;
+    private final AbsSwipeUpHandler.Factory mHandlerFactory;
 
     private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
     private final MotionPauseDetector mMotionPauseDetector;
@@ -101,13 +107,15 @@
 
     private VelocityTracker mVelocityTracker;
 
-    private BaseSwipeUpHandler mInteractionHandler;
+    private AbsSwipeUpHandler mInteractionHandler;
 
     private final boolean mIsDeferredDownTarget;
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
     private int mActivePointerId = INVALID_POINTER_ID;
 
+    private int mLastRotation = -1;
+
     // Distance after which we start dragging the window.
     private final float mTouchSlop;
 
@@ -125,6 +133,8 @@
     // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
     private float mStartDisplacement;
 
+    private final DisplayManager mDisplayManager;
+
     private Handler mMainThreadHandler;
     private Runnable mCancelRecentsAnimationRunnable = () -> {
         ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
@@ -134,8 +144,8 @@
     public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
             TaskAnimationManager taskAnimationManager, GestureState gestureState,
             boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
-            InputMonitorCompat inputMonitorCompat, boolean disableHorizontalSwipe,
-            Factory handlerFactory) {
+            InputMonitorCompat inputMonitorCompat, InputEventReceiver inputEventReceiver,
+            boolean disableHorizontalSwipe, Factory handlerFactory) {
         super(base);
         mDeviceState = deviceState;
         mNavBarPosition = mDeviceState.getNavBarPosition();
@@ -153,6 +163,7 @@
         mOnCompleteCallback = onCompleteCallback;
         mVelocityTracker = VelocityTracker.obtain();
         mInputMonitorCompat = inputMonitorCompat;
+        mInputEventReceiver = inputEventReceiver;
 
         boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
         mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
@@ -166,6 +177,7 @@
         mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
         mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
         mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
+        mDisplayManager = getSystemService(DisplayManager.class);
     }
 
     @Override
@@ -191,6 +203,17 @@
             return;
         }
 
+        if (TouchInteractionService.ENABLE_PER_WINDOW_INPUT_ROTATION) {
+            final Display display = mDisplayManager.getDisplay(mDeviceState.getDisplayId());
+            final int rotation = display.getRotation();
+            if (rotation != mLastRotation) {
+                // If rotation changes, reset tracking to avoid degenerate velocities.
+                mLastPos.set(ev.getX(), ev.getY());
+                mVelocityTracker.clear();
+                mLastRotation = rotation;
+            }
+        }
+
         // Proxy events to recents view
         if (mPassedWindowMoveSlop && mInteractionHandler != null
                 && !mRecentsViewDispatcher.hasConsumer()) {
@@ -214,6 +237,9 @@
 
         switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
+                // Until we detect the gesture, handle events as we receive them
+                mInputEventReceiver.setBatchingEnabled(false);
+
                 Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT,
                         FLAG_CHECK_FOR_RACE_CONDITIONS);
                 mActivePointerId = ev.getPointerId(0);
@@ -350,10 +376,8 @@
         }
         TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
         mInputMonitorCompat.pilferPointers();
-
-        mActivityInterface.closeOverlay();
-        ActivityManagerWrapper.getInstance().closeSystemWindows(
-                CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        // Once we detect the gesture, we can enable batching to reduce further updates
+        mInputEventReceiver.setBatchingEnabled(true);
 
         // Notify the handler that the gesture has actually started
         mInteractionHandler.onGestureStarted(isLikelyToStartNewTask);
@@ -362,12 +386,10 @@
     private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
         ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
 
-        mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
-                mTaskAnimationManager.isRecentsAnimationRunning());
+        mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
         mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
-        mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
-        Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
-        mInteractionHandler.initWhenReady(intent);
+        mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
+        mInteractionHandler.initWhenReady();
 
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
             mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
@@ -375,6 +397,7 @@
             mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
             notifyGestureStarted(true /*isLikelyToStartNewTask*/);
         } else {
+            Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
             intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
             mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
                     mInteractionHandler);
@@ -393,8 +416,7 @@
             if (ev.getActionMasked() == ACTION_CANCEL) {
                 mInteractionHandler.onGestureCancelled();
             } else {
-                mVelocityTracker.computeCurrentVelocity(1000,
-                        ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+                mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
                 float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
                 float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
                 float velocity = mNavBarPosition.isRightEdge()
@@ -470,4 +492,11 @@
     public boolean allowInterceptByParent() {
         return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
     }
+
+    @Override
+    public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
+        if (mInteractionHandler != null) {
+            mInteractionHandler.writeToProto(inputConsumerProto);
+        }
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
similarity index 80%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 9bfe84f..b0df286 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -18,13 +18,15 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
-import android.util.Log;
+import android.media.AudioManager;
+import android.media.session.MediaSessionManager;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
@@ -32,14 +34,14 @@
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
+import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.ActiveGestureLog;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
 /**
  * Input consumer for handling touch on the recents/Launcher activity.
  */
-public class OverviewInputConsumer<T extends StatefulActivity<?>>
+public class OverviewInputConsumer<S extends BaseState<S>, T extends StatefulActivity<S>>
         implements InputConsumer {
 
     private final T mActivity;
@@ -80,9 +82,6 @@
             ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
         }
         ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "OverviewInputConsumer");
-        }
         boolean handled = mTarget.proxyTouchEvent(ev, mStartingInActivityBounds);
         ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
         ev.setEdgeFlags(flags);
@@ -91,8 +90,7 @@
             mTargetHandledTouch = true;
             if (!mStartingInActivityBounds) {
                 mActivityInterface.closeOverlay();
-                ActivityManagerWrapper.getInstance()
-                        .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+                TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
                 ActiveGestureLog.INSTANCE.addLog("startQuickstep");
             }
             if (mInputMonitor != null) {
@@ -105,6 +103,17 @@
     @Override
     public void onKeyEvent(KeyEvent ev) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            switch (ev.getKeyCode()) {
+                case KeyEvent.KEYCODE_VOLUME_DOWN:
+                case KeyEvent.KEYCODE_VOLUME_UP:
+                case KeyEvent.KEYCODE_VOLUME_MUTE:
+                    MediaSessionManager mgr = mActivity.getSystemService(MediaSessionManager.class);
+                    mgr.dispatchVolumeKeyEventAsSystemService(ev,
+                            AudioManager.USE_DEFAULT_STREAM_TYPE);
+                    break;
+                default:
+                    break;
+            }
             mActivity.dispatchKeyEvent(ev);
         }
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
similarity index 79%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 1c77a05..864e08d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -15,9 +15,12 @@
  */
 package com.android.quickstep.inputconsumers;
 
+import static com.android.launcher3.Utilities.createHomeIntent;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
 
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.graphics.PointF;
 import android.view.MotionEvent;
@@ -27,9 +30,6 @@
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationDeviceState;
@@ -79,20 +79,19 @@
 
     @Override
     public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
-        mContext.startActivity(mGestureState.getHomeIntent());
+        try {
+            mContext.startActivity(mGestureState.getHomeIntent());
+        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+            mContext.startActivity(createHomeIntent());
+        }
         ActiveGestureLog.INSTANCE.addLog("startQuickstep");
         BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
-        int pageIndex = -1; // This number doesn't reflect workspace page index.
-                            // It only indicates that launcher client screen was shown.
-        int containerType = (mGestureState != null && mGestureState.getEndTarget() != null)
+        int state = (mGestureState != null && mGestureState.getEndTarget() != null)
                 ? mGestureState.getEndTarget().containerType
-                : LauncherLogProto.ContainerType.WORKSPACE;
-        activity.getUserEventDispatcher().logActionOnContainer(
-                wasFling ? Touch.FLING : Touch.SWIPE, Direction.UP, containerType, pageIndex);
-        activity.getUserEventDispatcher().setPreviousHomeGesture(true);
+                : LAUNCHER_STATE_HOME;
         activity.getStatsLogManager().logger()
-                .withSrcState(LAUNCHER_STATE_HOME)
-                .withDstState(LAUNCHER_STATE_HOME)
+                .withSrcState(LAUNCHER_STATE_BACKGROUND)
+                .withDstState(state)
                 .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
                         .setWorkspace(
                                 LauncherAtom.WorkspaceContainer.newBuilder()
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
similarity index 79%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index b9827ff..a8bf333 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -42,18 +42,16 @@
         mMotionPauseMinDisplacement = context.getResources().getDimension(
                 R.dimen.motion_pause_detector_min_displacement_from_app);
         mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/);
-        mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
-            if (isPaused) {
-                SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
-                BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
-                        .getCreatedActivity();
-                if (launcherActivity != null) {
-                    launcherActivity.getRootView().performHapticFeedback(
-                            HapticFeedbackConstants.LONG_PRESS,
-                            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-                }
-                mMotionPauseDetector.clear();
+        mMotionPauseDetector.setOnMotionPauseListener(() -> {
+            SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
+            BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
+                    .getCreatedActivity();
+            if (launcherActivity != null) {
+                launcherActivity.getRootView().performHapticFeedback(
+                        HapticFeedbackConstants.LONG_PRESS,
+                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
             }
+            mMotionPauseDetector.clear();
         });
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
new file mode 100644
index 0000000..4472bdc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -0,0 +1,102 @@
+/*
+ * 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.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import java.net.URISyntaxException;
+
+/**
+ * A page shows after SUW flow to hint users to swipe up from the bottom of the screen to go home
+ * for the gestural system navigation.
+ */
+public class AllSetActivity extends Activity {
+
+    private static final String LOG_TAG = "AllSetActivity";
+    private static final String URI_SYSTEM_NAVIGATION_SETTING =
+            "#Intent;action=com.android.settings.SEARCH_RESULT_TRAMPOLINE;S.:settings:fragment_args_key=gesture_system_navigation_input_summary;S.:settings:show_fragment=com.android.settings.gestures.SystemNavigationGestureSettings;end";
+    private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "suwColorAccentDark";
+    private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "suwColorAccentLight";
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_allset);
+
+        int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
+        int accentColor = getIntent().getIntExtra(
+                isDarkTheme ? EXTRA_ACCENT_COLOR_DARK_MODE : EXTRA_ACCENT_COLOR_LIGHT_MODE,
+                isDarkTheme ? Color.WHITE : Color.BLACK);
+
+        ((ImageView) findViewById(R.id.icon)).getDrawable().mutate().setTint(accentColor);
+
+        TextView tv = findViewById(R.id.navigation_settings);
+        tv.setTextColor(accentColor);
+        tv.setOnClickListener(v -> {
+            try {
+                startActivityForResult(
+                        Intent.parseUri(URI_SYSTEM_NAVIGATION_SETTING, 0), 0);
+            } catch (URISyntaxException e) {
+                Log.e(LOG_TAG, "Failed to parse system nav settings intent", e);
+            }
+            finish();
+        });
+
+        findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate());
+    }
+
+    /**
+     * Accessibility delegate which exposes a click event without making the view
+     * clickable in touch mode
+     */
+    private class SkipButtonAccessibilityDelegate extends AccessibilityDelegate {
+
+        @Override
+        public AccessibilityNodeInfo createAccessibilityNodeInfo(View host) {
+            AccessibilityNodeInfo info = super.createAccessibilityNodeInfo(host);
+            info.addAction(AccessibilityAction.ACTION_CLICK);
+            info.setClickable(true);
+            return info;
+        }
+
+        @Override
+        public boolean performAccessibilityAction(View host, int action, Bundle args) {
+            if (action == AccessibilityAction.ACTION_CLICK.getId()) {
+                startActivity(Utilities.createHomeIntent());
+                finish();
+                return true;
+            }
+            return super.performAccessibilityAction(host, action, args);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
index 6862f07..957f776 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
@@ -18,7 +18,6 @@
 import static com.android.quickstep.interaction.TutorialController.TutorialType.ASSISTANT_COMPLETE;
 
 import android.graphics.PointF;
-import android.view.View;
 
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
@@ -33,38 +32,6 @@
     }
 
     @Override
-    Integer getTitleStringId() {
-        switch (mTutorialType) {
-            case ASSISTANT:
-                return R.string.assistant_gesture_tutorial_playground_title;
-            case ASSISTANT_COMPLETE:
-                return R.string.gesture_tutorial_confirm_title;
-        }
-        return null;
-    }
-
-    @Override
-    Integer getSubtitleStringId() {
-        if (mTutorialType == TutorialType.ASSISTANT) {
-            return R.string.assistant_gesture_tutorial_playground_subtitle;
-        }
-        return null;
-    }
-
-    @Override
-    Integer getActionButtonStringId() {
-        if (mTutorialType == ASSISTANT_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label_done;
-        }
-        return null;
-    }
-
-    @Override
-    void onActionButtonClicked(View button) {
-        mTutorialFragment.closeTutorial();
-    }
-
-    @Override
     public void onBackGestureAttempted(BackGestureResult result) {
         switch (mTutorialType) {
             case ASSISTANT:
@@ -101,10 +68,9 @@
                         showFeedback(R.string.assistant_gesture_feedback_swipe_too_far_from_corner);
                         break;
                     case ASSISTANT_COMPLETED:
-                        hideFeedback();
-                        hideHandCoachingAnimation();
-                        showRippleEffect(
-                                () -> mTutorialFragment.changeController(ASSISTANT_COMPLETE));
+                        hideFeedback(true);
+                        showRippleEffect(null);
+                        showFeedback(R.string.assistant_gesture_tutorial_playground_subtitle);
                         break;
                     case ASSISTANT_NOT_STARTED_BAD_ANGLE:
                         showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
index 70181fb..b797f0c 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
@@ -18,17 +18,11 @@
 import android.view.MotionEvent;
 import android.view.View;
 
-import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 /** Shows the Home gesture interactive tutorial. */
 public class AssistantGestureTutorialFragment extends TutorialFragment {
     @Override
-    int getHandAnimationResId() {
-        return R.drawable.assistant_gesture;
-    }
-
-    @Override
     TutorialController createController(TutorialType type) {
         return new AssistantGestureTutorialController(this, type);
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 921e568..3cb22f4 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -15,11 +15,12 @@
  */
 package com.android.quickstep.interaction;
 
+import static com.android.quickstep.interaction.TutorialController.TutorialType.BACK_NAVIGATION;
 import static com.android.quickstep.interaction.TutorialController.TutorialType.BACK_NAVIGATION_COMPLETE;
-import static com.android.quickstep.interaction.TutorialController.TutorialType.LEFT_EDGE_BACK_NAVIGATION;
 
 import android.graphics.PointF;
-import android.view.View;
+
+import androidx.appcompat.content.res.AppCompatResources;
 
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
@@ -33,65 +34,28 @@
     }
 
     @Override
-    Integer getTitleStringId() {
-        switch (mTutorialType) {
-            case RIGHT_EDGE_BACK_NAVIGATION:
-                return R.string.back_gesture_tutorial_playground_title_swipe_inward_right_edge;
-            case LEFT_EDGE_BACK_NAVIGATION:
-                return R.string.back_gesture_tutorial_playground_title_swipe_inward_left_edge;
-            case BACK_NAVIGATION_COMPLETE:
-                return R.string.gesture_tutorial_confirm_title;
-        }
-        return null;
+    public Integer getIntroductionTitle() {
+        return R.string.back_gesture_intro_title;
     }
 
     @Override
-    Integer getSubtitleStringId() {
-        switch (mTutorialType) {
-            case RIGHT_EDGE_BACK_NAVIGATION:
-                return R.string.back_gesture_tutorial_engaged_subtitle_swipe_inward_right_edge;
-            case LEFT_EDGE_BACK_NAVIGATION:
-                return R.string.back_gesture_tutorial_engaged_subtitle_swipe_inward_left_edge;
-            case BACK_NAVIGATION_COMPLETE:
-                return R.string.back_gesture_tutorial_confirm_subtitle;
-        }
-        return null;
+    public Integer getIntroductionSubtitle() {
+        return R.string.back_gesture_intro_subtitle;
     }
 
     @Override
-    Integer getActionButtonStringId() {
-        if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label_done;
-        }
-        return null;
-    }
-
-    @Override
-    Integer getActionTextButtonStringId() {
-        if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label_settings;
-        }
-        return null;
-    }
-
-    @Override
-    void onActionButtonClicked(View button) {
-        mTutorialFragment.closeTutorial();
-    }
-
-    @Override
-    void onActionTextButtonClicked(View button) {
-        mTutorialFragment.startSystemNavigationSetting();
+    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
+        return R.drawable.mock_conversation;
     }
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
+        if (mGestureCompleted) {
+            return;
+        }
         switch (mTutorialType) {
-            case RIGHT_EDGE_BACK_NAVIGATION:
-                handleAttemptFromRight(result);
-                break;
-            case LEFT_EDGE_BACK_NAVIGATION:
-                handleAttemptFromLeft(result);
+            case BACK_NAVIGATION:
+                handleBackAttempt(result);
                 break;
             case BACK_NAVIGATION_COMPLETE:
                 if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
@@ -102,43 +66,25 @@
         }
     }
 
-    private void handleAttemptFromRight(BackGestureResult result) {
-        switch (result) {
-            case BACK_COMPLETED_FROM_RIGHT:
-                hideFeedback();
-                hideHandCoachingAnimation();
-                showRippleEffect(
-                        () -> mTutorialFragment.changeController(LEFT_EDGE_BACK_NAVIGATION));
-                break;
-            case BACK_CANCELLED_FROM_RIGHT:
-                showFeedback(R.string.back_gesture_feedback_cancelled_right_edge);
-                break;
-            case BACK_COMPLETED_FROM_LEFT:
-            case BACK_CANCELLED_FROM_LEFT:
-            case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
-                showFeedback(R.string.back_gesture_feedback_swipe_too_far_from_right_edge);
-                break;
-            case BACK_NOT_STARTED_IN_NAV_BAR_REGION:
-                showFeedback(R.string.back_gesture_feedback_swipe_in_nav_bar);
-                break;
-        }
-    }
-
-    private void handleAttemptFromLeft(BackGestureResult result) {
+    private void handleBackAttempt(BackGestureResult result) {
         switch (result) {
             case BACK_COMPLETED_FROM_LEFT:
-                hideFeedback();
-                hideHandCoachingAnimation();
-                showRippleEffect(
-                        () -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE));
+            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);
                 break;
             case BACK_CANCELLED_FROM_LEFT:
-                showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
-                break;
-            case BACK_COMPLETED_FROM_RIGHT:
             case BACK_CANCELLED_FROM_RIGHT:
+                showFeedback(R.string.back_gesture_feedback_cancelled);
+                break;
             case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
-                showFeedback(R.string.back_gesture_feedback_swipe_too_far_from_left_edge);
+                showFeedback(R.string.back_gesture_feedback_swipe_too_far_from_edge);
                 break;
             case BACK_NOT_STARTED_IN_NAV_BAR_REGION:
                 showFeedback(R.string.back_gesture_feedback_swipe_in_nav_bar);
@@ -148,10 +94,15 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
+        if (mGestureCompleted) {
+            return;
+        }
         if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
             if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
                 mTutorialFragment.closeTutorial();
             }
+        } else if (mTutorialType == BACK_NAVIGATION) {
+            showFeedback(R.string.back_gesture_feedback_swipe_in_nav_bar);
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
index bef50ea..1740f68 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
@@ -18,14 +18,23 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 /** Shows the Back gesture interactive tutorial. */
 public class BackGestureTutorialFragment extends TutorialFragment {
+    @Nullable
     @Override
-    int getHandAnimationResId() {
-        return R.drawable.back_gesture;
+    Integer getFeedbackVideoResId(boolean forDarkMode) {
+        return R.drawable.gesture_tutorial_motion_back;
+    }
+
+    @Nullable
+    @Override
+    Integer getGestureVideoResId() {
+        return R.drawable.gesture_tutorial_loop_back;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
index e4b348e..c532f8e 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
@@ -30,6 +30,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
 
 /**
  * Utility class to handle edge swipes for back gestures.
@@ -47,9 +48,9 @@
     private final Point mDisplaySize = new Point();
 
     // The edge width where touch down is allowed
-    private int mEdgeWidth;
+    private final int mEdgeWidth;
     // The bottom gesture area height
-    private int mBottomGestureHeight;
+    private final int mBottomGestureHeight;
     // The slop to distinguish between horizontal and vertical motion
     private final float mTouchSlop;
     // Duration after which we consider the event as longpress.
@@ -97,7 +98,9 @@
 
         mBottomGestureHeight =
             ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, res);
-        mEdgeWidth = ResourceUtils.getNavbarSize("config_backGestureInset", res);
+        int systemBackRegion = ResourceUtils.getNavbarSize("config_backGestureInset", res);
+        // System back region is 0 if gesture nav is not currently enabled.
+        mEdgeWidth = systemBackRegion == 0 ? Utilities.dpToPx(18) : systemBackRegion;
     }
 
     void setViewGroupParent(@Nullable ViewGroup parent) {
@@ -143,6 +146,10 @@
         return false;
     }
 
+    boolean onInterceptTouch(MotionEvent motionEvent) {
+        return isWithinTouchRegion((int) motionEvent.getX(), (int) motionEvent.getY());
+    }
+
     private boolean isWithinTouchRegion(int x, int y) {
         // Disallow if too far from the edge
         if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index f8d9d8d..7fb7d29 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -15,16 +15,16 @@
  */
 package com.android.quickstep.interaction;
 
-import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE;
-
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.View;
 import android.view.Window;
 
+import androidx.annotation.NonNull;
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.launcher3.R;
@@ -37,15 +37,26 @@
 
     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 TutorialType[] mTutorialSteps;
+    private TutorialType mCurrentTutorialStep;
     private TutorialFragment mFragment;
 
+    private int mCurrentStep;
+    private int mNumSteps;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         requestWindowFeature(Window.FEATURE_NO_TITLE);
         setContentView(R.layout.gesture_tutorial_activity);
 
-        mFragment = TutorialFragment.newInstance(getTutorialType(getIntent().getExtras()));
+        Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
+        mTutorialSteps = getTutorialSteps(args);
+        mCurrentTutorialStep = mTutorialSteps[mCurrentStep - 1];
+        mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
         getSupportFragmentManager().beginTransaction()
                 .add(R.id.gesture_tutorial_fragment_container, mFragment)
                 .commit();
@@ -72,17 +83,98 @@
         }
     }
 
-    private TutorialType getTutorialType(Bundle extras) {
-        TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
+    @Override
+    protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+        savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
+        savedInstanceState.putInt(KEY_CURRENT_STEP, mCurrentStep);
+        super.onSaveInstanceState(savedInstanceState);
+    }
 
-        if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) {
-            return defaultType;
+    /** Returns true iff there aren't anymore tutorial types to display to the user. */
+    public boolean isTutorialComplete() {
+        return mCurrentStep >= mNumSteps;
+    }
+
+    public int getCurrentStep() {
+        return mCurrentStep;
+    }
+
+    public int getNumSteps() {
+        return mNumSteps;
+    }
+
+    /**
+     * Closes the tutorial and this activity.
+     */
+    public void closeTutorial() {
+        mFragment.closeTutorial();
+    }
+
+    /**
+     * Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
+     *
+     * If there is no following step, the tutorial is closed.
+     */
+    public void continueTutorial() {
+        if (isTutorialComplete()) {
+            mFragment.closeTutorial();
+            return;
         }
-        try {
-            return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, ""));
-        } catch (IllegalArgumentException e) {
-            return defaultType;
+        mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
+        mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
+        getSupportFragmentManager().beginTransaction()
+                .replace(R.id.gesture_tutorial_fragment_container, mFragment)
+                .runOnCommit(() -> mFragment.onAttachedToWindow())
+                .commit();
+        mCurrentStep++;
+    }
+
+    private String[] getTutorialStepNames() {
+        String[] tutorialStepNames = new String[mTutorialSteps.length];
+
+        int i = 0;
+        for (TutorialType tutorialStep : mTutorialSteps) {
+            tutorialStepNames[i++] = tutorialStep.name();
         }
+
+        return tutorialStepNames;
+    }
+
+    private TutorialType[] getTutorialSteps(Bundle extras) {
+        TutorialType[] defaultSteps = new TutorialType[] {TutorialType.BACK_NAVIGATION};
+        mCurrentStep = 1;
+        mNumSteps = 1;
+
+        if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
+            return defaultSteps;
+        }
+
+        Object savedSteps = extras.get(KEY_TUTORIAL_STEPS);
+        int currentStep = extras.getInt(KEY_CURRENT_STEP, -1);
+        String[] savedStepsNames;
+
+        if (savedSteps instanceof String) {
+            savedStepsNames = TextUtils.isEmpty((String) savedSteps)
+                    ? null : ((String) savedSteps).split(",");
+        } else if (savedSteps instanceof String[]) {
+            savedStepsNames = (String[]) savedSteps;
+        } else {
+            return defaultSteps;
+        }
+
+        if (savedStepsNames == null) {
+            return defaultSteps;
+        }
+
+        TutorialType[] tutorialSteps = new TutorialType[savedStepsNames.length];
+        for (int i = 0; i < savedStepsNames.length; i++) {
+            tutorialSteps[i] = TutorialType.valueOf(savedStepsNames[i]);
+        }
+
+        mCurrentStep = Math.max(currentStep, 1);
+        mNumSteps = tutorialSteps.length;
+
+        return tutorialSteps;
     }
 
     private void hideSystemUI() {
@@ -91,6 +183,7 @@
                         | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                         | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                         | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                         | View.SYSTEM_UI_FLAG_FULLSCREEN);
         getWindow().setNavigationBarColor(Color.TRANSPARENT);
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 0edabd4..819c91c 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -15,12 +15,9 @@
  */
 package com.android.quickstep.interaction;
 
-import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
-
 import android.annotation.TargetApi;
 import android.graphics.PointF;
 import android.os.Build;
-import android.view.View;
 
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
@@ -35,39 +32,25 @@
     }
 
     @Override
-    Integer getTitleStringId() {
-        switch (mTutorialType) {
-            case HOME_NAVIGATION:
-                return R.string.home_gesture_tutorial_playground_title;
-            case HOME_NAVIGATION_COMPLETE:
-                return R.string.gesture_tutorial_confirm_title;
-        }
-        return null;
+    public Integer getIntroductionTitle() {
+        return R.string.home_gesture_intro_title;
     }
 
     @Override
-    Integer getSubtitleStringId() {
-        if (mTutorialType == TutorialType.HOME_NAVIGATION) {
-            return R.string.home_gesture_tutorial_playground_subtitle;
-        }
-        return null;
+    public Integer getIntroductionSubtitle() {
+        return R.string.home_gesture_intro_subtitle;
     }
 
     @Override
-    Integer getActionButtonStringId() {
-        if (mTutorialType == HOME_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label_done;
-        }
-        return null;
-    }
-
-    @Override
-    void onActionButtonClicked(View button) {
-        mTutorialFragment.closeTutorial();
+    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
+        return forDarkMode ? R.drawable.mock_webpage_dark_mode : R.drawable.mock_webpage_light_mode;
     }
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
+        if (mGestureCompleted) {
+            return;
+        }
         switch (mTutorialType) {
             case HOME_NAVIGATION:
                 switch (result) {
@@ -90,12 +73,19 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
+        if (mGestureCompleted) {
+            return;
+        }
         switch (mTutorialType) {
             case HOME_NAVIGATION:
                 switch (result) {
                     case HOME_GESTURE_COMPLETED: {
-                        animateFakeTaskViewHome(finalVelocity, () ->
-                                mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE));
+                        mTutorialFragment.releaseGestureVideoView();
+                        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);
                         break;
                     }
                     case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
@@ -103,12 +93,12 @@
                         showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
                         break;
                     case OVERVIEW_GESTURE_COMPLETED:
-                        fadeOutFakeTaskView(true, () ->
+                        fadeOutFakeTaskView(true, true, () ->
                                 showFeedback(R.string.home_gesture_feedback_overview_detected));
                         break;
                     case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
                     case HOME_OR_OVERVIEW_CANCELLED:
-                        fadeOutFakeTaskView(false, null);
+                        fadeOutFakeTaskView(false, true, null);
                         showFeedback(R.string.home_gesture_feedback_wrong_swipe_direction);
                         break;
                 }
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
index e2a9d12..9572637 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
@@ -15,14 +15,25 @@
  */
 package com.android.quickstep.interaction;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 /** Shows the Home gesture interactive tutorial. */
 public class HomeGestureTutorialFragment extends TutorialFragment {
+    @Nullable
     @Override
-    int getHandAnimationResId() {
-        return R.drawable.home_gesture;
+    Integer getFeedbackVideoResId(boolean forDarkMode) {
+        return forDarkMode
+                ? R.drawable.gesture_tutorial_motion_home_dark_mode
+                : R.drawable.gesture_tutorial_motion_home_light_mode;
+    }
+
+    @Nullable
+    @Override
+    Integer getGestureVideoResId() {
+        return R.drawable.gesture_tutorial_loop_home;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 81c4d0c..a9a9e2a 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -56,7 +56,7 @@
 
 /** Utility class to handle Home and Assistant gestures. */
 public class NavBarGestureHandler implements OnTouchListener,
-        TriggerSwipeUpTouchTracker.OnSwipeUpListener {
+        TriggerSwipeUpTouchTracker.OnSwipeUpListener, MotionPauseDetector.OnMotionPauseListener {
 
     private static final String LOG_TAG = "NavBarGestureHandler";
     private static final long RETRACT_GESTURE_ANIMATION_DURATION_MS = 300;
@@ -144,7 +144,6 @@
         if (mGestureCallback == null || mAssistantGestureActive) {
             return;
         }
-        finalVelocity.set(finalVelocity.x / 1000, finalVelocity.y / 1000);
         if (mTouchCameFromNavBar) {
             mGestureCallback.onNavBarGestureAttempted(wasFling
                     ? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED, finalVelocity);
@@ -182,7 +181,7 @@
                 mLaunchedAssistant = false;
                 mSwipeUpTouchTracker.init();
                 mMotionPauseDetector.clear();
-                mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
+                mMotionPauseDetector.setOnMotionPauseListener(this);
                 break;
             case MotionEvent.ACTION_MOVE:
                 mLastPos.set(event.getX(), event.getY());
@@ -220,7 +219,6 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 mMotionPauseDetector.clear();
-                mMotionPauseDetector.setOnMotionPauseListener(null);
                 if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) {
                     mGestureCallback.onNavBarGestureAttempted(
                             HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF());
@@ -252,10 +250,20 @@
         return intercepted;
     }
 
-    protected void onMotionPauseChanged(boolean isPaused) {
-        if (isPaused) {
-            VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
-        }
+    boolean onInterceptTouch(MotionEvent event) {
+        return mAssistantLeftRegion.contains(event.getX(), event.getY())
+                || mAssistantRightRegion.contains(event.getX(), event.getY())
+                || event.getY() >= mDisplaySize.y - mBottomGestureHeight;
+    }
+
+    @Override
+    public void onMotionPauseChanged(boolean isPaused) {
+        mGestureCallback.onMotionPaused(isPaused);
+    }
+
+    @Override
+    public void onMotionPauseDetected() {
+        VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
     }
 
     /**
@@ -309,6 +317,9 @@
         /** Called whenever any touch is completed. */
         void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity);
 
+        /** Called when a motion stops or resumes */
+        default void onMotionPaused(boolean isPaused) {}
+
         /** Indicates how far a touch originating in the nav bar has moved from the nav bar. */
         default void setNavBarGestureProgress(@Nullable Float displacement) {}
 
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index c636eba..77ddb2b 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -15,14 +15,17 @@
  */
 package com.android.quickstep.interaction;
 
-import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
 
+import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.graphics.PointF;
 import android.os.Build;
-import android.view.View;
 
 import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SwipeUpAnimationLogic;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
 
@@ -36,39 +39,25 @@
     }
 
     @Override
-    Integer getTitleStringId() {
-        switch (mTutorialType) {
-            case OVERVIEW_NAVIGATION:
-                return R.string.overview_gesture_tutorial_playground_title;
-            case OVERVIEW_NAVIGATION_COMPLETE:
-                return R.string.gesture_tutorial_confirm_title;
-        }
-        return null;
+    public Integer getIntroductionTitle() {
+        return R.string.overview_gesture_intro_title;
     }
 
     @Override
-    Integer getSubtitleStringId() {
-        if (mTutorialType == TutorialType.OVERVIEW_NAVIGATION) {
-            return R.string.overview_gesture_tutorial_playground_subtitle;
-        }
-        return null;
+    public Integer getIntroductionSubtitle() {
+        return R.string.overview_gesture_intro_subtitle;
     }
 
     @Override
-    Integer getActionButtonStringId() {
-        if (mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label_done;
-        }
-        return null;
-    }
-
-    @Override
-    void onActionButtonClicked(View button) {
-        mTutorialFragment.closeTutorial();
+    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
+        return R.drawable.mock_conversations_list;
     }
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
+        if (mGestureCompleted) {
+            return;
+        }
         switch (mTutorialType) {
             case OVERVIEW_NAVIGATION:
                 switch (result) {
@@ -91,12 +80,17 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
+        if (mGestureCompleted) {
+            return;
+        }
         switch (mTutorialType) {
             case OVERVIEW_NAVIGATION:
                 switch (result) {
                     case HOME_GESTURE_COMPLETED: {
-                        animateFakeTaskViewHome(finalVelocity, () ->
-                                showFeedback(R.string.overview_gesture_feedback_home_detected));
+                        animateFakeTaskViewHome(finalVelocity, () -> {
+                            resetFakeTaskView();
+                            showFeedback(R.string.overview_gesture_feedback_home_detected);
+                        });
                         break;
                     }
                     case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
@@ -104,12 +98,23 @@
                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
                         break;
                     case OVERVIEW_GESTURE_COMPLETED:
-                        fadeOutFakeTaskView(true, () ->
-                                mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE));
+                        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);
+                        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);
                         break;
                     case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
                     case HOME_OR_OVERVIEW_CANCELLED:
-                        fadeOutFakeTaskView(false, null);
+                        fadeOutFakeTaskView(false, true, null);
                         showFeedback(R.string.overview_gesture_feedback_wrong_swipe_direction);
                         break;
                 }
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
index 3357b70..d2ec327 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
@@ -15,14 +15,25 @@
  */
 package com.android.quickstep.interaction;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 /** Shows the Overview gesture interactive tutorial. */
 public class OverviewGestureTutorialFragment extends TutorialFragment {
+    @Nullable
     @Override
-    int getHandAnimationResId() {
-        return R.drawable.overview_gesture;
+    Integer getFeedbackVideoResId(boolean forDarkMode) {
+        return forDarkMode
+                ? R.drawable.gesture_tutorial_motion_overview_dark_mode
+                : R.drawable.gesture_tutorial_motion_overview_light_mode;
+    }
+
+    @Nullable
+    @Override
+    Integer getGestureVideoResId() {
+        return R.drawable.gesture_tutorial_loop_overview;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
new file mode 100644
index 0000000..db1afc2
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
@@ -0,0 +1,44 @@
+/*
+ * 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.interaction;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.RelativeLayout;
+
+import androidx.fragment.app.FragmentManager;
+
+/** Root layout that TutorialFragment uses to intercept motion events. */
+public class RootSandboxLayout extends RelativeLayout {
+    public RootSandboxLayout(Context context) {
+        super(context);
+    }
+
+    public RootSandboxLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public RootSandboxLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
+        return ((TutorialFragment) FragmentManager.findFragment(this))
+                .onInterceptTouch(motionEvent);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
new file mode 100644
index 0000000..19b7933
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
@@ -0,0 +1,74 @@
+/*
+ * 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.interaction;
+
+import android.graphics.PointF;
+
+import com.android.launcher3.R;
+import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
+import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
+
+/** A {@link TutorialController} for the Sandbox Mode. */
+public class SandboxModeTutorialController extends SwipeUpGestureTutorialController {
+
+    SandboxModeTutorialController(SandboxModeTutorialFragment fragment, TutorialType tutorialType) {
+        super(fragment, tutorialType);
+    }
+
+    @Override
+    public void onBackGestureAttempted(BackGestureResult result) {
+        switch (result) {
+            case BACK_COMPLETED_FROM_LEFT:
+            case BACK_COMPLETED_FROM_RIGHT:
+                showRippleEffect(null);
+                showFeedback(R.string.sandbox_mode_back_gesture_feedback_successful);
+                break;
+            case BACK_CANCELLED_FROM_LEFT:
+            case BACK_CANCELLED_FROM_RIGHT:
+                showFeedback(R.string.back_gesture_feedback_cancelled);
+                break;
+            case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
+                showFeedback(R.string.sandbox_mode_back_gesture_feedback_swipe_too_far_from_edge);
+                break;
+        }
+    }
+
+    @Override
+    public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
+        switch (result) {
+            case ASSISTANT_COMPLETED:
+                showRippleEffect(null);
+                showFeedback(R.string.sandbox_mode_assistant_gesture_feedback_successful);
+                break;
+            case HOME_GESTURE_COMPLETED:
+                animateFakeTaskViewHome(finalVelocity, () -> {
+                    showFeedback(R.string.sandbox_mode_home_gesture_feedback_successful);
+                });
+                break;
+            case OVERVIEW_GESTURE_COMPLETED:
+                fadeOutFakeTaskView(true, true, () -> {
+                    showFeedback(R.string.sandbox_mode_overview_gesture_feedback_successful);
+                });
+                break;
+            case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
+            case HOME_OR_OVERVIEW_CANCELLED:
+            case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
+            case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
+                showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
+                break;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
new file mode 100644
index 0000000..955a2f7
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
@@ -0,0 +1,43 @@
+/*
+ * 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.interaction;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.quickstep.interaction.TutorialController.TutorialType;
+
+/** Shows the general navigation gesture sandbox environment. */
+public class SandboxModeTutorialFragment extends TutorialFragment {
+
+    @Override
+    TutorialController createController(TutorialType type) {
+        return new SandboxModeTutorialController(this, type);
+    }
+
+    @Override
+    Class<? extends TutorialController> getControllerClass() {
+        return SandboxModeTutorialController.class;
+    }
+
+    @Override
+    public boolean onTouch(View view, MotionEvent motionEvent) {
+        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN && mTutorialController != null) {
+            mTutorialController.setRippleHotspot(motionEvent.getX(), motionEvent.getY());
+        }
+        return super.onTouch(view, motionEvent);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index a157314..b2183d6 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -16,9 +16,9 @@
 package com.android.quickstep.interaction;
 
 import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
+import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION;
 import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
 import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
 
@@ -27,17 +27,15 @@
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.Insets;
 import android.graphics.Outline;
 import android.graphics.PointF;
 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;
-import android.view.WindowInsets;
-import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -45,7 +43,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 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;
 import com.android.quickstep.AnimatedFloat;
@@ -54,23 +52,52 @@
 import com.android.quickstep.RecentsAnimationDeviceState;
 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.QuickStepContract;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 @TargetApi(Build.VERSION_CODES.R)
 abstract class SwipeUpGestureTutorialController extends TutorialController {
-    private final ViewSwipeUpAnimation mViewSwipeUpAnimation;
+
+    private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(12);
+
+    final ViewSwipeUpAnimation mTaskViewSwipeUpAnimation;
     private float mFakeTaskViewRadius;
     private Rect mFakeTaskViewRect = new Rect();
-    private RunningWindowAnim mRunningWindowAnim;
+    RunningWindowAnim mRunningWindowAnim;
+    private boolean mShowTasks = false;
+    private boolean mShowPreviousTasks = false;
+
+    private 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;
+                mFakeTaskViewRect.set(0, 0, width, height);
+            }
+            mFakeTaskViewRadius = 0;
+            mFakeTaskView.invalidateOutline();
+            mFakeTaskView.setVisibility(View.VISIBLE);
+            mFakeTaskView.setAlpha(1);
+            mFakePreviousTaskView.setVisibility(View.INVISIBLE);
+            mFakePreviousTaskView.setAlpha(1);
+            mShowTasks = false;
+            mShowPreviousTasks = false;
+            mRunningWindowAnim = null;
+        }
+    };
 
     SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
         super(tutorialFragment, tutorialType);
         RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext);
         OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState);
-        mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
+        mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
                 new GestureState(observer, -1));
         observer.onDestroy();
         deviceState.destroy();
@@ -78,21 +105,26 @@
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext)
                 .getDeviceProfile(mContext)
                 .copy(mContext);
-        Insets insets = mContext.getSystemService(WindowManager.class)
-                .getCurrentWindowMetrics()
-                .getWindowInsets()
-                .getInsets(WindowInsets.Type.systemBars());
-        dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom));
-        mViewSwipeUpAnimation.initDp(dp);
+        mTaskViewSwipeUpAnimation.initDp(dp);
 
-        mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources());
-        mFakeTaskView.setClipToOutline(true);
-        mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() {
+        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+        int height = displayMetrics.heightPixels;
+        int width = displayMetrics.widthPixels;
+        mFakeTaskViewRect.set(0, 0, width, height);
+        mFakeTaskViewRadius = 0;
+
+        ViewOutlineProvider outlineProvider = new ViewOutlineProvider() {
             @Override
             public void getOutline(View view, Outline outline) {
                 outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius);
             }
-        });
+        };
+
+        mFakeTaskView.setClipToOutline(true);
+        mFakeTaskView.setOutlineProvider(outlineProvider);
+
+        mFakePreviousTaskView.setClipToOutline(true);
+        mFakePreviousTaskView.setOutlineProvider(outlineProvider);
     }
 
     private void cancelRunningAnimation() {
@@ -103,28 +135,29 @@
     }
 
     /** Fades the task view, optionally after animating to a fake Overview. */
-    void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) {
-        hideFeedback();
-        hideHandCoachingAnimation();
+    void fadeOutFakeTaskView(boolean toOverviewFirst, boolean reset,
+                             @Nullable Runnable onEndRunnable) {
+        hideFeedback(true);
         cancelRunningAnimation();
         PendingAnimation anim = new PendingAnimation(300);
-        AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation, boolean isReverse) {
-                mFakeIconView.setVisibility(View.INVISIBLE);
-                mFakeTaskView.setVisibility(View.INVISIBLE);
-                mFakeTaskView.setAlpha(1);
-                mRunningWindowAnim = null;
-            }
-        };
         if (toOverviewFirst) {
-            anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
+            anim.setFloat(mTaskViewSwipeUpAnimation
+                    .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation, boolean isReverse) {
                     PendingAnimation fadeAnim = new PendingAnimation(300);
-                    fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL);
-                    fadeAnim.addListener(resetTaskView);
+                    if (reset) {
+                        fadeAnim.setFloat(mTaskViewSwipeUpAnimation
+                                .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
+                        fadeAnim.addListener(mResetTaskView);
+                    } else {
+                        fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+                        fadeAnim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL);
+                    }
+                    if (onEndRunnable != null) {
+                        fadeAnim.addListener(AnimatorListeners.forSuccessCallback(onEndRunnable));
+                    }
                     AnimatorSet animset = fadeAnim.buildAnim();
                     animset.setStartDelay(100);
                     animset.start();
@@ -132,40 +165,92 @@
                 }
             });
         } else {
-            anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
-            anim.setViewAlpha(mFakeIconView, 0, ACCEL);
-            anim.addListener(resetTaskView);
-        }
-        if (onEndRunnable != null) {
-            anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable));
+            if (reset) {
+                anim.setFloat(mTaskViewSwipeUpAnimation
+                        .getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
+                anim.addListener(mResetTaskView);
+            } else {
+                anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+                anim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL);
+            }
+            if (onEndRunnable != null) {
+                anim.addListener(AnimatorListeners.forSuccessCallback(onEndRunnable));
+            }
         }
         AnimatorSet animset = anim.buildAnim();
         animset.start();
         mRunningWindowAnim = RunningWindowAnim.wrap(animset);
     }
 
+    void resetFakeTaskView() {
+        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();
+        animset.start();
+        mRunningWindowAnim = RunningWindowAnim.wrap(animset);
+    }
+
     void animateFakeTaskViewHome(PointF finalVelocity, @Nullable Runnable onEndRunnable) {
-        hideFeedback();
-        hideHandCoachingAnimation();
+        hideFeedback(true);
         cancelRunningAnimation();
+        mFakePreviousTaskView.setVisibility(View.INVISIBLE);
+        mFakeHotseatView.setVisibility(View.VISIBLE);
+        mShowPreviousTasks = false;
         RectFSpringAnim rectAnim =
-                mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity);
+                mTaskViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity);
         // After home animation finishes, fade out and run onEndRunnable.
-        rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable(
-                () -> fadeOutFakeTaskView(false, onEndRunnable)));
+        PendingAnimation fadeAnim = new PendingAnimation(300);
+        fadeAnim.setViewAlpha(mFakeIconView, 0, ACCEL);
+        if (onEndRunnable != null) {
+            fadeAnim.addListener(AnimatorListeners.forSuccessCallback(onEndRunnable));
+        }
+        AnimatorSet animset = fadeAnim.buildAnim();
+        rectAnim.addAnimatorListener(AnimatorListeners.forSuccessCallback(animset::start));
         mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim);
     }
 
     @Override
     public void setNavBarGestureProgress(@Nullable Float displacement) {
-        if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE
+        if (mGestureCompleted) {
+            return;
+        }
+        if (displacement != null) {
+            hideFeedback(true);
+        }
+        if (mTutorialType == HOME_NAVIGATION_COMPLETE
                 || mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) {
             mFakeTaskView.setVisibility(View.INVISIBLE);
+            mFakePreviousTaskView.setVisibility(View.INVISIBLE);
         } else {
+            mShowTasks = true;
             mFakeTaskView.setVisibility(View.VISIBLE);
-            if (mRunningWindowAnim == null) {
-                mViewSwipeUpAnimation.updateDisplacement(displacement);
+            if (mShowPreviousTasks) {
+                mFakePreviousTaskView.setVisibility(View.VISIBLE);
             }
+            if (mRunningWindowAnim == null && displacement != null) {
+                mTaskViewSwipeUpAnimation.updateDisplacement(displacement);
+            }
+        }
+    }
+
+    @Override
+    public void onMotionPaused(boolean unused) {
+        if (mGestureCompleted) {
+            return;
+        }
+        if (mShowTasks) {
+            if (!mShowPreviousTasks) {
+                mFakePreviousTaskView.setTranslationX(
+                        -(2 * mFakePreviousTaskView.getWidth() + FAKE_PREVIOUS_TASK_MARGIN));
+                mFakePreviousTaskView.animate()
+                    .setDuration(300)
+                    .translationX(-(mFakePreviousTaskView.getWidth() + FAKE_PREVIOUS_TASK_MARGIN))
+                    .start();
+            }
+            mShowPreviousTasks = true;
         }
     }
 
@@ -213,25 +298,27 @@
                 @NonNull
                 @Override
                 public RectF getWindowTargetRect() {
-                    int fakeHomeIconSizePx = mDp.allAppsIconSizePx;
-                    int fakeHomeIconLeft = (mDp.widthPx - fakeHomeIconSizePx) / 2;
-                    int fakeHomeIconTop = mDp.heightPx - (mDp.allAppsCellHeightPx * 3);
+                    int fakeHomeIconSizePx = Utilities.dpToPx(60);
+                    int fakeHomeIconLeft = mFakeHotseatView.getLeft();
+                    int fakeHomeIconTop = mDp.heightPx - Utilities.dpToPx(216);
                     return new RectF(fakeHomeIconLeft, fakeHomeIconTop,
                             fakeHomeIconLeft + fakeHomeIconSizePx,
                             fakeHomeIconTop + fakeHomeIconSizePx);
                 }
 
                 @Override
-                public void update(RectF rect, float progress, float radius) {
+                public void update(@Nullable AppCloseConfig config, RectF rect, float progress,
+                        float radius) {
                     mFakeIconView.setVisibility(View.VISIBLE);
                     mFakeIconView.update(rect, progress,
                             1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
-                            radius,
+                            radius, 255,
                             false, /* isOpening */
                             mFakeIconView, mDp,
                             false /* isVerticalBarLayout */);
                     mFakeIconView.setAlpha(1);
                     mFakeTaskView.setAlpha(getWindowAlpha(progress));
+                    mFakePreviousTaskView.setAlpha(getWindowAlpha(progress));
                 }
 
                 @Override
@@ -258,9 +345,11 @@
         public void applySurfaceParams(SurfaceParams[] params) {
             SurfaceParams p = params[0];
             mFakeTaskView.setAnimationMatrix(p.matrix);
+            mFakePreviousTaskView.setAnimationMatrix(p.matrix);
             mFakeTaskViewRect.set(p.windowCrop);
             mFakeTaskViewRadius = p.cornerRadius;
             mFakeTaskView.invalidateOutline();
+            mFakePreviousTaskView.invalidateOutline();
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 73f1f8c..4b4e7e6 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -15,19 +15,35 @@
  */
 package com.android.quickstep.interaction;
 
+import static android.view.View.GONE;
+
 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.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
-import android.widget.ImageButton;
 import android.widget.ImageView;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import androidx.annotation.CallSuper;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.content.res.AppCompatResources;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.views.ClipIconView;
 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
@@ -35,92 +51,292 @@
 abstract class TutorialController implements BackGestureAttemptCallback,
         NavBarGestureAttemptCallback {
 
-    private static final int FEEDBACK_VISIBLE_MS = 3000;
-    private static final int FEEDBACK_ANIMATION_MS = 500;
+    private static final String TAG = "TutorialController";
+
+    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 RIPPLE_VISIBLE_MS = 300;
+    private static final int GESTURE_ANIMATION_DELAY_MS = 1500;
+    private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 4000;
 
     final TutorialFragment mTutorialFragment;
     TutorialType mTutorialType;
     final Context mContext;
 
-    final ImageButton mCloseButton;
-    final TextView mTitleTextView;
-    final TextView mSubtitleTextView;
-    final TextView mFeedbackView;
+    final TextView mCloseButton;
+    final ViewGroup mFeedbackView;
+    final TextView mFeedbackTitleView;
+    final ImageView mFeedbackVideoView;
+    final ImageView mGestureVideoView;
+    final RelativeLayout mFakeLauncherView;
+    final ImageView mFakeHotseatView;
     final ClipIconView mFakeIconView;
     final View mFakeTaskView;
+    final View mFakePreviousTaskView;
     final View mRippleView;
     final RippleDrawable mRippleDrawable;
-    final TutorialHandAnimation mHandCoachingAnimation;
-    final ImageView mHandCoachingView;
-    final Button mActionTextButton;
     final Button mActionButton;
-    private final Runnable mHideFeedbackRunnable;
+    final TutorialStepIndicator mTutorialStepView;
+    private final AlertDialog mSkipTutorialDialog;
+
+    protected 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;
 
     TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
         mTutorialFragment = tutorialFragment;
         mTutorialType = tutorialType;
         mContext = mTutorialFragment.getContext();
 
-        View rootView = tutorialFragment.getRootView();
+        RootSandboxLayout rootView = tutorialFragment.getRootView();
         mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
-        mCloseButton.setOnClickListener(button -> mTutorialFragment.closeTutorial());
-        mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
-        mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
+        mCloseButton.setOnClickListener(button -> showSkipTutorialDialog());
         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);
+        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);
+        mFakePreviousTaskView =
+                rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
         mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
         mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
-        mHandCoachingAnimation = tutorialFragment.getHandAnimation();
-        mHandCoachingView = rootView.findViewById(R.id.gesture_tutorial_fragment_hand_coaching);
-        mHandCoachingView.bringToFront();
-        mActionTextButton =
-                rootView.findViewById(R.id.gesture_tutorial_fragment_action_text_button);
         mActionButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
+        mTutorialStepView =
+                rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
+        mSkipTutorialDialog = createSkipTutorialDialog();
 
-        mHideFeedbackRunnable =
-                () -> mFeedbackView.animate().alpha(0).setDuration(FEEDBACK_ANIMATION_MS)
-                        .withEndAction(this::showHandCoachingAnimation).start();
+        mTitleViewCallback = () -> mFeedbackTitleView.sendAccessibilityEvent(
+                AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
+
+    private void showSkipTutorialDialog() {
+        if (mSkipTutorialDialog != null) {
+            mSkipTutorialDialog.show();
+        }
     }
 
     void setTutorialType(TutorialType tutorialType) {
         mTutorialType = tutorialType;
     }
 
-    @Nullable
-    Integer getTitleStringId() {
+    @DrawableRes
+    protected int getMockHotseatResId() {
+        return R.drawable.default_sandbox_mock_launcher;
+    }
+
+    @DrawableRes
+    protected int getMockAppTaskThumbnailResId(boolean forDarkMode) {
+        return R.drawable.default_sandbox_app_task_thumbnail;
+    }
+
+    @DrawableRes
+    protected int getMockPreviousAppTaskThumbnailResId() {
+        return R.drawable.default_sandbox_app_previous_task_thumbnail;
+    }
+
+    @DrawableRes
+    public int getMockAppIconResId() {
+        return R.drawable.default_sandbox_app_icon;
+    }
+
+    @DrawableRes
+    public int getMockWallpaperResId() {
+        return R.drawable.default_sandbox_wallpaper;
+    }
+
+    void fadeTaskViewAndRun(Runnable r) {
+        mFakeTaskView.animate().alpha(0).setListener(AnimatorListeners.forSuccessCallback(r));
+    }
+
+    @StringRes
+    public Integer getIntroductionTitle() {
         return null;
     }
 
-    @Nullable
-    Integer getSubtitleStringId() {
+    @StringRes
+    public Integer getIntroductionSubtitle() {
         return null;
     }
 
-    @Nullable
-    Integer getActionButtonStringId() {
-        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);
+        }
     }
 
-    @Nullable
-    Integer getActionTextButtonStringId() {
-        return null;
+    /**
+     * Show feedback reflecting a failed gesture attempt.
+     *
+     * @param subtitleResId Resource of the text to display.
+     **/
+    void showFeedback(int subtitleResId) {
+        showFeedback(subtitleResId, false);
     }
 
-    void showFeedback(int resId) {
-        hideHandCoachingAnimation();
-        mFeedbackView.setText(resId);
-        mFeedbackView.animate().alpha(1).setDuration(FEEDBACK_ANIMATION_MS).start();
-        mFeedbackView.removeCallbacks(mHideFeedbackRunnable);
-        mFeedbackView.postDelayed(mHideFeedbackRunnable, FEEDBACK_VISIBLE_MS);
+    /**
+     * Show feedback reflecting the result of a gesture attempt.
+     *
+     * @param isGestureSuccessful Whether the tutorial feedback's action button should be shown.
+     **/
+    void showFeedback(int subtitleResId, boolean isGestureSuccessful) {
+        showFeedback(
+                isGestureSuccessful
+                        ? R.string.gesture_tutorial_nice : R.string.gesture_tutorial_try_again,
+                subtitleResId,
+                isGestureSuccessful,
+                false);
     }
 
-    void hideFeedback() {
-        mFeedbackView.setText(null);
-        mFeedbackView.removeCallbacks(mHideFeedbackRunnable);
+    void showFeedback(
+            int titleResId,
+            int subtitleResId,
+            boolean isGestureSuccessful,
+            boolean useGestureAnimationDelay) {
+        mFeedbackTitleView.setText(titleResId);
+        mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
+        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;
+            }
+        }
+        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();
+            }
+        }
+        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);
+    }
+
+    void hideFeedback(boolean releaseFeedbackVideo) {
         mFeedbackView.clearAnimation();
-        mFeedbackView.setAlpha(0);
+        mFeedbackView.setVisibility(View.INVISIBLE);
+        if (releaseFeedbackVideo) {
+            mTutorialFragment.releaseFeedbackVideoView();
+        }
+    }
+
+    private void playFeedbackVideo(
+            @NonNull AnimatedVectorDrawable tutorialAnimation,
+            @NonNull AnimatedVectorDrawable gestureAnimation,
+            @NonNull Runnable onStartRunnable,
+            boolean useGestureAnimationDelay) {
+
+        if (tutorialAnimation.isRunning()) {
+            tutorialAnimation.reset();
+        }
+        tutorialAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
+
+            @Override
+            public void onAnimationStart(Drawable drawable) {
+                super.onAnimationStart(drawable);
+
+                mGestureVideoView.setVisibility(GONE);
+                if (gestureAnimation.isRunning()) {
+                    gestureAnimation.stop();
+                }
+
+                if (!useGestureAnimationDelay) {
+                    onStartRunnable.run();
+                }
+            }
+
+            @Override
+            public void onAnimationEnd(Drawable drawable) {
+                super.onAnimationEnd(drawable);
+
+                mGestureVideoView.setVisibility(View.VISIBLE);
+                gestureAnimation.start();
+
+                tutorialAnimation.unregisterAnimationCallback(this);
+            }
+        });
+
+        if (mFeedbackViewCallback != null) {
+            mFeedbackVideoView.removeCallbacks(mFeedbackViewCallback);
+            mFeedbackViewCallback = null;
+        }
+        if (mFeedbackVideoViewCallback != null) {
+            mFeedbackVideoView.removeCallbacks(mFeedbackVideoViewCallback);
+            mFeedbackVideoViewCallback = null;
+        }
+        if (useGestureAnimationDelay) {
+            mFeedbackViewCallback = onStartRunnable;
+            mFeedbackVideoViewCallback = () -> {
+                mFeedbackVideoView.setVisibility(View.VISIBLE);
+                tutorialAnimation.start();
+            };
+
+            mFeedbackVideoView.setVisibility(View.GONE);
+            mFeedbackView.post(mFeedbackViewCallback);
+            mFeedbackVideoView.postDelayed(mFeedbackVideoViewCallback, GESTURE_ANIMATION_DELAY_MS);
+        } else {
+            mFeedbackVideoView.setVisibility(View.VISIBLE);
+            tutorialAnimation.start();
+        }
     }
 
     void setRippleHotspot(float x, float y) {
@@ -138,87 +354,147 @@
         }, RIPPLE_VISIBLE_MS);
     }
 
-    void onActionButtonClicked(View button) {}
-
-    void onActionTextButtonClicked(View button) {}
-
-    void showHandCoachingAnimation() {
-        if (isComplete()) {
-            return;
-        }
-        mHandCoachingAnimation.startLoopedAnimation(mTutorialType);
-    }
-
-    void hideHandCoachingAnimation() {
-        mHandCoachingAnimation.stop();
-        mHandCoachingView.setVisibility(View.INVISIBLE);
+    void onActionButtonClicked(View button) {
+        mTutorialFragment.continueTutorial();
     }
 
     @CallSuper
     void transitToController() {
-        hideFeedback();
-        updateTitles();
-        updateActionButtons();
+        hideFeedback(false);
+        hideActionButton();
+        updateSubtext();
+        updateDrawables();
 
-        if (isComplete()) {
-            hideHandCoachingAnimation();
-        } else {
-            showHandCoachingAnimation();
+        mGestureCompleted = false;
+        if (mFakeHotseatView != null) {
+            mFakeHotseatView.setVisibility(View.INVISIBLE);
         }
     }
 
-    private void updateTitles() {
-        updateTitleView(mTitleTextView, getTitleStringId(),
-                R.style.TextAppearance_GestureTutorial_Title);
-        updateTitleView(mSubtitleTextView, getSubtitleStringId(),
-                R.style.TextAppearance_GestureTutorial_Subtitle);
+    void hideCloseButton() {
+        mCloseButton.setVisibility(GONE);
     }
 
-    private void updateTitleView(TextView textView, @Nullable Integer stringId, int styleId) {
-        if (stringId == null) {
-            textView.setVisibility(View.GONE);
-            return;
+    void showCloseButton() {
+        mCloseButton.setVisibility(View.VISIBLE);
+        mCloseButton.setTextAppearance(Utilities.isDarkTheme(mContext)
+                ? R.style.TextAppearance_GestureTutorial_Feedback_Subtext
+                : R.style.TextAppearance_GestureTutorial_Feedback_Subtext_Dark);
+    }
+
+    void hideActionButton() {
+        showCloseButton();
+        // Invisible to maintain the layout.
+        mActionButton.setVisibility(View.INVISIBLE);
+        mActionButton.setOnClickListener(null);
+    }
+
+    void showActionButton() {
+        hideCloseButton();
+        mActionButton.setVisibility(View.VISIBLE);
+        mActionButton.setOnClickListener(this::onActionButtonClicked);
+    }
+
+    private void updateSubtext() {
+        mTutorialStepView.setTutorialProgress(
+                mTutorialFragment.getCurrentStep(), mTutorialFragment.getNumSteps());
+    }
+
+    private void updateDrawables() {
+        if (mContext != null) {
+            mTutorialFragment.getRootView().setBackground(AppCompatResources.getDrawable(
+                    mContext, getMockWallpaperResId()));
+            mTutorialFragment.updateFeedbackVideo();
+            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))));
+            mFakeTaskView.animate().alpha(1).setListener(
+                    AnimatorListeners.forSuccessCallback(() -> mFakeTaskView.animate().cancel()));
+            mFakePreviousTaskView.setBackground(AppCompatResources.getDrawable(
+                    mContext, getMockPreviousAppTaskThumbnailResId()));
+            mFakeIconView.setBackground(AppCompatResources.getDrawable(
+                    mContext, getMockAppIconResId()));
+        }
+    }
+
+    private AlertDialog createSkipTutorialDialog() {
+        if (mContext instanceof GestureSandboxActivity) {
+            GestureSandboxActivity sandboxActivity = (GestureSandboxActivity) mContext;
+            View contentView = View.inflate(
+                    sandboxActivity, R.layout.gesture_tutorial_dialog, null);
+            AlertDialog tutorialDialog = new AlertDialog
+                    .Builder(sandboxActivity, R.style.Theme_AppCompat_Dialog_Alert)
+                    .setView(contentView)
+                    .create();
+
+            PackageManager packageManager = mContext.getPackageManager();
+            CharSequence tipsAppName = DEFAULT_PIXEL_TIPS_APP_NAME;
+
+            try {
+                tipsAppName = packageManager.getApplicationLabel(
+                        packageManager.getApplicationInfo(
+                                PIXEL_TIPS_APP_PACKAGE_NAME, PackageManager.GET_META_DATA));
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG,
+                        "Could not find app label for package name: "
+                                + PIXEL_TIPS_APP_PACKAGE_NAME
+                                + ". Defaulting to 'Pixel Tips.'",
+                        e);
+            }
+
+            TextView subtitleTextView = (TextView) contentView.findViewById(
+                    R.id.gesture_tutorial_dialog_subtitle);
+            if (subtitleTextView != null) {
+                subtitleTextView.setText(
+                        mContext.getString(R.string.skip_tutorial_dialog_subtitle, tipsAppName));
+            } else {
+                Log.w(TAG, "No subtitle view in the skip tutorial dialog to update.");
+            }
+
+            Button cancelButton = (Button) contentView.findViewById(
+                    R.id.gesture_tutorial_dialog_cancel_button);
+            if (cancelButton != null) {
+                cancelButton.setOnClickListener(
+                        v -> tutorialDialog.dismiss());
+            } else {
+                Log.w(TAG, "No cancel button in the skip tutorial dialog to update.");
+            }
+
+            Button confirmButton = contentView.findViewById(
+                    R.id.gesture_tutorial_dialog_confirm_button);
+            if (confirmButton != null) {
+                confirmButton.setOnClickListener(v -> {
+                    sandboxActivity.closeTutorial();
+                    tutorialDialog.dismiss();
+                });
+            } else {
+                Log.w(TAG, "No confirm button in the skip tutorial dialog to update.");
+            }
+
+            tutorialDialog.getWindow().setBackgroundDrawable(
+                    new ColorDrawable(sandboxActivity.getColor(android.R.color.transparent)));
+
+            return tutorialDialog;
         }
 
-        textView.setVisibility(View.VISIBLE);
-        textView.setText(stringId);
-        textView.setTextAppearance(styleId);
-    }
-
-    private void updateActionButtons() {
-        updateButton(mActionButton, getActionButtonStringId(), this::onActionButtonClicked);
-        updateButton(
-                mActionTextButton, getActionTextButtonStringId(), this::onActionTextButtonClicked);
-    }
-
-    private void updateButton(Button button, @Nullable Integer stringId, OnClickListener listener) {
-        if (stringId == null) {
-            button.setVisibility(View.INVISIBLE);
-            return;
-        }
-
-        button.setVisibility(View.VISIBLE);
-        button.setText(stringId);
-        button.setOnClickListener(listener);
-    }
-
-    private boolean isComplete() {
-        return mTutorialType == TutorialType.BACK_NAVIGATION_COMPLETE
-                || mTutorialType == TutorialType.HOME_NAVIGATION_COMPLETE
-                || mTutorialType == TutorialType.OVERVIEW_NAVIGATION_COMPLETE
-                || mTutorialType == TutorialType.ASSISTANT_COMPLETE;
+        return null;
     }
 
     /** Denotes the type of the tutorial. */
     enum TutorialType {
-        RIGHT_EDGE_BACK_NAVIGATION,
-        LEFT_EDGE_BACK_NAVIGATION,
+        BACK_NAVIGATION,
         BACK_NAVIGATION_COMPLETE,
         HOME_NAVIGATION,
         HOME_NAVIGATION_COMPLETE,
         OVERVIEW_NAVIGATION,
         OVERVIEW_NAVIGATION_COMPLETE,
         ASSISTANT,
-        ASSISTANT_COMPLETE
+        ASSISTANT_COMPLETE,
+        SANDBOX_MODE
     }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 9a8264d..7637450 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -15,8 +15,13 @@
  */
 package com.android.quickstep.interaction;
 
+import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Insets;
+import android.graphics.drawable.Animatable2;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -25,6 +30,7 @@
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
+import android.widget.ImageView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -32,6 +38,7 @@
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
 abstract class TutorialFragment extends Fragment implements OnTouchListener {
@@ -41,17 +48,25 @@
 
     TutorialType mTutorialType;
     @Nullable TutorialController mTutorialController = null;
-    View mRootView;
-    TutorialHandAnimation mHandCoachingAnimation;
+    RootSandboxLayout mRootView;
     EdgeBackGestureHandler mEdgeBackGestureHandler;
     NavBarGestureHandler mNavBarGestureHandler;
+    private ImageView mFeedbackVideoView;
+    private ImageView mGestureVideoView;
+
+    @Nullable private AnimatedVectorDrawable mTutorialAnimation = null;
+    @Nullable private AnimatedVectorDrawable mGestureAnimation = null;
+    private boolean mIntroductionShown = false;
+
+    private boolean mFragmentStopped = false;
 
     public static TutorialFragment newInstance(TutorialType tutorialType) {
         TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
         if (fragment == null) {
             fragment = new BackGestureTutorialFragment();
-            tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
+            tutorialType = TutorialType.BACK_NAVIGATION;
         }
+
         Bundle args = new Bundle();
         args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
         fragment.setArguments(args);
@@ -61,8 +76,7 @@
     @Nullable
     private static TutorialFragment getFragmentForTutorialType(TutorialType tutorialType) {
         switch (tutorialType) {
-            case RIGHT_EDGE_BACK_NAVIGATION:
-            case LEFT_EDGE_BACK_NAVIGATION:
+            case BACK_NAVIGATION:
             case BACK_NAVIGATION_COMPLETE:
                 return new BackGestureTutorialFragment();
             case HOME_NAVIGATION:
@@ -74,13 +88,31 @@
             case ASSISTANT:
             case ASSISTANT_COMPLETE:
                 return new AssistantGestureTutorialFragment();
+            case SANDBOX_MODE:
+                return new SandboxModeTutorialFragment();
             default:
                 Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name());
         }
         return null;
     }
 
-    abstract int getHandAnimationResId();
+    @Nullable Integer getFeedbackVideoResId(boolean forDarkMode) {
+        return null;
+    }
+
+    @Nullable Integer getGestureVideoResId() {
+        return null;
+    }
+
+    @Nullable
+    AnimatedVectorDrawable getTutorialAnimation() {
+        return mTutorialAnimation;
+    }
+
+    @Nullable
+    AnimatedVectorDrawable getGestureAnimation() {
+        return mGestureAnimation;
+    }
 
     abstract TutorialController createController(TutorialType type);
 
@@ -107,39 +139,142 @@
             @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         super.onCreateView(inflater, container, savedInstanceState);
 
-        mRootView = inflater.inflate(R.layout.gesture_tutorial_fragment, container, false);
+        mRootView = (RootSandboxLayout) inflater.inflate(
+                R.layout.gesture_tutorial_fragment, container, false);
         mRootView.setOnApplyWindowInsetsListener((view, insets) -> {
             Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());
             mEdgeBackGestureHandler.setInsets(systemInsets.left, systemInsets.right);
             return insets;
         });
         mRootView.setOnTouchListener(this);
-        mHandCoachingAnimation = new TutorialHandAnimation(getContext(), mRootView,
-                getHandAnimationResId());
+        mFeedbackVideoView = mRootView.findViewById(R.id.gesture_tutorial_feedback_video);
+        mGestureVideoView = mRootView.findViewById(R.id.gesture_tutorial_gesture_video);
         return mRootView;
     }
 
     @Override
+    public void onStop() {
+        super.onStop();
+        releaseFeedbackVideoView();
+        releaseGestureVideoView();
+        mFragmentStopped = true;
+    }
+
+    void initializeFeedbackVideoView() {
+        if (!updateFeedbackVideo()) {
+            return;
+        }
+
+        if (!mIntroductionShown && mTutorialController != null) {
+            Integer introTileStringResId = mTutorialController.getIntroductionTitle();
+            Integer introSubtitleResId = mTutorialController.getIntroductionSubtitle();
+            if (introTileStringResId != null && introSubtitleResId != null) {
+                mTutorialController.showFeedback(
+                        introTileStringResId, introSubtitleResId, false, true);
+                mIntroductionShown = true;
+            }
+        }
+    }
+
+    boolean updateFeedbackVideo() {
+        if (getContext() == null) {
+            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);
+
+        if (mGestureAnimation != null) {
+            mGestureAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
+
+                @Override
+                public void onAnimationEnd(Drawable drawable) {
+                    super.onAnimationEnd(drawable);
+
+                    mGestureAnimation.start();
+                }
+            });
+        }
+        mGestureVideoView.setImageDrawable(mGestureAnimation);
+
+        return true;
+    }
+
+    void releaseFeedbackVideoView() {
+        if (mTutorialAnimation != null && mTutorialAnimation.isRunning()) {
+            mTutorialAnimation.stop();
+        }
+
+        mFeedbackVideoView.setVisibility(View.GONE);
+    }
+
+    void releaseGestureVideoView() {
+        if (mGestureAnimation != null && mGestureAnimation.isRunning()) {
+            mGestureAnimation.stop();
+        }
+
+        mGestureVideoView.setVisibility(View.GONE);
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
-        changeController(mTutorialType);
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        mHandCoachingAnimation.stop();
+        if (mFragmentStopped && mTutorialController != null) {
+            mTutorialController.showFeedback();
+            mFragmentStopped = false;
+        } else {
+            changeController(mTutorialType);
+        }
     }
 
     @Override
     public boolean onTouch(View view, MotionEvent motionEvent) {
-        // Note: Using logical or to ensure both functions get called.
+        // Note: Using logical-or to ensure both functions get called.
         return mEdgeBackGestureHandler.onTouch(view, motionEvent)
                 | mNavBarGestureHandler.onTouch(view, motionEvent);
     }
 
+    boolean onInterceptTouch(MotionEvent motionEvent) {
+        // Note: Using logical-or to ensure both functions get called.
+        return mEdgeBackGestureHandler.onInterceptTouch(motionEvent)
+                | mNavBarGestureHandler.onInterceptTouch(motionEvent);
+    }
+
     void onAttachedToWindow() {
-        mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView());
+        mEdgeBackGestureHandler.setViewGroupParent(getRootView());
     }
 
     void onDetachedFromWindow() {
@@ -149,13 +284,15 @@
     void changeController(TutorialType tutorialType) {
         if (getControllerClass().isInstance(mTutorialController)) {
             mTutorialController.setTutorialType(tutorialType);
+            mTutorialController.fadeTaskViewAndRun(mTutorialController::transitToController);
         } else {
             mTutorialController = createController(tutorialType);
+            mTutorialController.transitToController();
         }
-        mTutorialController.transitToController();
         mEdgeBackGestureHandler.registerBackGestureAttemptCallback(mTutorialController);
         mNavBarGestureHandler.registerNavBarGestureAttemptCallback(mTutorialController);
         mTutorialType = tutorialType;
+        initializeFeedbackVideoView();
     }
 
     @Override
@@ -164,17 +301,24 @@
         super.onSaveInstanceState(savedInstanceState);
     }
 
-    View getRootView() {
+    RootSandboxLayout getRootView() {
         return mRootView;
     }
 
-    TutorialHandAnimation getHandAnimation() {
-        return mHandCoachingAnimation;
+    void continueTutorial() {
+        GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
+
+        if (gestureSandboxActivity == null) {
+            closeTutorial();
+            return;
+        }
+        gestureSandboxActivity.continueTutorial();
     }
 
     void closeTutorial() {
         FragmentActivity activity = getActivity();
         if (activity != null) {
+            activity.setResult(Activity.RESULT_OK);
             activity.finish();
         }
     }
@@ -182,4 +326,27 @@
     void startSystemNavigationSetting() {
         startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
     }
+
+    int getCurrentStep() {
+        GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
+
+        return gestureSandboxActivity == null ? -1 : gestureSandboxActivity.getCurrentStep();
+    }
+
+    int getNumSteps() {
+        GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
+
+        return gestureSandboxActivity == null ? -1 : gestureSandboxActivity.getNumSteps();
+    }
+
+    boolean isAtFinalStep() {
+        return getCurrentStep() == getNumSteps();
+    }
+
+    @Nullable
+    private GestureSandboxActivity getGestureSandboxActivity() {
+        Context context = getContext();
+
+        return context instanceof GestureSandboxActivity ? (GestureSandboxActivity) context : null;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialHandAnimation.java b/quickstep/src/com/android/quickstep/interaction/TutorialHandAnimation.java
deleted file mode 100644
index c810e43..0000000
--- a/quickstep/src/com/android/quickstep/interaction/TutorialHandAnimation.java
+++ /dev/null
@@ -1,78 +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.interaction;
-
-import android.content.Context;
-import android.graphics.drawable.Animatable2;
-import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.core.content.ContextCompat;
-
-import com.android.launcher3.R;
-import com.android.quickstep.interaction.TutorialController.TutorialType;
-
-import java.time.Duration;
-
-/** Hand coaching animation. */
-final class TutorialHandAnimation {
-
-    // A delay for waiting the Activity fully launches.
-    private static final Duration ANIMATION_START_DELAY = Duration.ofMillis(300L);
-
-    private final ImageView mHandCoachingView;
-    private final AnimatedVectorDrawable mGestureAnimation;
-
-    TutorialHandAnimation(Context context, View rootView, int resId) {
-        mHandCoachingView = rootView.findViewById(R.id.gesture_tutorial_fragment_hand_coaching);
-        mGestureAnimation = (AnimatedVectorDrawable) ContextCompat.getDrawable(context, resId);
-    }
-
-    /** [Re]starts animation for the given tutorial. */
-    void startLoopedAnimation(TutorialType tutorialType) {
-        mHandCoachingView.setVisibility(View.VISIBLE);
-        if (mGestureAnimation.isRunning()) {
-            stop();
-        }
-
-        mGestureAnimation.clearAnimationCallbacks();
-        mGestureAnimation.registerAnimationCallback(
-                new Animatable2.AnimationCallback() {
-                    @Override
-                    public void onAnimationEnd(Drawable drawable) {
-                        super.onAnimationEnd(drawable);
-                        mGestureAnimation.start();
-                    }
-                });
-        start(tutorialType);
-    }
-
-    private void start(TutorialType tutorialType) {
-        // Because the gesture animation has only the right side form.
-        // The left side form of the gesture animation is made from flipping the View.
-        float rotationY = tutorialType == TutorialType.LEFT_EDGE_BACK_NAVIGATION ? 180f : 0f;
-        mHandCoachingView.setRotationY(rotationY);
-        mHandCoachingView.setImageDrawable(mGestureAnimation);
-        mHandCoachingView.postDelayed(mGestureAnimation::start, ANIMATION_START_DELAY.toMillis());
-    }
-
-    void stop() {
-        mGestureAnimation.clearAnimationCallbacks();
-        mGestureAnimation.stop();
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
new file mode 100644
index 0000000..d880d74
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialStepIndicator.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.interaction;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import androidx.appcompat.content.res.AppCompatResources;
+import androidx.core.graphics.ColorUtils;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.GraphicsUtils;
+
+/** Indicator displaying the current progress through the gesture navigation tutorial. */
+public class TutorialStepIndicator extends LinearLayout {
+
+    private static final String LOG_TAG = "TutorialStepIndicator";
+
+    private int mCurrentStep = -1;
+    private int mTotalSteps = -1;
+
+    public TutorialStepIndicator(Context context) {
+        super(context);
+    }
+
+    public TutorialStepIndicator(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public TutorialStepIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public TutorialStepIndicator(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Updates this indicator to display totalSteps indicator pills, with the first currentStep
+     * pills highlighted.
+     */
+    public void setTutorialProgress(int currentStep, int totalSteps) {
+        if (currentStep <= 0) {
+            Log.w(LOG_TAG, "Current step number invalid: " + currentStep + ". Assuming step 1.");
+            currentStep = 1;
+        }
+        if (totalSteps <= 0) {
+            Log.w(LOG_TAG, "Total number of steps invalid: " + totalSteps + ". Assuming 1 step.");
+            totalSteps = 1;
+        }
+        if (currentStep > totalSteps) {
+            Log.w(LOG_TAG, "Current step number greater than the total number of steps. Assuming"
+                    + " final step.");
+            currentStep = totalSteps;
+        }
+        if (totalSteps < 2) {
+            setVisibility(GONE);
+            return;
+        }
+        setVisibility(VISIBLE);
+        mCurrentStep = currentStep;
+        mTotalSteps = totalSteps;
+
+        initializeStepIndicators();
+    }
+
+    private void initializeStepIndicators() {
+        for (int i = mTotalSteps; i < getChildCount(); i++) {
+            removeViewAt(i);
+        }
+        int stepIndicatorColor = GraphicsUtils.getAttrColor(
+                getContext(), android.R.attr.textColorPrimary);
+        for (int i = 0; i < mTotalSteps; i++) {
+            Drawable pageIndicatorPillDrawable = AppCompatResources.getDrawable(
+                    getContext(), R.drawable.tutorial_step_indicator_pill);
+
+            if (i >= getChildCount()) {
+                ImageView pageIndicatorPill = new ImageView(getContext());
+                pageIndicatorPill.setImageDrawable(pageIndicatorPillDrawable);
+
+                LinearLayout.LayoutParams lp = new LayoutParams(
+                        LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+
+                lp.setMarginStart(Utilities.dpToPx(3));
+                lp.setMarginEnd(Utilities.dpToPx(3));
+
+                addView(pageIndicatorPill, lp);
+            }
+            if (pageIndicatorPillDrawable != null) {
+                if (i < mCurrentStep) {
+                    pageIndicatorPillDrawable.setTint(stepIndicatorColor);
+                } else {
+                    pageIndicatorPillDrawable.setTint(
+                            ColorUtils.setAlphaComponent(stepIndicatorColor, 0x22));
+                }
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
new file mode 100644
index 0000000..7eca360
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -0,0 +1,193 @@
+/*
+ * 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.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.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;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.TypedArray;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Xml;
+
+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.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.util.SettingsCache;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Utility class to log launcher settings changes
+ */
+public class SettingsChangeLogger implements
+        NavigationModeChangeListener, OnSharedPreferenceChangeListener {
+
+    private static final String TAG = "SettingsChangeLogger";
+    private static final String ROOT_TAG = "androidx.preference.PreferenceScreen";
+    private static final String BOOLEAN_PREF = "SwitchPreference";
+
+    private final Context mContext;
+    private final ArrayMap<String, LoggablePref> mLoggablePrefs;
+
+    private Mode mNavMode;
+    private boolean mNotificationDotsEnabled;
+
+    public SettingsChangeLogger(Context context) {
+        mContext = context;
+        mLoggablePrefs = loadPrefKeys(context);
+        mNavMode = SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
+
+        getPrefs(context).registerOnSharedPreferenceChangeListener(this);
+        getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
+
+        SettingsCache mSettingsCache = SettingsCache.INSTANCE.get(context);
+        mSettingsCache.register(NOTIFICATION_BADGING_URI,
+                this::onNotificationDotsChanged);
+        onNotificationDotsChanged(mSettingsCache.getValue(NOTIFICATION_BADGING_URI));
+    }
+
+    private static ArrayMap<String, LoggablePref> loadPrefKeys(Context context) {
+        XmlPullParser parser = context.getResources().getXml(R.xml.launcher_preferences);
+        ArrayMap<String, LoggablePref> result = new ArrayMap<>();
+
+        try {
+            AutoInstallsLayout.beginDocument(parser, ROOT_TAG);
+            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) {
+                    continue;
+                }
+                if (BOOLEAN_PREF.equals(parser.getName())) {
+                    TypedArray a = context.obtainStyledAttributes(
+                            Xml.asAttributeSet(parser), R.styleable.LoggablePref);
+                    String key = a.getString(R.styleable.LoggablePref_android_key);
+                    LoggablePref pref = new LoggablePref();
+                    pref.defaultValue =
+                            a.getBoolean(R.styleable.LoggablePref_android_defaultValue, true);
+                    pref.eventIdOn = a.getInt(R.styleable.LoggablePref_logIdOn, 0);
+                    pref.eventIdOff = a.getInt(R.styleable.LoggablePref_logIdOff, 0);
+                    if (pref.eventIdOff > 0 && pref.eventIdOn > 0) {
+                        result.put(key, pref);
+                    }
+                }
+            }
+        } catch (XmlPullParserException | IOException e) {
+            Log.e(TAG, "Error parsing preference xml", e);
+        }
+        return result;
+    }
+
+    private void onNotificationDotsChanged(boolean isDotsEnabled) {
+        mNotificationDotsEnabled = isDotsEnabled;
+        dispatchUserEvent();
+    }
+
+    @Override
+    public void onNavigationModeChanged(Mode newMode) {
+        mNavMode = newMode;
+        dispatchUserEvent();
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+        if (LAST_PREDICTION_ENABLED_STATE.equals(key) || KEY_MIGRATION_SRC_HOTSEAT_COUNT.equals(key)
+                || mLoggablePrefs.containsKey(key)) {
+            dispatchUserEvent();
+        }
+    }
+
+    private void dispatchUserEvent() {
+        StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
+                .withInstanceId(new InstanceIdSequence().newInstanceId());
+
+        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);
+
+        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
+                    : LAUNCHER_THEMED_ICON_DISABLED);
+        }
+
+        mLoggablePrefs.forEach((key, lp) -> logger.log(() ->
+                prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff));
+    }
+
+    private static class LoggablePref {
+        public boolean defaultValue;
+        public int eventIdOn;
+        public int eventIdOff;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 256ad82..6575996 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,24 +16,26 @@
 
 package com.android.quickstep.logging;
 
-import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static android.text.format.DateUtils.formatElapsedTime;
+import static androidx.core.util.Preconditions.checkNotNull;
+import static androidx.core.util.Preconditions.checkState;
 
-import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
+import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
 import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
 
-import static java.lang.System.currentTimeMillis;
-
 import android.content.Context;
 import android.util.Log;
+import android.view.View;
 
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
+import androidx.slice.SliceItem;
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.Utilities;
@@ -43,25 +45,23 @@
 import com.android.launcher3.logger.LauncherAtom.FolderIcon;
 import com.android.launcher3.logger.LauncherAtom.FromState;
 import com.android.launcher3.logger.LauncherAtom.ToState;
+import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer;
+import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
 import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
 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.Executors;
-import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.LogConfig;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 import com.android.systemui.shared.system.SysUiStatsLog;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Optional;
 import java.util.OptionalInt;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * This class calls StatsLog compile time generated methods.
@@ -77,14 +77,17 @@
 
     private static final String TAG = "StatsLog";
     private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
-
-    private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
     private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
     // LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
     // from nano to lite, bake constant to prevent robo test failure.
     private static final int DEFAULT_PAGE_INDEX = -2;
     private static final int FOLDER_HIERARCHY_OFFSET = 100;
     private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
+    private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300;
+    private static final int ATTRIBUTE_MULTIPLIER = 100;
+
+    public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER =
+            new CopyOnWriteArrayList<>();
 
     private final Context mContext;
 
@@ -93,89 +96,15 @@
     }
 
     @Override
-    public StatsLogger logger() {
-        return new StatsCompatLogger();
+    protected StatsLogger createLogger() {
+        return new StatsCompatLogger(mContext);
     }
 
     /**
-     * Logs a ranking event and accompanying {@link InstanceId} and package name.
+     * Synchronously writes an itemInfo to stats log
      */
-    @Override
-    public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
-            int position) {
-        SysUiStatsLog.write(SysUiStatsLog.RANKING_SELECTED,
-                rankingEvent.getId() /* event_id = 1; */,
-                packageName /* package_name = 2; */,
-                instanceId.getId() /* instance_id = 3; */,
-                position /* position_picked = 4; */);
-    }
-
-    /**
-     * Logs impression of the current workspace with additional launcher events.
-     */
-    @Override
-    public void logSnapshot(List<EventEnum> extraEvents) {
-        LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
-                new SnapshotWorker(extraEvents));
-    }
-
-    private class SnapshotWorker extends BaseModelUpdateTask {
-        private final InstanceId mInstanceId;
-        private final List<EventEnum> mExtraEvents;
-
-        SnapshotWorker(List<EventEnum> extraEvents) {
-            mInstanceId = new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
-                    .newInstanceId();
-            this.mExtraEvents = extraEvents;
-        }
-
-        @Override
-        public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-            long lastSnapshotTimeMillis = getDevicePrefs(mContext)
-                    .getLong(LAST_SNAPSHOT_TIME_MILLIS, 0);
-            // Log snapshot only if previous snapshot was older than a day
-            if (currentTimeMillis() - lastSnapshotTimeMillis < DAY_IN_MILLIS) {
-                if (IS_VERBOSE) {
-                    String elapsedTime = formatElapsedTime(
-                            (currentTimeMillis() - lastSnapshotTimeMillis) / 1000);
-                    Log.d(TAG, String.format(
-                            "Skipped snapshot logging since previous snapshot was %s old.",
-                            elapsedTime));
-                }
-                return;
-            }
-
-            IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
-            ArrayList<ItemInfo> workspaceItems = (ArrayList) dataModel.workspaceItems.clone();
-            ArrayList<LauncherAppWidgetInfo> appWidgets = (ArrayList) dataModel.appWidgets.clone();
-            for (ItemInfo info : workspaceItems) {
-                LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
-                writeSnapshot(atomInfo, mInstanceId);
-            }
-            for (FolderInfo fInfo : folders) {
-                try {
-                    ArrayList<WorkspaceItemInfo> folderContents =
-                            (ArrayList) Executors.MAIN_EXECUTOR.submit(fInfo.contents::clone).get();
-                    for (ItemInfo info : folderContents) {
-                        LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
-                        writeSnapshot(atomInfo, mInstanceId);
-                    }
-                } catch (Exception e) {
-                }
-            }
-            for (ItemInfo info : appWidgets) {
-                LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
-                writeSnapshot(atomInfo, mInstanceId);
-            }
-            mExtraEvents
-                    .forEach(eventName -> logger().withInstanceId(mInstanceId).log(eventName));
-
-            getDevicePrefs(mContext).edit()
-                    .putLong(LAST_SNAPSHOT_TIME_MILLIS, currentTimeMillis()).apply();
-        }
-    }
-
-    private void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
+    @WorkerThread
+    public static void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
         if (IS_VERBOSE) {
             Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
         }
@@ -184,7 +113,8 @@
         }
         SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_SNAPSHOT,
                 LAUNCHER_WORKSPACE_SNAPSHOT.getId() /* event_id */,
-                info.getItemCase().getNumber() /* target_id */,
+                info.getAttribute().getNumber() * ATTRIBUTE_MULTIPLIER
+                        + info.getItemCase().getNumber()  /* target_id */,
                 instanceId.getId() /* instance_id */,
                 0 /* uid */,
                 getPackageName(info) /* package_name */,
@@ -200,7 +130,8 @@
                 info.getAttribute().getNumber() /* origin */,
                 getCardinality(info) /* cardinality */,
                 info.getWidget().getSpanX(),
-                info.getWidget().getSpanY());
+                info.getWidget().getSpanY(),
+                getFeatures(info));
     }
 
     /**
@@ -209,6 +140,8 @@
     private static class StatsCompatLogger implements StatsLogger {
 
         private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
+
+        private Context mContext;
         private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
         private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
         private OptionalInt mRank = OptionalInt.empty();
@@ -218,6 +151,12 @@
         private Optional<FromState> mFromState = Optional.empty();
         private Optional<ToState> mToState = Optional.empty();
         private Optional<String> mEditText = Optional.empty();
+        private SliceItem mSliceItem;
+        private LauncherAtom.Slice mSlice;
+
+        StatsCompatLogger(Context context) {
+            mContext = context;
+        }
 
         @Override
         public StatsLogger withItemInfo(ItemInfo itemInfo) {
@@ -255,10 +194,8 @@
 
         @Override
         public StatsLogger withContainerInfo(ContainerInfo containerInfo) {
-            if (mItemInfo != DEFAULT_ITEM_INFO) {
-                throw new IllegalArgumentException(
-                        "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
-            }
+            checkState(mItemInfo == DEFAULT_ITEM_INFO,
+                    "ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
             this.mContainerInfo = Optional.of(containerInfo);
             return this;
         }
@@ -282,41 +219,96 @@
         }
 
         @Override
+        public StatsLogger withSliceItem(@NonNull SliceItem sliceItem) {
+            checkState(mItemInfo == DEFAULT_ITEM_INFO && mSlice == null,
+                    "ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one"
+                            + " of them.");
+            this.mSliceItem = checkNotNull(sliceItem, "expected valid sliceItem but received null");
+            return this;
+        }
+
+        @Override
+        public StatsLogger withSlice(LauncherAtom.Slice slice) {
+            checkState(mItemInfo == DEFAULT_ITEM_INFO && mSliceItem == null,
+                    "ItemInfo, Slice and SliceItem are mutual exclusive; cannot set more than one"
+                            + " of them.");
+            checkNotNull(slice, "expected valid slice but received null");
+            checkNotNull(slice.getUri(), "expected valid slice uri but received null");
+            this.mSlice = slice;
+            return this;
+        }
+
+        @Override
         public void log(EventEnum event) {
             if (!Utilities.ATLEAST_R) {
                 return;
             }
+            LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
 
-            if (mItemInfo.container < 0) {
-                // Item is not within a folder. Write to StatsLog in same thread.
-                write(event, mInstanceId, applyOverwrites(mItemInfo.buildProto()), mSrcState,
-                        mDstState);
+            if (mSlice == null && mSliceItem != null) {
+                mSlice = LauncherAtom.Slice.newBuilder().setUri(
+                        mSliceItem.getSlice().getUri().toString()).build();
+            }
+
+            if (mSlice != null) {
+                Executors.MODEL_EXECUTOR.execute(
+                        () -> {
+                            LauncherAtom.ItemInfo.Builder itemInfoBuilder =
+                                    LauncherAtom.ItemInfo.newBuilder().setSlice(mSlice);
+                            mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
+                            write(event, applyOverwrites(itemInfoBuilder.build()));
+                        });
+                return;
+            }
+
+            if (mItemInfo.container < 0 || appState == null) {
+                // Write log on the model thread so that logs do not go out of order
+                // (for eg: drop comes after drag)
+                Executors.MODEL_EXECUTOR.execute(
+                        () -> write(event, applyOverwrites(mItemInfo.buildProto())));
             } else {
                 // Item is inside the folder, fetch folder info in a BG thread
                 // and then write to StatsLog.
-                LauncherAppState.getInstanceNoCreate().getModel().enqueueModelUpdateTask(
+                appState.getModel().enqueueModelUpdateTask(
                         new BaseModelUpdateTask() {
                             @Override
                             public void execute(LauncherAppState app, BgDataModel dataModel,
                                     AllAppsList apps) {
                                 FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
-                                write(event, mInstanceId,
-                                        applyOverwrites(mItemInfo.buildProto(folderInfo)),
-                                        mSrcState, mDstState);
+                                write(event, applyOverwrites(mItemInfo.buildProto(folderInfo)));
                             }
                         });
             }
         }
 
+        @Override
+        public void sendToInteractionJankMonitor(EventEnum event, View view) {
+            if (!(event instanceof LauncherEvent)) {
+                return;
+            }
+            switch ((LauncherEvent) event) {
+                case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN:
+                    InteractionJankMonitorWrapper.begin(
+                            view,
+                            InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
+                    break;
+                case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END:
+                    InteractionJankMonitorWrapper.end(
+                            InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
+                    break;
+                default:
+                    break;
+            }
+        }
+
         private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
-            LauncherAtom.ItemInfo.Builder itemInfoBuilder =
-                    (LauncherAtom.ItemInfo.Builder) atomInfo.toBuilder();
+            LauncherAtom.ItemInfo.Builder itemInfoBuilder = atomInfo.toBuilder();
 
             mRank.ifPresent(itemInfoBuilder::setRank);
             mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
 
             if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
-                FolderIcon.Builder folderIconBuilder = (FolderIcon.Builder) itemInfoBuilder
+                FolderIcon.Builder folderIconBuilder = itemInfoBuilder
                         .getFolderIcon()
                         .toBuilder();
                 mFromState.ifPresent(folderIconBuilder::setFromLabelState);
@@ -327,8 +319,11 @@
             return itemInfoBuilder.build();
         }
 
-        private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo,
-                int srcState, int dstState) {
+        @WorkerThread
+        private void write(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
+            InstanceId instanceId = mInstanceId;
+            int srcState = mSrcState;
+            int dstState = mDstState;
             if (IS_VERBOSE) {
                 String name = (event instanceof Enum) ? ((Enum) event).name() :
                         event.getId() + "";
@@ -341,6 +336,10 @@
                                 atomInfo));
             }
 
+            for (StatsLogConsumer consumer : LOGS_CONSUMER) {
+                consumer.consume(event, atomInfo);
+            }
+
             SysUiStatsLog.write(
                     SysUiStatsLog.LAUNCHER_EVENT,
                     SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
@@ -349,7 +348,8 @@
                     null /* launcher extensions, deprecated */,
                     false /* quickstep_enabled, deprecated */,
                     event.getId() /* event_id */,
-                    atomInfo.getItemCase().getNumber() /* target_id */,
+                    atomInfo.getAttribute().getNumber() * ATTRIBUTE_MULTIPLIER
+                            + atomInfo.getItemCase().getNumber() /* target_id */,
                     instanceId.getId() /* instance_id TODO */,
                     0 /* uid TODO */,
                     getPackageName(atomInfo) /* package_name */,
@@ -366,7 +366,8 @@
                     atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
                     atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
                     atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
-                    getCardinality(atomInfo) /* cardinality */);
+                    getCardinality(atomInfo) /* cardinality */,
+                    getFeatures(atomInfo) /* features */);
         }
     }
 
@@ -376,6 +377,14 @@
                 return info.getContainerInfo().getPredictedHotseatContainer().getCardinality();
             case SEARCH_RESULT_CONTAINER:
                 return info.getContainerInfo().getSearchResultContainer().getQueryLength();
+            case EXTENDED_CONTAINERS:
+                ExtendedContainers extendedCont = info.getContainerInfo().getExtendedContainers();
+                if (extendedCont.getContainerCase() == DEVICE_SEARCH_RESULT_CONTAINER) {
+                    DeviceSearchResultContainer deviceSearchResultCont = extendedCont
+                            .getDeviceSearchResultContainer();
+                    return deviceSearchResultCont.hasQueryLength() ? deviceSearchResultCont
+                            .getQueryLength() : -1;
+                }
             default:
                 return info.getFolderIcon().getCardinality();
         }
@@ -391,6 +400,8 @@
                 return info.getWidget().getPackageName();
             case TASK:
                 return info.getTask().getPackageName();
+            case SEARCH_ACTION_ITEM:
+                return info.getSearchActionItem().getPackageName();
             default:
                 return null;
         }
@@ -406,6 +417,10 @@
                 return info.getWidget().getComponentName();
             case TASK:
                 return info.getTask().getComponentName();
+            case SEARCH_ACTION_ITEM:
+                return info.getSearchActionItem().getTitle();
+            case SLICE:
+                return info.getSlice().getUri();
             default:
                 return null;
         }
@@ -474,6 +489,9 @@
         } else if (info.getContainerInfo().getContainerCase() == SEARCH_RESULT_CONTAINER) {
             return info.getContainerInfo().getSearchResultContainer().getParentContainerCase()
                     .getNumber() + SEARCH_RESULT_HIERARCHY_OFFSET;
+        } else if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS) {
+            return info.getContainerInfo().getExtendedContainers().getContainerCase().getNumber()
+                    + EXTENDED_CONTAINERS_HIERARCHY_OFFSET;
         } else {
             return info.getContainerInfo().getContainerCase().getNumber();
         }
@@ -491,7 +509,23 @@
                 return "ALLAPPS";
             default:
                 return "INVALID";
-
         }
     }
+
+    private static int getFeatures(LauncherAtom.ItemInfo info) {
+        if (info.getItemCase().equals(LauncherAtom.ItemInfo.ItemCase.WIDGET)) {
+            return info.getWidget().getWidgetFeatures();
+        }
+        return 0;
+    }
+
+
+    /**
+     * Interface to get stats log while it is dispatched to the system
+     */
+    public interface StatsLogConsumer {
+
+        @WorkerThread
+        void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
deleted file mode 100644
index 9ca7f23..0000000
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.logging;
-
-import android.content.Context;
-import android.util.Log;
-
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET;
-import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS;
-import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
-import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
-import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
-
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.systemui.shared.system.MetricsLoggerCompat;
-
-/**
- * This class handles AOSP MetricsLogger function calls and logging around
- * quickstep interactions.
- */
-@SuppressWarnings("unused")
-public class UserEventDispatcherExtension extends UserEventDispatcher {
-
-    public static final int ALL_APPS_PREDICTION_TIPS = 2;
-
-    private static final String TAG = "UserEventDispatcher";
-
-    public UserEventDispatcherExtension(Context context) { }
-
-    public void logStateChangeAction(int action, int dir, int downX, int downY,
-                                     int srcChildTargetType, int srcParentContainerType,
-                                     int dstContainerType, int pageIndex) {
-        new MetricsLoggerCompat().visibility(MetricsLoggerCompat.OVERVIEW_ACTIVITY,
-                dstContainerType == LauncherLogProto.ContainerType.TASKSWITCHER);
-        super.logStateChangeAction(action, dir, downX, downY, srcChildTargetType,
-                srcParentContainerType, dstContainerType, pageIndex);
-    }
-
-    public void logActionTip(int actionType, int viewType) {
-        LauncherLogProto.Action action = new LauncherLogProto.Action();
-        LauncherLogProto.Target target = new LauncherLogProto.Target();
-        switch(actionType) {
-            case VISIBLE:
-                action.type = LauncherLogProto.Action.Type.TIP;
-                target.type = LauncherLogProto.Target.Type.CONTAINER;
-                target.containerType = LauncherLogProto.ContainerType.TIP;
-                break;
-            case DISMISS:
-                action.type = LauncherLogProto.Action.Type.TOUCH;
-                action.touch = LauncherLogProto.Action.Touch.TAP;
-                target.type = LauncherLogProto.Target.Type.CONTROL;
-                target.controlType = CANCEL_TARGET;
-                break;
-            default:
-                Log.e(TAG, "Unexpected action type = " + actionType);
-        }
-
-        switch(viewType) {
-            case RECENTS_QUICK_SCRUB_ONBOARDING_TIP:
-                target.tipType = LauncherLogProto.TipType.QUICK_SCRUB_TEXT;
-                break;
-            case RECENTS_SWIPE_UP_ONBOARDING_TIP:
-                target.tipType = LauncherLogProto.TipType.SWIPE_UP_TEXT;
-                break;
-            default:
-                Log.e(TAG, "Unexpected viewType = " + viewType);
-        }
-        LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
-        dispatchUserEvent(event, null);
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java
rename to quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
index dfb8c1d..b9879ab 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -26,7 +26,8 @@
 
 import java.util.function.BiPredicate;
 
-public class ActivityInitListener<T extends BaseActivity> implements SchedulerCallback<T> {
+public class ActivityInitListener<T extends BaseActivity> implements
+        SchedulerCallback<T> {
 
     private BiPredicate<T, Boolean> mOnInitListener;
     private final ActivityTracker<T> mActivityTracker;
@@ -47,6 +48,7 @@
     @Override
     public final boolean init(T activity, boolean alreadyOnHome) {
         if (!mIsRegistered) {
+            // Don't receive any more updates
             return false;
         }
         return handleInit(activity, alreadyOnHome);
@@ -59,18 +61,17 @@
     /**
      * Registers the activity-created listener. If the activity is already created, then the
      * callback provided in the constructor will be called synchronously.
-     * @param intent The intent that will be used to initialize the activity, if the activity
-     *               doesn't already exist. We add the callback as an extra on this intent.
      */
-    public void register(Intent intent) {
+    public void register() {
         mIsRegistered = true;
-        mActivityTracker.runCallbackWhenActivityExists(this, intent);
+        mActivityTracker.registerCallback(this);
     }
 
     /**
      * After calling this, we won't {@link #init} even when the activity is ready.
      */
     public void unregister() {
+        mActivityTracker.unregisterCallback(this);
         mIsRegistered = false;
         mOnInitListener = null;
     }
@@ -82,9 +83,9 @@
      */
     public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
             Context context, Handler handler, long duration) {
-        mIsRegistered = true;
+        register();
 
         Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
-        context.startActivity(addToIntent(new Intent(intent)), options);
+        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 deb70e0..7f94839 100644
--- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
@@ -38,7 +37,6 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -49,12 +47,6 @@
  */
 public class AnimatorControllerWithResistance {
 
-    /**
-     * How much farther we can drag past overview in 2-button mode, as a factor of the distance
-     * it takes to drag from an app to overview.
-     */
-    public static final float TWO_BUTTON_EXTRA_DRAG_FACTOR = 0.25f;
-
     private enum RecentsResistanceParams {
         FROM_APP(0.75f, 0.5f, 1f),
         FROM_OVERVIEW(1f, 0.75f, 0.5f);
@@ -161,12 +153,6 @@
         LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect,
                 orientationHandler);
         long distanceToCover = startRect.bottom;
-        boolean isTwoButtonMode = SysUINavigationMode.getMode(params.context) == TWO_BUTTONS;
-        if (isTwoButtonMode) {
-            // We can only drag a small distance past overview, not to the top of the screen.
-            distanceToCover = (long)
-                    ((params.dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR);
-        }
         PendingAnimation resistAnim = params.resistAnim != null
                 ? params.resistAnim
                 : new PendingAnimation(distanceToCover * 2);
@@ -178,43 +164,35 @@
                 / (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;
-        final TimeInterpolator scaleInterpolator;
-        if (isTwoButtonMode) {
-            // We are bounded by the distance of the drag, so we don't need to apply resistance.
-            scaleInterpolator = LINEAR;
-        } else {
-            // 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);
-            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);
-            };
-        }
+        // 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);
 
-        if (!isTwoButtonMode) {
-            // Compute where the task view would be based on the end scale, if we didn't translate.
-            RectF endRectF = new RectF(startRect);
-            Matrix temp = new Matrix();
-            temp.setScale(params.resistanceParams.scaleMaxResist,
-                    params.resistanceParams.scaleMaxResist, pivot.x, pivot.y);
-            temp.mapRect(endRectF);
-            // Translate such that the task view touches the top of the screen when drag does.
-            float endTranslation = endRectF.top
-                    * orientationHandler.getSecondaryTranslationDirectionFactor()
-                    * params.resistanceParams.translationFactor;
-            resistAnim.addFloat(params.translationTarget, params.translationProperty,
-                    params.startTranslation, endTranslation, RECENTS_TRANSLATE_RESIST_INTERPOLATOR);
-        }
+        // Compute where the task view would be based on the end scale.
+        RectF endRectF = new RectF(startRect);
+        Matrix temp = new Matrix();
+        temp.setScale(params.resistanceParams.scaleMaxResist,
+                params.resistanceParams.scaleMaxResist, pivot.x, pivot.y);
+        temp.mapRect(endRectF);
+        // Translate such that the task view touches the top of the screen when drag does.
+        float endTranslation = endRectF.top
+                * orientationHandler.getSecondaryTranslationDirectionFactor()
+                * params.resistanceParams.translationFactor;
+        resistAnim.addFloat(params.translationTarget, params.translationProperty,
+                params.startTranslation, endTranslation, RECENTS_TRANSLATE_RESIST_INTERPOLATOR);
 
         return resistAnim;
     }
diff --git a/quickstep/src/com/android/quickstep/util/AppCloseConfig.java b/quickstep/src/com/android/quickstep/util/AppCloseConfig.java
new file mode 100644
index 0000000..bec3379
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AppCloseConfig.java
@@ -0,0 +1,57 @@
+/*
+ * 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.FloatRange;
+import android.annotation.IntRange;
+
+/*
+ * Adds getter methods to {@link MultiValueUpdateListener} specific to app close animation,
+ * so that the entire animation can be defined in one place.
+ */
+public abstract class AppCloseConfig extends MultiValueUpdateListener {
+
+    /**
+     * Returns the translation y of the workspace contents.
+     */
+    public abstract float getWorkspaceTransY();
+
+    /*
+     * Returns the scale of the workspace contents.
+     */
+    public abstract float getWorkspaceScale();
+
+    /*
+     * Returns the alpha of the window.
+     */
+    public abstract @FloatRange(from = 0, to = 1) float getWindowAlpha();
+
+    /*
+     * Returns the alpha of the foreground layer of an adaptive icon.
+     */
+    public abstract @IntRange(from = 0, to = 255) int getFgAlpha();
+
+    /*
+     * Returns the corner radius of the window and icon.
+     */
+    public abstract float getCornerRadius();
+
+    /*
+     * Returns the interpolated progress of the animation.
+     */
+    public abstract float getInterpolatedProgress();
+
+}
diff --git a/quickstep/src/com/android/quickstep/util/AssistContentRequester.java b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
new file mode 100644
index 0000000..b1e38eb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
@@ -0,0 +1,135 @@
+/*
+ * 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.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.IAssistDataReceiver;
+import android.app.assist.AssistContent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.launcher3.util.Executors;
+
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * Can be used to request the AssistContent from a provided task id, useful for getting the web uri
+ * if provided from the task.
+ */
+public class AssistContentRequester {
+    private static final String TAG = "AssistContentRequester";
+    private static final String ASSIST_KEY_CONTENT = "content";
+
+    /** For receiving content, called on the main thread. */
+    public interface Callback {
+        /**
+         * Called when the {@link android.app.assist.AssistContent} of the requested task is
+         * available.
+         **/
+        void onAssistContentAvailable(AssistContent assistContent);
+    }
+
+    private final IActivityTaskManager mActivityTaskManager;
+    private final String mPackageName;
+    private final Executor mCallbackExecutor;
+    private final Executor mSystemInteractionExecutor;
+
+    // If system loses the callback, our internal cache of original callback will also get cleared.
+    private final Map<Object, Callback> mPendingCallbacks =
+            Collections.synchronizedMap(new WeakHashMap<>());
+
+    public AssistContentRequester(Context context) {
+        mActivityTaskManager = ActivityTaskManager.getService();
+        mPackageName = context.getApplicationContext().getPackageName();
+        mCallbackExecutor = Executors.MAIN_EXECUTOR;
+        mSystemInteractionExecutor = Executors.UI_HELPER_EXECUTOR;
+    }
+
+    /**
+     * Request the {@link AssistContent} from the task with the provided id.
+     *
+     * @param taskId to query for the content.
+     * @param callback to call when the content is available, called on the main thread.
+     */
+    public void requestAssistContent(final int taskId, final Callback callback) {
+        // ActivityTaskManager interaction here is synchronous, so call off the main thread.
+        mSystemInteractionExecutor.execute(() -> {
+            try {
+                mActivityTaskManager.requestAssistDataForTask(
+                        new AssistDataReceiver(callback, this), taskId, mPackageName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Requesting assist content failed for task: " + taskId, e);
+            }
+        });
+    }
+
+    private void executeOnMainExecutor(Runnable callback) {
+        mCallbackExecutor.execute(callback);
+    }
+
+    private static final class AssistDataReceiver extends IAssistDataReceiver.Stub {
+
+        // The AssistDataReceiver binder callback object is passed to a system server, that may
+        // keep hold of it for longer than the lifetime of the AssistContentRequester object,
+        // potentially causing a memory leak. In the callback passed to the system server, only
+        // keep a weak reference to the parent object and lookup its callback if it still exists.
+        private final WeakReference<AssistContentRequester> mParentRef;
+        private final Object mCallbackKey = new Object();
+
+        AssistDataReceiver(Callback callback, AssistContentRequester parent) {
+            parent.mPendingCallbacks.put(mCallbackKey, callback);
+            mParentRef = new WeakReference<>(parent);
+        }
+
+        @Override
+        public void onHandleAssistData(Bundle data) {
+            if (data == null) {
+                return;
+            }
+
+            final AssistContent content = data.getParcelable(ASSIST_KEY_CONTENT);
+            if (content == null) {
+                Log.e(TAG, "Received AssistData, but no AssistContent found");
+                return;
+            }
+
+            AssistContentRequester requester = mParentRef.get();
+            if (requester != null) {
+                Callback callback = requester.mPendingCallbacks.get(mCallbackKey);
+                if (callback != null) {
+                    requester.executeOnMainExecutor(
+                            () -> callback.onAssistContentAvailable(content));
+                } else {
+                    Log.d(TAG, "Callback received after calling UI was disposed of");
+                }
+            } else {
+                Log.d(TAG, "Callback received after Requester was collected");
+            }
+        }
+
+        @Override
+        public void onHandleAssistScreenshot(Bitmap screenshot) {}
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
similarity index 92%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java
rename to quickstep/src/com/android/quickstep/util/AssistantUtilities.java
index 552db1f..336f7d1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java
+++ b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
@@ -23,7 +23,6 @@
 import android.os.Build;
 
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskInfoCompat;
 
 /**
  * Utility class for interacting with the Assistant.
@@ -39,7 +38,7 @@
     /** Returns true if the given task holds an Assistant activity that is excluded from recents. */
     public static boolean isExcludedAssistant(TaskInfo info) {
         return info != null
-            && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT
+            && info.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_ASSISTANT
             && (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/CancellableTask.java b/quickstep/src/com/android/quickstep/util/CancellableTask.java
new file mode 100644
index 0000000..a6e2e81
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/CancellableTask.java
@@ -0,0 +1,68 @@
+/*
+ * 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 com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+/**
+ * Utility class to executore a task on background and post the result on UI thread
+ */
+public abstract class CancellableTask<T> implements Runnable {
+
+    private boolean mCancelled = false;
+
+    @Override
+    public final void run() {
+        if (mCancelled) {
+            return;
+        }
+        T result = getResultOnBg();
+        if (mCancelled) {
+            return;
+        }
+        MAIN_EXECUTOR.execute(() -> {
+            if (mCancelled) {
+                return;
+            }
+            handleResult(result);
+        });
+    }
+
+    /**
+     * Called on the worker thread to process the request. The return object is passed to
+     * {@link #handleResult(Object)}
+     */
+    @WorkerThread
+    public abstract T getResultOnBg();
+
+    /**
+     * Called on the UI thread to handle the final result.
+     * @param result
+     */
+    @UiThread
+    public abstract void handleResult(T result);
+
+    /**
+     * Cancels the request. If it is called before {@link #handleResult(Object)}, that method
+     * will not be called
+     */
+    public void cancel() {
+        mCancelled = true;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index e998e9a..de7dbd6 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -19,25 +19,35 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
 
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.prediction.AppTarget;
+import android.content.ActivityNotFoundException;
 import android.content.ClipData;
 import android.content.ClipDescription;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Insets;
 import android.graphics.Picture;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.net.Uri;
 import android.util.Log;
+import android.view.View;
 
 import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
 import androidx.core.content.FileProvider;
 
+import com.android.internal.app.ChooserActivity;
 import com.android.launcher3.BuildConfig;
 import com.android.quickstep.SystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
@@ -59,6 +69,7 @@
     private static final long FILE_LIFE = 1000L /*ms*/ * 60L /*s*/ * 60L /*m*/ * 24L /*h*/;
     private static final String SUB_FOLDER = "Overview";
     private static final String BASE_NAME = "overview_image_";
+    private static final String TAG = "ImageActionUtils";
 
     /**
      * Saves screenshot to location determine by SystemUiProxy
@@ -71,6 +82,41 @@
     }
 
     /**
+     * Launch the activity to share image for overview sharing. This is to share cropped bitmap
+     * with specific share targets (with shortcutInfo and appTarget) rendered in overview.
+     */
+    @UiThread
+    public static void shareImage(Context context, Supplier<Bitmap> bitmapSupplier, RectF rectF,
+            ShortcutInfo shortcutInfo, AppTarget appTarget, String tag) {
+        if (bitmapSupplier.get() == null) {
+            return;
+        }
+        Rect crop = new Rect();
+        rectF.round(crop);
+        Intent intent = new Intent();
+        Uri uri =  getImageUri(bitmapSupplier.get(), crop, context, tag);
+        ClipData clipdata = new ClipData(new ClipDescription("content",
+                new String[]{"image/png"}),
+                new ClipData.Item(uri));
+        intent.setAction(Intent.ACTION_SEND)
+            .setComponent(new ComponentName(appTarget.getPackageName(), appTarget.getClassName()))
+            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
+            .setType("image/png")
+            .putExtra(Intent.EXTRA_STREAM, uri)
+            .putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId())
+            .setClipData(clipdata);
+
+        if (context.getUserId() != appTarget.getUser().getIdentifier()) {
+            intent.prepareToLeaveUser(context.getUserId());
+            intent.fixUris(context.getUserId());
+            context.startActivityAsUser(intent, appTarget.getUser());
+        } else {
+            context.startActivity(intent);
+        }
+    }
+
+    /**
      * Launch the activity to share image.
      */
     @UiThread
@@ -87,6 +133,22 @@
     }
 
     /**
+     * Launch the activity to share image with shared element transition.
+     */
+    @UiThread
+    public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier,
+            Rect crop, Intent intent, String tag, View sharedElement) {
+        if (bitmapSupplier.get() == null) {
+            Log.e(tag, "No snapshot available, not starting share.");
+            return;
+        }
+
+        UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context,
+                bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri,
+                tag, sharedElement));
+    }
+
+    /**
      * Starts activity based on given intent created from image uri.
      */
     @WorkerThread
@@ -94,15 +156,43 @@
             Intent intent, BiFunction<Uri, Intent, Intent[]> uriToIntentMap, String tag) {
         Intent[] intents = uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent);
 
-        // Work around b/159412574
-        if (intents.length == 1) {
-            context.startActivity(intents[0]);
-        } else {
-            context.startActivities(intents);
+        try {
+            // Work around b/159412574
+            if (intents.length == 1) {
+                context.startActivity(intents[0]);
+            } else {
+                context.startActivities(intents);
+            }
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "No activity found to receive image intent");
         }
     }
 
     /**
+     * Starts activity based on given intent created from image uri with shared element transition.
+     */
+    @WorkerThread
+    public static void persistBitmapAndStartActivity(Context context, Bitmap bitmap, Rect crop,
+            Intent intent, BiFunction<Uri, Intent, Intent[]> uriToIntentMap, String tag,
+            View scaledImage) {
+        Intent[] intents = uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent);
+
+        // Work around b/159412574
+        if (intents.length == 1) {
+            MAIN_EXECUTOR.execute(() -> context.startActivity(intents[0],
+                    ActivityOptions.makeSceneTransitionAnimation((Activity) context, scaledImage,
+                            ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle()));
+
+        } else {
+            MAIN_EXECUTOR.execute(() -> context.startActivities(intents,
+                    ActivityOptions.makeSceneTransitionAnimation((Activity) context, scaledImage,
+                            ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle()));
+        }
+    }
+
+
+
+    /**
      * Converts image bitmap to Uri by temporarily saving bitmap to cache, and creating Uri pointing
      * to that location. Used to be able to share an image with another app.
      *
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
similarity index 85%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java
rename to quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
index 3e87f48..2e5b33a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
@@ -38,7 +38,8 @@
     private static final String TAG = "InputConsumerProxy";
 
     private final InputConsumerController mInputConsumerController;
-    private final Supplier<InputConsumer> mConsumerSupplier;
+    private Runnable mCallback;
+    private Supplier<InputConsumer> mConsumerSupplier;
 
     // The consumer is created lazily on demand.
     private InputConsumer mInputConsumer;
@@ -48,8 +49,9 @@
     private boolean mDestroyPending = false;
 
     public InputConsumerProxy(InputConsumerController inputConsumerController,
-            Supplier<InputConsumer> consumerSupplier) {
+            Runnable callback, Supplier<InputConsumer> consumerSupplier) {
         mInputConsumerController = inputConsumerController;
+        mCallback = callback;
         mConsumerSupplier = consumerSupplier;
     }
 
@@ -64,9 +66,7 @@
         if (ev instanceof MotionEvent) {
             onInputConsumerMotionEvent((MotionEvent) ev);
         } else if (ev instanceof KeyEvent) {
-            if (mInputConsumer == null) {
-                mInputConsumer = mConsumerSupplier.get();
-            }
+            initInputConsumerIfNeeded();
             mInputConsumer.onKeyEvent((KeyEvent) ev);
             return true;
         }
@@ -89,9 +89,7 @@
 
         if (action == ACTION_DOWN) {
             mTouchInProgress = true;
-            if (mInputConsumer == null) {
-                mInputConsumer = mConsumerSupplier.get();
-            }
+            initInputConsumerIfNeeded();
         } else if (action == ACTION_CANCEL || action == ACTION_UP) {
             // Finish any pending actions
             mTouchInProgress = false;
@@ -115,4 +113,18 @@
         mDestroyed = true;
         mInputConsumerController.setInputListener(null);
     }
+
+    public void unregisterCallback() {
+        mCallback = null;
+    }
+
+    private void initInputConsumerIfNeeded() {
+        if (mInputConsumer == null) {
+            if (mCallback != null) {
+                mCallback.run();
+            }
+            mInputConsumer = mConsumerSupplier.get();
+            mConsumerSupplier = null;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java b/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.java
new file mode 100644
index 0000000..8209c09
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/InputProxyHandlerFactory.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.quickstep.util;
+
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.inputconsumers.OverviewInputConsumer;
+
+import java.util.function.Supplier;
+
+/**
+ * A factory that creates a input consumer for
+ *  {@link com.android.quickstep.util.InputConsumerProxy}.
+ */
+public class InputProxyHandlerFactory implements Supplier<InputConsumer> {
+
+    private final BaseActivityInterface mActivityInterface;
+    private final GestureState mGestureState;
+
+    @UiThread
+    public InputProxyHandlerFactory(BaseActivityInterface activityInterface,
+            GestureState gestureState) {
+        mActivityInterface = activityInterface;
+        mGestureState = gestureState;
+    }
+
+    /**
+     * Called to create a input proxy for the running task
+     */
+    @Override
+    public InputConsumer get() {
+        StatefulActivity activity = mActivityInterface.getCreatedActivity();
+        return activity == null ? InputConsumer.NO_OP
+                : new OverviewInputConsumer(mGestureState, activity, null, true);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index f7bd1e2..8834dc2 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -15,16 +15,12 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.SysUINavigationMode;
@@ -45,16 +41,10 @@
     public static int getShelfTrackingDistance(Context context, DeviceProfile dp,
             PagedOrientationHandler orientationHandler) {
         // Track the bottom of the window.
-        if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
-            Rect taskSize = new Rect();
-            LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
-                    orientationHandler);
-            return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
-        }
-        int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
-        int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
-                R.dimen.task_card_vert_space);
-        return shelfHeight + spaceBetweenShelfAndRecents;
+        Rect taskSize = new Rect();
+        LauncherActivityInterface.INSTANCE.calculateTaskSize(
+                context, dp, taskSize, orientationHandler);
+        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 f60f7ad..1ed2da3 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -15,17 +15,14 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_LSQ_VELOCITY_PROVIDER;
-
 import android.content.Context;
 import android.content.res.Resources;
-import android.util.Log;
 import android.view.MotionEvent;
+import android.view.VelocityTracker;
 
 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
@@ -52,7 +49,7 @@
     private final Alarm mForcePauseTimeout;
     private final boolean mMakePauseHarderToTrigger;
     private final Context mContext;
-    private final VelocityProvider mVelocityProvider;
+    private final SystemVelocityProvider mVelocityProvider;
 
     private Float mPreviousVelocity = null;
 
@@ -86,14 +83,10 @@
         mSpeedSlow = res.getDimension(R.dimen.motion_pause_detector_speed_slow);
         mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
         mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "creating alarm");
-        }
         mForcePauseTimeout = new Alarm();
         mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
         mMakePauseHarderToTrigger = makePauseHarderToTrigger;
-        mVelocityProvider = ENABLE_LSQ_VELOCITY_PROVIDER.get()
-                ? new LSqVelocityProvider(axis) : new LinearVelocityProvider(axis);
+        mVelocityProvider = new SystemVelocityProvider(axis);
     }
 
     /**
@@ -125,14 +118,11 @@
      * @param pointerIndex Index for the pointer being tracked in the motion event
      */
     public void addPosition(MotionEvent ev, int pointerIndex) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "setting alarm");
-        }
         mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
                 ? HARDER_TRIGGER_TIMEOUT
                 : FORCE_PAUSE_TIMEOUT);
-        Float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
-        if (newVelocity != null && mPreviousVelocity != null) {
+        float newVelocity = mVelocityProvider.addMotionEvent(ev, ev.getPointerId(pointerIndex));
+        if (mPreviousVelocity != null) {
             checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
         }
         mPreviousVelocity = newVelocity;
@@ -175,20 +165,24 @@
     }
 
     private void updatePaused(boolean isPaused) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "updatePaused: " + isPaused);
-        }
         if (mDisallowPause) {
             isPaused = false;
         }
         if (mIsPaused != isPaused) {
             mIsPaused = isPaused;
+            boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
             if (mIsPaused) {
                 AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
                 mHasEverBeenPaused = true;
             }
             if (mOnMotionPauseListener != null) {
-                mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+                if (isFirstDetectedPause) {
+                    mOnMotionPauseListener.onMotionPauseDetected();
+                }
+                // Null check again as onMotionPauseDetected() maybe have called clear().
+                if (mOnMotionPauseListener != null) {
+                    mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+                }
             }
         }
     }
@@ -199,9 +193,6 @@
         setOnMotionPauseListener(null);
         mIsPaused = mHasEverBeenPaused = false;
         mSlowStartTime = 0;
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "canceling alarm");
-        }
         mForcePauseTimeout.cancelAlarm();
     }
 
@@ -210,195 +201,39 @@
     }
 
     public interface OnMotionPauseListener {
-        void onMotionPauseChanged(boolean isPaused);
+        /** Called only the first time motion pause is detected. */
+        void onMotionPauseDetected();
+        /** Called every time motion changes from paused to not paused and vice versa. */
+        default void onMotionPauseChanged(boolean isPaused) { }
     }
 
-    /**
-     * Interface to abstract out velocity calculations
-     */
-    protected interface VelocityProvider {
+    private static class SystemVelocityProvider {
+
+        private final VelocityTracker mVelocityTracker;
+        private final int mAxis;
+
+        SystemVelocityProvider(int axis) {
+            mVelocityTracker = VelocityTracker.obtain();
+            mAxis = axis;
+        }
 
         /**
          * Adds a new motion events, and returns the velocity at this point, or null if
          * the velocity is not available
          */
-        Float addMotionEvent(MotionEvent ev, int pointer);
+        public float addMotionEvent(MotionEvent ev, int pointer) {
+            mVelocityTracker.addMovement(ev);
+            mVelocityTracker.computeCurrentVelocity(1); // px / ms
+            return mAxis == MotionEvent.AXIS_X
+                    ? mVelocityTracker.getXVelocity(pointer)
+                    : mVelocityTracker.getYVelocity(pointer);
+        }
 
         /**
          * Clears all stored motion event records
          */
-        void clear();
-    }
-
-    private static class LinearVelocityProvider implements VelocityProvider {
-
-        private Long mPreviousTime = null;
-        private Float mPreviousPosition = null;
-
-        private final int mAxis;
-
-        LinearVelocityProvider(int axis) {
-            mAxis = axis;
-        }
-
-        @Override
-        public Float addMotionEvent(MotionEvent ev, int pointer) {
-            long time = ev.getEventTime();
-            float position = ev.getAxisValue(mAxis, pointer);
-            Float velocity = null;
-
-            if (mPreviousTime != null && mPreviousPosition != null) {
-                long changeInTime = Math.max(1, time - mPreviousTime);
-                float changeInPosition = position - mPreviousPosition;
-                velocity = changeInPosition / changeInTime;
-            }
-            mPreviousTime = time;
-            mPreviousPosition = position;
-            return velocity;
-        }
-
-        @Override
         public void clear() {
-            mPreviousTime = null;
-            mPreviousPosition = null;
-        }
-    }
-
-    /**
-     * Java implementation of {@link android.view.VelocityTracker} using the Least Square (deg 2)
-     * algorithm.
-     */
-    private static class LSqVelocityProvider implements VelocityProvider {
-
-        // Maximum age of a motion event to be considered when calculating the velocity.
-        private static final long HORIZON_MS = 100;
-        // Number of samples to keep.
-        private static final int HISTORY_SIZE = 20;
-
-        // Position history are stored in a circular array
-        private final long[] mHistoricTimes = new long[HISTORY_SIZE];
-        private final float[] mHistoricPos = new float[HISTORY_SIZE];
-        private int mHistoryCount = 0;
-        private int mHistoryStart = 0;
-
-        private final int mAxis;
-
-        LSqVelocityProvider(int axis) {
-            mAxis = axis;
-        }
-
-        @Override
-        public void clear() {
-            mHistoryCount = mHistoryStart = 0;
-        }
-
-        private void addPositionAndTime(long eventTime, float eventPosition) {
-            mHistoricTimes[mHistoryStart] = eventTime;
-            mHistoricPos[mHistoryStart] = eventPosition;
-            mHistoryStart++;
-            if (mHistoryStart >= HISTORY_SIZE) {
-                mHistoryStart = 0;
-            }
-            mHistoryCount = Math.min(HISTORY_SIZE, mHistoryCount + 1);
-        }
-
-        @Override
-        public Float addMotionEvent(MotionEvent ev, int pointer) {
-            // Add all historic points
-            int historyCount = ev.getHistorySize();
-            for (int i = 0; i < historyCount; i++) {
-                addPositionAndTime(
-                        ev.getHistoricalEventTime(i), ev.getHistoricalAxisValue(mAxis, pointer, i));
-            }
-
-            // Start index for the last position (about to be added)
-            int eventStartIndex = mHistoryStart;
-            addPositionAndTime(ev.getEventTime(), ev.getAxisValue(mAxis, pointer));
-            return solveUnweightedLeastSquaresDeg2(eventStartIndex);
-        }
-
-        /**
-         * Solves the instantaneous velocity.
-         * Based on solveUnweightedLeastSquaresDeg2 in VelocityTracker.cpp
-         */
-        private Float solveUnweightedLeastSquaresDeg2(final int pointPos) {
-            final long eventTime = mHistoricTimes[pointPos];
-
-            float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
-            int count = 0;
-            for (int i = 0; i < mHistoryCount; i++) {
-                int index = pointPos - i;
-                if (index < 0) {
-                    index += HISTORY_SIZE;
-                }
-
-                long time = mHistoricTimes[index];
-                long age = eventTime - time;
-                if (age > HORIZON_MS) {
-                    break;
-                }
-                count++;
-                float xi = -age;
-
-                float yi = mHistoricPos[index];
-                float xi2 = xi * xi;
-                float xi3 = xi2 * xi;
-                float xi4 = xi3 * xi;
-                float xiyi = xi * yi;
-                float xi2yi = xi2 * yi;
-
-                sxi += xi;
-                sxi2 += xi2;
-                sxiyi += xiyi;
-                sxi2yi += xi2yi;
-                syi += yi;
-                sxi3 += xi3;
-                sxi4 += xi4;
-            }
-
-            if (count < 3) {
-                // Too few samples
-                switch (count) {
-                    case 2: {
-                        int endPos = pointPos - 1;
-                        if (endPos < 0) {
-                            endPos += HISTORY_SIZE;
-                        }
-                        long denominator = eventTime - mHistoricTimes[endPos];
-                        if (denominator != 0) {
-                            return (mHistoricPos[pointPos] - mHistoricPos[endPos]) / denominator;
-                        }
-                    }
-                    // fall through
-                    case 1:
-                        return 0f;
-                    default:
-                        return null;
-                }
-            }
-
-            float Sxx = sxi2 - sxi * sxi / count;
-            float Sxy = sxiyi - sxi * syi / count;
-            float Sxx2 = sxi3 - sxi * sxi2 / count;
-            float Sx2y = sxi2yi - sxi2 * syi / count;
-            float Sx2x2 = sxi4 - sxi2 * sxi2 / count;
-
-            float denominator = Sxx * Sx2x2 - Sxx2 * Sxx2;
-            if (denominator == 0) {
-                // division by 0 when computing velocity
-                return null;
-            }
-            // Compute a
-            // float numerator = Sx2y*Sxx - Sxy*Sxx2;
-
-            // Compute b
-            float numerator = Sxy * Sx2x2 - Sx2y * Sxx2;
-            float b = numerator / denominator;
-
-            // Compute c
-            // float c = syi/count - b * sxi/count - a * sxi2/count;
-
-            return b;
+            mVelocityTracker.clear();
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java b/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java
index e798d5c..1c3c9c2 100644
--- a/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java
+++ b/quickstep/src/com/android/quickstep/util/MultiValueUpdateListener.java
@@ -40,10 +40,14 @@
             newPercent = prop.mInterpolator.getInterpolation(newPercent);
             prop.value = prop.mEnd * newPercent + prop.mStart * (1 - newPercent);
         }
-        onUpdate(percent);
+        onUpdate(percent, false /* initOnly */);
     }
 
-    public abstract void onUpdate(float percent);
+    /**
+     * @param percent The total animation progress.
+     * @param initOnly When true, only does enough work to initialize the animation.
+     */
+    public abstract void onUpdate(float percent, boolean initOnly);
 
     public final class FloatProp {
 
@@ -64,5 +68,12 @@
 
             mAllProperties.add(this);
         }
+
+        /**
+         * Gets the start value.
+         */
+        public float getStartValue() {
+            return mStart;
+        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
index 0a98e1b..449dba8 100644
--- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
@@ -19,7 +19,7 @@
 
 import android.view.Surface;
 
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.quickstep.SysUINavigationMode;
 
 /**
@@ -30,7 +30,7 @@
     private final SysUINavigationMode.Mode mMode;
     private final int mDisplayRotation;
 
-    public NavBarPosition(SysUINavigationMode.Mode mode, DefaultDisplay.Info info) {
+    public NavBarPosition(SysUINavigationMode.Mode mode, Info info) {
         mMode = mode;
         mDisplayRotation = info.rotation;
     }
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
new file mode 100644
index 0000000..5cf4f0b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
@@ -0,0 +1,105 @@
+/*
+ * 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 com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.util.Log;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.StateAnimationConfig;
+
+/**
+ * Runs an animation from overview to home. Currently, this animation is just a wrapper around the
+ * normal state transition and may play a {@link WorkspaceRevealAnim} if we're starting from an
+ * upward fling.
+ */
+public class OverviewToHomeAnim {
+
+    private static final String TAG = "OverviewToHomeAnim";
+
+    private final Launcher mLauncher;
+    private final Runnable mOnReachedHome;
+
+    // Only run mOnReachedHome when both of these are true.
+    private boolean mIsHomeStaggeredAnimFinished;
+    private boolean mIsOverviewHidden;
+
+    public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome) {
+        mLauncher = launcher;
+        mOnReachedHome = onReachedHome;
+    }
+
+    /**
+     * Starts the animation. If velocity < 0 (i.e. upwards), also plays a
+     * {@link WorkspaceRevealAnim}.
+     */
+    public void animateWithVelocity(float velocity) {
+        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
+        LauncherState startState = stateManager.getState();
+        if (startState != OVERVIEW) {
+            Log.e(TAG, "animateFromOverviewToHome: unexpected start state " + startState);
+        }
+        AnimatorSet anim = new AnimatorSet();
+
+        boolean playWorkspaceRevealAnim = velocity < 0;
+        if (playWorkspaceRevealAnim) {
+            WorkspaceRevealAnim workspaceRevealAnim = new WorkspaceRevealAnim(mLauncher,
+                    false /* animateOverviewScrim */);
+            workspaceRevealAnim.addAnimatorListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    mIsHomeStaggeredAnimFinished = true;
+                    maybeOverviewToHomeAnimComplete();
+                }
+            });
+            anim.play(workspaceRevealAnim.getAnimators());
+        } else {
+            mIsHomeStaggeredAnimFinished = true;
+        }
+
+        StateAnimationConfig config = new StateAnimationConfig();
+        if (playWorkspaceRevealAnim) {
+            // WorkspaceRevealAnim handles the depth, so don't interfere.
+            config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
+        }
+        config.duration = startState.getTransitionDuration(mLauncher);
+        AnimatorSet stateAnim = stateManager.createAtomicAnimation(
+                startState, NORMAL, config);
+        stateAnim.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                mIsOverviewHidden = true;
+                maybeOverviewToHomeAnimComplete();
+            }
+        });
+        anim.play(stateAnim);
+        stateManager.setCurrentAnimation(anim, NORMAL);
+        anim.start();
+    }
+
+    private void maybeOverviewToHomeAnimComplete() {
+        if (mIsHomeStaggeredAnimFinished && mIsOverviewHidden) {
+            mOnReachedHome.run();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/ProtoTracer.java b/quickstep/src/com/android/quickstep/util/ProtoTracer.java
new file mode 100644
index 0000000..ef9586d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ProtoTracer.java
@@ -0,0 +1,135 @@
+/*
+ * 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.util;
+
+import static com.android.launcher3.tracing.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_H_VALUE;
+import static com.android.launcher3.tracing.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_L_VALUE;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import android.os.Trace;
+import com.android.launcher3.tracing.LauncherTraceProto;
+import com.android.launcher3.tracing.LauncherTraceEntryProto;
+import com.android.launcher3.tracing.LauncherTraceFileProto;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.systemui.shared.tracing.FrameProtoTracer;
+import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams;
+import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.google.protobuf.MessageLite;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Queue;
+
+
+/**
+ * Controller for coordinating winscope proto tracing.
+ */
+public class ProtoTracer implements ProtoTraceParams<MessageLite.Builder,
+        LauncherTraceFileProto.Builder, LauncherTraceEntryProto.Builder,
+                LauncherTraceProto.Builder> {
+
+    public static final MainThreadInitializedObject<ProtoTracer> INSTANCE =
+            new MainThreadInitializedObject<>(ProtoTracer::new);
+
+    private static final String TAG = "ProtoTracer";
+    private static final long MAGIC_NUMBER_VALUE =
+            ((long) MAGIC_NUMBER_H_VALUE << 32) | MAGIC_NUMBER_L_VALUE;
+
+    private final Context mContext;
+    private final FrameProtoTracer<MessageLite.Builder, LauncherTraceFileProto.Builder,
+        LauncherTraceEntryProto.Builder, LauncherTraceProto.Builder> mProtoTracer;
+
+    public ProtoTracer(Context context) {
+        mContext = context;
+        mProtoTracer = new FrameProtoTracer<>(this);
+    }
+
+    @Override
+    public File getTraceFile() {
+        return new File(mContext.getFilesDir(), "launcher_trace.pb");
+    }
+
+    @Override
+    public LauncherTraceFileProto.Builder getEncapsulatingTraceProto() {
+        return LauncherTraceFileProto.newBuilder();
+    }
+
+    @Override
+    public LauncherTraceEntryProto.Builder updateBufferProto(
+            LauncherTraceEntryProto.Builder reuseObj,
+            ArrayList<ProtoTraceable<LauncherTraceProto.Builder>> traceables) {
+        Trace.beginSection("ProtoTracer.updateBufferProto");
+        LauncherTraceEntryProto.Builder proto = LauncherTraceEntryProto.newBuilder();
+        proto.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+        LauncherTraceProto.Builder launcherProto = LauncherTraceProto.newBuilder();
+        for (ProtoTraceable t : traceables) {
+            t.writeToProto(launcherProto);
+        }
+        proto.setLauncher(launcherProto);
+        Trace.endSection();
+        return proto;
+    }
+
+    @Override
+    public byte[] serializeEncapsulatingProto(LauncherTraceFileProto.Builder encapsulatingProto,
+            Queue<LauncherTraceEntryProto.Builder> buffer) {
+        Trace.beginSection("ProtoTracer.serializeEncapsulatingProto");
+        encapsulatingProto.setMagicNumber(MAGIC_NUMBER_VALUE);
+        for (LauncherTraceEntryProto.Builder entry : buffer) {
+            encapsulatingProto.addEntry(entry);
+        }
+        byte[] bytes = encapsulatingProto.build().toByteArray();
+        Trace.endSection();
+        return bytes;
+    }
+
+    @Override
+    public byte[] getProtoBytes(MessageLite.Builder proto) {
+        return proto.build().toByteArray();
+    }
+
+    @Override
+    public int getProtoSize(MessageLite.Builder proto) {
+        return proto.build().getSerializedSize();
+    }
+
+    public void start() {
+        mProtoTracer.start();
+    }
+
+    public void stop() {
+        mProtoTracer.stop();
+    }
+
+    public void add(ProtoTraceable<LauncherTraceProto.Builder> traceable) {
+        mProtoTracer.add(traceable);
+    }
+
+    public void remove(ProtoTraceable<LauncherTraceProto.Builder> traceable) {
+        mProtoTracer.remove(traceable);
+    }
+
+    public void scheduleFrameUpdate() {
+        mProtoTracer.scheduleFrameUpdate();
+    }
+
+    public void update() {
+        mProtoTracer.update();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 9ed2bbe..6d6e802 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -21,19 +21,18 @@
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.content.SharedPreferences;
 
-import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.hybridhotseat.HotseatPredictionController;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.views.AllAppsEduView;
@@ -41,9 +40,9 @@
 /**
  * Extends {@link OnboardingPrefs} for quickstep-specific onboarding data.
  */
-public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLauncher> {
+public class QuickstepOnboardingPrefs extends OnboardingPrefs<QuickstepLauncher> {
 
-    public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs) {
+    public QuickstepOnboardingPrefs(QuickstepLauncher launcher, SharedPreferences sharedPrefs) {
         super(launcher, sharedPrefs);
 
         StateManager<LauncherState> stateManager = launcher.getStateManager();
@@ -65,44 +64,8 @@
             });
         }
 
-        boolean shelfBounceSeen = getBoolean(SHELF_BOUNCE_SEEN);
-        if (!shelfBounceSeen && ENABLE_OVERVIEW_ACTIONS.get()
-                && removeShelfFromOverview(launcher)) {
-            // There's no shelf in overview, so don't bounce it (can't get to all apps anyway).
-            shelfBounceSeen = true;
-            mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, shelfBounceSeen).apply();
-        }
-        if (!shelfBounceSeen) {
-            stateManager.addStateListener(new StateListener<LauncherState>() {
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    LauncherState prevState = stateManager.getLastState();
-
-                    if ((finalState == ALL_APPS && prevState == OVERVIEW) ||
-                            hasReachedMaxCount(SHELF_BOUNCE_COUNT)) {
-                        mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
-                        stateManager.removeStateListener(this);
-                    }
-                }
-            });
-        }
-
-        if (!hasReachedMaxCount(ALL_APPS_COUNT)) {
-            stateManager.addStateListener(new StateListener<LauncherState>() {
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    if (finalState == ALL_APPS) {
-                        if (incrementEventCount(ALL_APPS_COUNT)) {
-                            stateManager.removeStateListener(this);
-                            mLauncher.getScrimView().updateDragHandleVisibility();
-                        }
-                    }
-                }
-            });
-        }
-
-        if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() && !hasReachedMaxCount(
-                HOTSEAT_DISCOVERY_TIP_COUNT)) {
+        if (!Utilities.IS_RUNNING_IN_TEST_HARNESS
+                && !hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
             stateManager.addStateListener(new StateListener<LauncherState>() {
                 boolean mFromAllApps = false;
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
similarity index 80%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
rename to quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index 5b0d503..5c72c8f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -15,11 +15,13 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 
+import androidx.dynamicanimation.animation.DynamicAnimation;
+
 import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.statemanager.StatefulActivity;
@@ -32,16 +34,11 @@
     public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = AtomicAnimationFactory.NEXT_INDEX + 1;
 
     private static final int MY_ANIM_COUNT = 2;
-    protected static final int NEXT_INDEX = AtomicAnimationFactory.NEXT_INDEX + MY_ANIM_COUNT;
 
     protected final ACTIVITY_TYPE mActivity;
 
-    /**
-     * @param extraAnims number of animations supported by the subclass. This should not include
-     *                  the 2 animations supported by this class.
-     */
-    public RecentsAtomicAnimationFactory(ACTIVITY_TYPE activity, int extraAnims) {
-        super(MY_ANIM_COUNT + extraAnims);
+    public RecentsAtomicAnimationFactory(ACTIVITY_TYPE activity) {
+        super(MY_ANIM_COUNT);
         mActivity = activity;
     }
 
@@ -54,11 +51,11 @@
             case INDEX_RECENTS_TRANSLATE_X_ANIM: {
                 RecentsView rv = mActivity.getOverviewPanel();
                 return new SpringAnimationBuilder(mActivity)
-                        .setMinimumVisibleChange(1f / rv.getPageOffsetScale())
+                        .setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE)
                         .setDampingRatio(0.8f)
                         .setStiffness(250)
                         .setValues(values)
-                        .build(rv, ADJACENT_PAGE_OFFSET);
+                        .build(rv, ADJACENT_PAGE_HORIZONTAL_OFFSET);
             }
             default:
                 return super.createStateElementAnimation(index, values);
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 81d24d7..7cfd151 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -16,30 +16,25 @@
 
 package com.android.quickstep.util;
 
-import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 import static android.view.OrientationEventListener.ORIENTATION_UNKNOWN;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
-import static com.android.launcher3.logging.LoggerUtils.extractObjectNameAndAddress;
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
+import static com.android.launcher3.util.SettingsCache.ROTATION_SETTING_URI;
+import static com.android.quickstep.BaseActivityInterface.getTaskDimension;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.database.ContentObserver;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.os.Handler;
-import android.provider.Settings;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
@@ -53,9 +48,11 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.util.WindowBounds;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.SettingsCache;
 import com.android.quickstep.BaseActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.TaskView;
 
 import java.lang.annotation.Retention;
 import java.util.function.IntConsumer;
@@ -68,17 +65,12 @@
  * This class has initial default state assuming the device and foreground app have
  * no ({@link Surface#ROTATION_0} rotation.
  */
-public final class RecentsOrientedState implements SharedPreferences.OnSharedPreferenceChangeListener {
+public class RecentsOrientedState implements
+        SharedPreferences.OnSharedPreferenceChangeListener {
 
     private static final String TAG = "RecentsOrientedState";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
-    private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
-        @Override
-        public void onChange(boolean selfChange) {
-            updateAutoRotateSetting();
-        }
-    };
     @Retention(SOURCE)
     @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
     public @interface SurfaceRotation {}
@@ -122,14 +114,20 @@
                     | FLAG_SWIPE_UP_NOT_RUNNING;
 
     private final Context mContext;
-    private final ContentResolver mContentResolver;
     private final SharedPreferences mSharedPrefs;
     private final OrientationEventListener mOrientationListener;
+    private final SettingsCache mSettingsCache;
+    private final SettingsCache.OnChangeListener mRotationChangeListener =
+            isEnabled -> updateAutoRotateSetting();
 
     private final Matrix mTmpMatrix = new Matrix();
 
     private int mFlags;
     private int mPreviousRotation = ROTATION_0;
+    private boolean mListenersInitialized = false;
+
+    // Combined int which encodes the full state.
+    private int mStateId = 0;
 
     /**
      * @param rotationChangeListener Callback for receiving rotation events when rotation watcher
@@ -139,7 +137,6 @@
     public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy,
             IntConsumer rotationChangeListener) {
         mContext = context;
-        mContentResolver = context.getContentResolver();
         mSharedPrefs = Utilities.getPrefs(context);
         mOrientationListener = new OrientationEventListener(context) {
             @Override
@@ -155,23 +152,38 @@
         mFlags = sizeStrategy.rotationSupportedByActivity
                 ? FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY : 0;
 
-        Resources res = context.getResources();
-        int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
-                * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
-        if (originalSmallestWidth < 600) {
-            mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
-        }
         mFlags |= FLAG_SWIPE_UP_NOT_RUNNING;
+        mSettingsCache = SettingsCache.INSTANCE.get(mContext);
         initFlags();
     }
 
     /**
+     * Sets the device profile for the current state.
+     */
+    public void setDeviceProfile(DeviceProfile deviceProfile) {
+        boolean oldMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
+        setFlag(FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY, !deviceProfile.allowRotation);
+        if (mListenersInitialized) {
+            boolean newMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
+            // If isMultipleOrientationSupportedByDevice is changed, init or destroy listeners
+            // accordingly.
+            if (newMultipleOrientationsSupported != oldMultipleOrientationsSupported) {
+                if (newMultipleOrientationsSupported) {
+                    initMultipleOrientationListeners();
+                } else {
+                    destroyMultipleOrientationListeners();
+                }
+            }
+        }
+    }
+
+    /**
      * Sets the rotation for the recents activity, which could affect the appearance of task view.
      * @see #update(int, int)
      */
     public boolean setRecentsRotation(@SurfaceRotation int recentsRotation) {
         mRecentsRotation = recentsRotation;
-        return update(mTouchRotation, mDisplayRotation);
+        return updateHandler();
     }
 
     /**
@@ -185,8 +197,7 @@
      * Sets if the swipe up gesture is currently running or not
      */
     public boolean setGestureActive(boolean isGestureActive) {
-        setFlag(FLAG_SWIPE_UP_NOT_RUNNING, !isGestureActive);
-        return update(mTouchRotation, mDisplayRotation);
+        return setFlag(FLAG_SWIPE_UP_NOT_RUNNING, !isGestureActive);
     }
 
     /**
@@ -199,18 +210,17 @@
      */
     public boolean update(
             @SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
-        mRecentsActivityRotation = inferRecentsActivityRotation(displayRotation);
         mDisplayRotation = displayRotation;
         mTouchRotation = touchRotation;
         mPreviousRotation = touchRotation;
+        return updateHandler();
+    }
 
-        PagedOrientationHandler oldHandler = mOrientationHandler;
+    private boolean updateHandler() {
+        mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
         if (mRecentsActivityRotation == mTouchRotation
                 || (canRecentsActivityRotate() && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) {
             mOrientationHandler = PagedOrientationHandler.PORTRAIT;
-            if (DEBUG) {
-                Log.d(TAG, "current RecentsOrientedState: " + this);
-            }
         } else if (mTouchRotation == ROTATION_90) {
             mOrientationHandler = PagedOrientationHandler.LANDSCAPE;
         } else if (mTouchRotation == ROTATION_270) {
@@ -221,19 +231,26 @@
         if (DEBUG) {
             Log.d(TAG, "current RecentsOrientedState: " + this);
         }
-        return oldHandler != mOrientationHandler;
+
+        int oldStateId = mStateId;
+        // Each SurfaceRotation value takes two bits
+        mStateId = (((((mFlags << 2)
+                | mDisplayRotation) << 2)
+                | mTouchRotation) << 3)
+                | (mRecentsRotation < 0 ? 7 : mRecentsRotation);
+        return mStateId != oldStateId;
     }
 
     @SurfaceRotation
     private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
         if (isRecentsActivityRotationAllowed()) {
-            return mRecentsRotation < ROTATION_0 ? displayRotation : mRecentsRotation;
+            return mRecentsRotation < 0 ? displayRotation : mRecentsRotation;
         } else {
             return ROTATION_0;
         }
     }
 
-    private void setFlag(int mask, boolean enabled) {
+    private boolean setFlag(int mask, boolean enabled) {
         boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
                 && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
                 && !canRecentsActivityRotate();
@@ -255,6 +272,7 @@
                 }
             });
         }
+        return updateHandler();
     }
 
     @Override
@@ -265,36 +283,42 @@
     }
 
     private void updateAutoRotateSetting() {
-        setFlag(FLAG_SYSTEM_ROTATION_ALLOWED, Settings.System.getInt(mContentResolver,
-                Settings.System.ACCELEROMETER_ROTATION, 1) == 1);
+        setFlag(FLAG_SYSTEM_ROTATION_ALLOWED,
+                mSettingsCache.getValue(ROTATION_SETTING_URI, 1));
     }
 
     private void updateHomeRotationSetting() {
-        setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS,
-                mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false));
+        boolean homeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false);
+        setFlag(FLAG_HOME_ROTATION_ALLOWED_IN_PREFS, homeRotationEnabled);
+        SystemUiProxy.INSTANCE.get(mContext).setHomeRotationEnabled(homeRotationEnabled);
     }
 
     private void initFlags() {
-        SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
-        boolean rotationWatcherSupported = mOrientationListener.canDetectOrientation() &&
-                currentMode != TWO_BUTTONS;
-        setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, rotationWatcherSupported);
+        setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, mOrientationListener.canDetectOrientation());
 
         // initialize external flags
         updateAutoRotateSetting();
         updateHomeRotationSetting();
     }
 
+    private void initMultipleOrientationListeners() {
+        mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
+        mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener);
+    }
+
+    private void destroyMultipleOrientationListeners() {
+        mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
+        mSettingsCache.unregister(ROTATION_SETTING_URI, mRotationChangeListener);
+    }
+
     /**
      * Initializes any system values and registers corresponding change listeners. It must be
      * paired with {@link #destroyListeners()} call
      */
     public void initListeners() {
+        mListenersInitialized = true;
         if (isMultipleOrientationSupportedByDevice()) {
-            mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
-            mContentResolver.registerContentObserver(
-                    Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
-                    false, mSystemAutoRotateObserver);
+            initMultipleOrientationListeners();
         }
         initFlags();
     }
@@ -303,9 +327,9 @@
      * Unregisters any previously registered listeners.
      */
     public void destroyListeners() {
+        mListenersInitialized = false;
         if (isMultipleOrientationSupportedByDevice()) {
-            mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
-            mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
+            destroyMultipleOrientationListeners();
         }
         setRotationWatcherEnabled(false);
     }
@@ -329,6 +353,13 @@
         return mRecentsActivityRotation;
     }
 
+    /**
+     * Returns an id that can be used to tracking internal changes
+     */
+    public int getStateId() {
+        return mStateId;
+    }
+
     public boolean isMultipleOrientationSupportedByDevice() {
         return (mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
                 == MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE;
@@ -363,15 +394,14 @@
      */
     public float getFullScreenScaleAndPivot(Rect taskView, DeviceProfile dp, PointF outPivot) {
         Rect insets = dp.getInsets();
-        float fullWidth = dp.widthPx - insets.left - insets.right;
-        float fullHeight = dp.heightPx - insets.top - insets.bottom;
-
-        if (dp.isMultiWindowMode) {
-            WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(mContext);
-            outPivot.set(bounds.availableSize.x, bounds.availableSize.y);
-        } else {
-            outPivot.set(fullWidth, fullHeight);
+        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;
         }
+
+        getTaskDimension(mContext, dp, outPivot);
         float scale = Math.min(outPivot.x / taskView.width(), outPivot.y / taskView.height());
         // We also scale the preview as part of fullScreenParams, so account for that as well.
         if (fullWidth > 0) {
@@ -506,19 +536,43 @@
         }
     }
 
+    /**
+     * Contrary to {@link #postDisplayRotation}.
+     */
+    public static void preDisplayRotation(@SurfaceRotation int displayRotation,
+            float screenWidth, float screenHeight, Matrix out) {
+        switch (displayRotation) {
+            case ROTATION_0:
+                return;
+            case ROTATION_90:
+                out.postRotate(90);
+                out.postTranslate(screenWidth, 0);
+                break;
+            case ROTATION_180:
+                out.postRotate(180);
+                out.postTranslate(screenHeight, screenWidth);
+                break;
+            case ROTATION_270:
+                out.postRotate(270);
+                out.postTranslate(0, screenHeight);
+                break;
+        }
+    }
+
     @NonNull
     @Override
     public String toString() {
         boolean systemRotationOn = (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0;
         return "["
-                + "this=" + extractObjectNameAndAddress(super.toString())
-                + " mOrientationHandler=" +
-                    extractObjectNameAndAddress(mOrientationHandler.toString())
+                + "this=" + nameAndAddress(this)
+                + " mOrientationHandler=" + nameAndAddress(mOrientationHandler)
                 + " mDisplayRotation=" + mDisplayRotation
                 + " mTouchRotation=" + mTouchRotation
                 + " mRecentsActivityRotation=" + mRecentsActivityRotation
+                + " mRecentsRotation=" + mRecentsRotation
                 + " isRecentsActivityRotationAllowed=" + isRecentsActivityRotationAllowed()
                 + " mSystemRotation=" + systemRotationOn
+                + " mStateId=" + mStateId
                 + " mFlags=" + mFlags
                 + "]";
     }
@@ -528,10 +582,30 @@
      */
     public DeviceProfile getLauncherDeviceProfile() {
         InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
-        // TODO also check the natural orientation is landscape or portrait
-        return  (mRecentsActivityRotation == ROTATION_90
-                || mRecentsActivityRotation == ROTATION_270)
-                ? idp.landscapeProfile
-                : idp.portraitProfile;
+        Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
+
+        int width, height;
+        if ((mRecentsActivityRotation == ROTATION_90 || mRecentsActivityRotation == ROTATION_270)) {
+            width = Math.max(currentSize.x, currentSize.y);
+            height = Math.min(currentSize.x, currentSize.y);
+        } else {
+            width = Math.min(currentSize.x, currentSize.y);
+            height = Math.max(currentSize.x, currentSize.y);
+        }
+
+        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;
+    }
+
+    private static String nameAndAddress(Object obj) {
+        return obj.getClass().getSimpleName() + "@" + obj.hashCode();
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
similarity index 97%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
rename to quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
index e5d2c53..02ec68a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -20,6 +20,7 @@
 import android.graphics.PointF;
 import android.graphics.RectF;
 
+import androidx.annotation.Nullable;
 import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
 import androidx.dynamicanimation.animation.FloatPropertyCompat;
 import androidx.dynamicanimation.animation.SpringAnimation;
@@ -241,7 +242,7 @@
                         mCurrentCenterX + currentWidth / 2, mCurrentY + currentHeight);
             }
             for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
-                onUpdateListener.onUpdate(mCurrentRect, mCurrentScaleProgress);
+                onUpdateListener.onUpdate(null, mCurrentRect, mCurrentScaleProgress);
             }
         }
     }
@@ -266,7 +267,7 @@
     }
 
     public interface OnUpdateListener {
-        void onUpdate(RectF currentRect, float progress);
+        void onUpdate(@Nullable AppCloseConfig values, 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
new file mode 100644
index 0000000..c331a13
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim2.java
@@ -0,0 +1,370 @@
+/*
+ * 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 04308c8..98dbd47 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -21,31 +21,23 @@
 import android.os.Handler;
 
 import com.android.launcher3.LauncherAnimationRunner;
-import com.android.launcher3.WrappedLauncherAnimationRunner;
+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 {
 
-    LauncherAnimationRunner mAnimationRunner;
+    RemoteAnimationFactory mAnimationRunner;
 
     public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets);
 
     ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
-        mAnimationRunner = new LauncherAnimationRunner(handler,
-                false /* startAtFrontOfQueue */) {
-
-            @Override
-            public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+        mAnimationRunner = (transit, appTargets, wallpaperTargets, nonApps, result) ->
                 result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
-            }
-        };
-        final LauncherAnimationRunner wrapper = new WrappedLauncherAnimationRunner(
-                mAnimationRunner, false /* startAtFrontOfQueue */);
-
+        final LauncherAnimationRunner wrapper = new LauncherAnimationRunner(
+                handler, mAnimationRunner, false /* startAtFrontOfQueue */);
         return ActivityOptionsCompat.makeRemoteAnimation(
                 new RemoteAnimationAdapterCompat(wrapper, duration, 0));
     }
diff --git a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
index 958ee24..81c124f 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteFadeOutAnimationListener.java
@@ -34,7 +34,8 @@
 
     public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets) {
-        mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_CLOSING);
+        mTarget = new RemoteAnimationTargets(appTargets, wallpaperTargets,
+                new RemoteAnimationTargetCompat[0], MODE_CLOSING);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
index a770e8e..483a1c6 100644
--- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
+++ b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
@@ -20,10 +20,7 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.Insets;
-import android.graphics.Rect;
 import android.os.Build;
-import android.view.WindowInsets.Type;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
 
@@ -32,7 +29,7 @@
 import androidx.annotation.UiThread;
 
 import com.android.launcher3.R;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.WindowBounds;
 
 import java.util.ArrayList;
@@ -73,11 +70,9 @@
      */
     private static WindowBounds createDefaultWindowBounds(Context context) {
         WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
-        Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
+        WindowBounds bounds = WindowBounds.fromWindowMetrics(wm);
 
-        WindowBounds bounds = new WindowBounds(wm.getBounds(),
-                new Rect(insets.left, insets.top, insets.right, insets.bottom));
-        int rotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
+        int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
         int halfDividerSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
 
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
new file mode 100644
index 0000000..a147b68
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -0,0 +1,236 @@
+/*
+ * 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.util;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+
+import android.animation.AnimatorSet;
+import android.app.ActivityOptions;
+import android.content.res.Resources;
+import android.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.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.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.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;
+
+/**
+ * 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;
+
+    public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
+        mSystemUiProxy = systemUiProxy;
+        mHandler = handler;
+    }
+
+    /**
+     * To be called after first task selected
+     */
+    public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
+            Rect initialBounds) {
+        mInitialTaskView = taskView;
+        mInitialPosition = positionOption;
+        mInitialBounds = initialBounds;
+    }
+
+    /**
+     * To be called after second task selected
+     */
+    public void setSecondTaskId(TaskView taskView) {
+        if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
+            // Assume initial task is for top/left part of screen
+            final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT
+                    ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id}
+                    : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id};
+
+            RemoteSplitLaunchAnimationRunner animationRunner =
+                    new RemoteSplitLaunchAnimationRunner(mInitialTaskView, taskView);
+            mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
+                    null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
+                    new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR));
+            return;
+        }
+        // Assume initial mInitialTaskId is for top/left part of screen
+        RemoteAnimationFactory initialSplitRunnerWrapped =  new SplitLaunchAnimationRunner(
+                mInitialTaskView, 0);
+        RemoteAnimationFactory secondarySplitRunnerWrapped =  new SplitLaunchAnimationRunner(
+                taskView, 1);
+        RemoteAnimationRunnerCompat initialSplitRunner = new LauncherAnimationRunner(
+                new Handler(Looper.getMainLooper()), initialSplitRunnerWrapped,
+                true /* startAtFrontOfQueue */);
+        RemoteAnimationRunnerCompat secondarySplitRunner = new LauncherAnimationRunner(
+                new Handler(Looper.getMainLooper()), secondarySplitRunnerWrapped,
+                true /* startAtFrontOfQueue */);
+        ActivityOptions initialOptions = ActivityOptionsCompat.makeRemoteAnimation(
+                new RemoteAnimationAdapterCompat(initialSplitRunner, 300, 150));
+        ActivityOptions secondaryOptions = ActivityOptionsCompat.makeRemoteAnimation(
+                new RemoteAnimationAdapterCompat(secondarySplitRunner, 300, 150));
+        mSystemUiProxy.startTask(mInitialTaskView.getTask().key.id, mInitialPosition.mStageType,
+                mInitialPosition.mStagePosition,
+                /*null*/ initialOptions.toBundle());
+        Pair<Integer, Integer> compliment = getComplimentaryStageAndPosition(mInitialPosition);
+        mSystemUiProxy.startTask(taskView.getTask().key.id, compliment.first,
+                compliment.second,
+                /*null*/ secondaryOptions.toBundle());
+        // After successful launch, call resetState
+        resetState();
+    }
+
+    /**
+     * @return {@link InsettableFrameLayout.LayoutParams} to correctly position the
+     * split placeholder view
+     */
+    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;
+    }
+
+    @Nullable
+    public SplitPositionOption getActiveSplitPositionOption() {
+        return mInitialPosition;
+    }
+
+    /**
+     * Requires Shell Transitions
+     */
+    private class RemoteSplitLaunchAnimationRunner implements RemoteTransitionRunner {
+
+        private final TaskView mInitialTaskView;
+        private final TaskView mTaskView;
+
+        RemoteSplitLaunchAnimationRunner(TaskView initialTaskView, TaskView taskView) {
+            mInitialTaskView = initialTaskView;
+            mTaskView = taskView;
+        }
+
+        @Override
+        public void startAnimation(IBinder transition, TransitionInfo info,
+                SurfaceControl.Transaction t, Runnable finishCallback) {
+            TaskViewUtils.composeRecentsSplitLaunchAnimator(mInitialTaskView, mTaskView,
+                    info, t, finishCallback);
+            // After successful launch, call resetState
+            resetState();
+        }
+    }
+
+    /**
+     * 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 final TaskView mV;
+        private final int mTargetState;
+
+        SplitLaunchAnimationRunner(TaskView v, int targetState) {
+            mV = v;
+            mTargetState = targetState;
+        }
+
+        @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);
+        }
+    }
+
+
+    /**
+     * To be called if split select was cancelled
+     */
+    public void resetState() {
+        mInitialTaskView = null;
+        mInitialPosition = null;
+        mInitialBounds = null;
+    }
+
+    public boolean isSplitSelectActive() {
+        return mInitialTaskView != null;
+    }
+
+    public Rect getInitialBounds() {
+        return mInitialBounds;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
similarity index 62%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
rename to quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 4120331..ccc587c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -18,12 +18,11 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -33,18 +32,19 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 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.PropertySetter;
 import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.graphics.OverviewScrim;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.util.DynamicResource;
@@ -59,7 +59,10 @@
 public class StaggeredWorkspaceAnim {
 
     private static final int APP_CLOSE_ROW_START_DELAY_MS = 10;
+    // How long it takes to fade in each staggered row.
     private static final int ALPHA_DURATION_MS = 250;
+    // Should be used for animations running alongside this StaggeredWorkspaceAnim.
+    public static final int DURATION_MS = 250;
 
     private static final float MAX_VELOCITY_PX_PER_S = 22f;
 
@@ -67,10 +70,18 @@
     private final float mSpringTransY;
 
     private final AnimatorSet mAnimators = new AnimatorSet();
+    private final @Nullable View mIgnoredView;
 
-    public StaggeredWorkspaceAnim(Launcher launcher, float velocity, boolean animateOverviewScrim) {
+    public StaggeredWorkspaceAnim(Launcher launcher, float velocity, boolean animateOverviewScrim,
+            @Nullable View ignoredView) {
+        this(launcher, velocity, animateOverviewScrim, ignoredView, true);
+    }
+
+    public StaggeredWorkspaceAnim(Launcher launcher, float velocity, boolean animateOverviewScrim,
+            @Nullable View ignoredView, boolean staggerWorkspace) {
         prepareToAnimate(launcher, animateOverviewScrim);
 
+        mIgnoredView = ignoredView;
         mVelocity = velocity;
 
         // Scale the translationY based on the initial velocity to better sync the workspace items
@@ -79,76 +90,98 @@
         mSpringTransY = transFactor * launcher.getResources()
                 .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);
 
-        DeviceProfile grid = launcher.getDeviceProfile();
-        Workspace workspace = launcher.getWorkspace();
-        CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
-        ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets();
-        ViewGroup hotseat = launcher.getHotseat();
+        if (staggerWorkspace) {
+            DeviceProfile grid = launcher.getDeviceProfile();
+            Workspace workspace = launcher.getWorkspace();
+            Hotseat hotseat = launcher.getHotseat();
 
-        boolean workspaceClipChildren = workspace.getClipChildren();
-        boolean workspaceClipToPadding = workspace.getClipToPadding();
-        boolean cellLayoutClipChildren = cellLayout.getClipChildren();
-        boolean cellLayoutClipToPadding = cellLayout.getClipToPadding();
-        boolean hotseatClipChildren = hotseat.getClipChildren();
-        boolean hotseatClipToPadding = hotseat.getClipToPadding();
+            // Hotseat and QSB takes up two additional rows.
+            int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
 
-        workspace.setClipChildren(false);
-        workspace.setClipToPadding(false);
-        cellLayout.setClipChildren(false);
-        cellLayout.setClipToPadding(false);
-        hotseat.setClipChildren(false);
-        hotseat.setClipToPadding(false);
+            // Add animation for all the visible workspace pages
+            workspace.forEachVisiblePage(page -> addAnimationForPage((CellLayout) page, totalRows));
 
-        // Hotseat and QSB takes up two additional rows.
-        int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
+            boolean workspaceClipChildren = workspace.getClipChildren();
+            boolean workspaceClipToPadding = workspace.getClipToPadding();
+            boolean hotseatClipChildren = hotseat.getClipChildren();
+            boolean hotseatClipToPadding = hotseat.getClipToPadding();
+
+            workspace.setClipChildren(false);
+            workspace.setClipToPadding(false);
+            hotseat.setClipChildren(false);
+            hotseat.setClipToPadding(false);
+
+            // Set up springs for the hotseat and qsb.
+            ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
+            if (grid.isVerticalBarLayout()) {
+                for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
+                    View child = hotseatIcons.getChildAt(i);
+                    CellLayout.LayoutParams lp =
+                            ((CellLayout.LayoutParams) child.getLayoutParams());
+                    addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
+                }
+            } else {
+                final int hotseatRow, qsbRow;
+                if (grid.isTaskbarPresent) {
+                    qsbRow = grid.inv.numRows + 1;
+                    hotseatRow = grid.inv.numRows + 2;
+                } else {
+                    hotseatRow = grid.inv.numRows + 1;
+                    qsbRow = grid.inv.numRows + 2;
+                }
+                for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
+                    View child = hotseatIcons.getChildAt(i);
+                    addStaggeredAnimationForView(child, hotseatRow, totalRows);
+                }
+
+                addStaggeredAnimationForView(hotseat.getQsb(), qsbRow, totalRows);
+            }
+
+            mAnimators.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    workspace.setClipChildren(workspaceClipChildren);
+                    workspace.setClipToPadding(workspaceClipToPadding);
+                    hotseat.setClipChildren(hotseatClipChildren);
+                    hotseat.setClipToPadding(hotseatClipToPadding);
+                }
+            });
+        }
+
+        if (animateOverviewScrim) {
+            PendingAnimation pendingAnimation = new PendingAnimation(DURATION_MS);
+            launcher.getWorkspace().getStateTransitionAnimation()
+                    .setScrim(pendingAnimation, NORMAL, new StateAnimationConfig());
+            mAnimators.play(pendingAnimation.buildAnim());
+        }
+
+        addDepthAnimationForState(launcher, NORMAL, DURATION_MS);
+
+        mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f)
+                .setDuration(DURATION_MS));
+    }
+
+    private void addAnimationForPage(CellLayout page, int totalRows) {
+        ShortcutAndWidgetContainer itemsContainer = page.getShortcutsAndWidgets();
+
+        boolean pageClipChildren = page.getClipChildren();
+        boolean pageClipToPadding = page.getClipToPadding();
+
+        page.setClipChildren(false);
+        page.setClipToPadding(false);
 
         // Set up springs on workspace items.
-        for (int i = currentPage.getChildCount() - 1; i >= 0; i--) {
-            View child = currentPage.getChildAt(i);
+        for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) {
+            View child = itemsContainer.getChildAt(i);
             CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
             addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows);
         }
 
-        // Set up springs for the hotseat and qsb.
-        ViewGroup hotseatChild = (ViewGroup) hotseat.getChildAt(0);
-        if (grid.isVerticalBarLayout()) {
-            for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
-                View child = hotseatChild.getChildAt(i);
-                CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
-                addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
-            }
-        } else {
-            for (int i = hotseatChild.getChildCount() - 1; i >= 0; i--) {
-                View child = hotseatChild.getChildAt(i);
-                addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows);
-            }
-
-            if (launcher.getAppsView().getSearchUiManager()
-                    .isQsbVisible(NORMAL.getVisibleElements(launcher))) {
-                addStaggeredAnimationForView(launcher.getAppsView().getSearchView(),
-                        grid.inv.numRows + 2, totalRows);
-            }
-        }
-
-        if (animateOverviewScrim) {
-            PendingAnimation pendingAnimation = new PendingAnimation(ALPHA_DURATION_MS);
-            addScrimAnimationForState(launcher, NORMAL, pendingAnimation);
-            mAnimators.play(pendingAnimation.buildAnim());
-        }
-
-        addDepthAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
-
-        mAnimators.play(launcher.getDragLayer().getScrim().createSysuiMultiplierAnim(0f, 1f)
-                .setDuration(ALPHA_DURATION_MS));
         mAnimators.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                workspace.setClipChildren(workspaceClipChildren);
-                workspace.setClipToPadding(workspaceClipToPadding);
-                cellLayout.setClipChildren(cellLayoutClipChildren);
-                cellLayout.setClipToPadding(cellLayoutClipToPadding);
-                hotseat.setClipChildren(hotseatClipChildren);
-                hotseat.setClipToPadding(hotseatClipToPadding);
+                page.setClipChildren(pageClipChildren);
+                page.setClipToPadding(pageClipToPadding);
             }
         });
     }
@@ -158,7 +191,7 @@
      */
     private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
         StateAnimationConfig config = new StateAnimationConfig();
-        config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
+        config.animFlags = SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER | SKIP_SCRIM;
         config.duration = 0;
         // setRecentsAttachedToAppWindow() will animate recents out.
         launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
@@ -167,7 +200,8 @@
         launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
 
         if (animateOverviewScrim) {
-            addScrimAnimationForState(launcher, BACKGROUND_APP, NO_ANIM_PROPERTY_SETTER);
+            launcher.getWorkspace().getStateTransitionAnimation()
+                    .setScrim(NO_ANIM_PROPERTY_SETTER, BACKGROUND_APP, config);
         }
     }
 
@@ -195,6 +229,7 @@
      * @param totalRows Total number of rows.
      */
     private void addStaggeredAnimationForView(View v, int row, int totalRows) {
+        if (mIgnoredView != null && mIgnoredView == v) return;
         // Invert the rows, because we stagger starting from the bottom of the screen.
         int invertedRow = totalRows - row;
         // Add 1 to the inverted row so that the bottom most row has a start delay.
@@ -238,16 +273,6 @@
         mAnimators.play(alpha);
     }
 
-    private void addScrimAnimationForState(Launcher launcher, LauncherState state,
-            PropertySetter setter) {
-        launcher.getWorkspace().getStateTransitionAnimation().setScrim(setter, state);
-        setter.setFloat(
-                launcher.getDragLayer().getOverviewScrim(),
-                OverviewScrim.SCRIM_PROGRESS,
-                state.getOverviewScrimAlpha(launcher),
-                ACCEL_DEACCEL);
-    }
-
     private void addDepthAnimationForState(Launcher launcher, LauncherState state, long duration) {
         if (!(launcher instanceof BaseQuickstepLauncher)) {
             return;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
similarity index 94%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java
rename to quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 0436a1c..3b4fd31 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -15,8 +15,6 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.systemui.shared.system.TransactionCompat.deferTransactionUntil;
-
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.os.Handler;
@@ -90,11 +88,10 @@
             for (int i = params.length - 1; i >= 0; i--) {
                 SurfaceParams surfaceParams = params[i];
                 if (surfaceParams.surface.isValid()) {
-                    deferTransactionUntil(t, surfaceParams.surface, mBarrierSurfaceControl, frame);
-                    surfaceParams.applyTo(t);
+                      surfaceParams.applyTo(t);
                 }
             }
-            t.apply();
+            mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
             Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
                     .sendToTarget();
         });
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
new file mode 100644
index 0000000..c0f5c14
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -0,0 +1,423 @@
+/*
+ * 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 com.android.systemui.shared.system.InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_PIP;
+
+import android.animation.Animator;
+import android.animation.RectEvaluator;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.window.PictureInPictureSurfaceTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.util.Themes;
+import com.android.systemui.shared.pip.PipSurfaceTransactionHelper;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+
+/**
+ * Subclass of {@link RectFSpringAnim} that animates an Activity to PiP (picture-in-picture) window
+ * when swiping up (in gesture navigation mode).
+ */
+public class SwipePipToHomeAnimator extends RectFSpringAnim {
+    private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName();
+
+    private static final float END_PROGRESS = 1.0f;
+
+    private final int mTaskId;
+    private final ComponentName mComponentName;
+    private final SurfaceControl mLeash;
+    private final Rect mAppBounds = new Rect();
+    private final Matrix mHomeToWindowPositionMap = new Matrix();
+    private final Rect mStartBounds = new Rect();
+    private final RectF mCurrentBoundsF = new RectF();
+    private final Rect mCurrentBounds = new Rect();
+    private final Rect mDestinationBounds = new Rect();
+    private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
+    /** for calculating transform in {@link #onAnimationUpdate(AppCloseConfig, RectF, float)} */
+    private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
+    private final Rect mSourceHintRectInsets;
+    private final Rect mSourceInsets = new Rect();
+
+    /** for rotation calculations */
+    private final @RecentsOrientedState.SurfaceRotation int mFromRotation;
+    private final Rect mDestinationBoundsTransformed = new Rect();
+
+    /**
+     * Flag to avoid the double-end problem since the leash would have been released
+     * after the first end call and any further operations upon it would lead to NPE.
+     */
+    private boolean mHasAnimationEnded;
+
+    /**
+     * An overlay used to mask changes in content when entering PiP for apps that aren't seamless.
+     */
+    @Nullable
+    private SurfaceControl mContentOverlay;
+
+    /**
+     * @param context {@link Context} provides Launcher resources
+     * @param taskId Task id associated with this animator, see also {@link #getTaskId()}
+     * @param componentName Component associated with this animator,
+     *                      see also {@link #getComponentName()}
+     * @param leash {@link SurfaceControl} this animator operates on
+     * @param sourceRectHint See the definition in {@link android.app.PictureInPictureParams}
+     * @param appBounds Bounds of the application, sourceRectHint is based on this bounds
+     * @param homeToWindowPositionMap {@link Matrix} to map a Rect from home to window space
+     * @param startBounds Bounds of the application when this animator starts. This can be
+     *                    different from the appBounds if user has swiped a certain distance and
+     *                    Launcher has performed transform on the leash.
+     * @param destinationBounds Bounds of the destination this animator ends to
+     * @param fromRotation From rotation if different from final rotation, ROTATION_0 otherwise
+     * @param destinationBoundsTransformed Destination bounds in window space
+     * @param cornerRadius Corner radius in pixel value for PiP window
+     * @param view Attached view for logging purpose
+     */
+    private SwipePipToHomeAnimator(@NonNull Context context,
+            int taskId,
+            @NonNull ComponentName componentName,
+            @NonNull SurfaceControl leash,
+            @Nullable Rect sourceRectHint,
+            @NonNull Rect appBounds,
+            @NonNull Matrix homeToWindowPositionMap,
+            @NonNull RectF startBounds,
+            @NonNull Rect destinationBounds,
+            @RecentsOrientedState.SurfaceRotation int fromRotation,
+            @NonNull Rect destinationBoundsTransformed,
+            int cornerRadius,
+            @NonNull View view) {
+        super(startBounds, new RectF(destinationBoundsTransformed), context);
+        mTaskId = taskId;
+        mComponentName = componentName;
+        mLeash = leash;
+        mAppBounds.set(appBounds);
+        mHomeToWindowPositionMap.set(homeToWindowPositionMap);
+        startBounds.round(mStartBounds);
+        mDestinationBounds.set(destinationBounds);
+        mFromRotation = fromRotation;
+        mDestinationBoundsTransformed.set(destinationBoundsTransformed);
+        mSurfaceTransactionHelper = new PipSurfaceTransactionHelper(cornerRadius);
+
+        if (sourceRectHint != null && (sourceRectHint.width() < destinationBounds.width()
+                || sourceRectHint.height() < destinationBounds.height())) {
+            // This is a situation in which the source hint rect on at least one axis is smaller
+            // than the destination bounds, which presents a problem because we would have to scale
+            // up that axis to fit the bounds. So instead, just fallback to the non-source hint
+            // animation in this case.
+            sourceRectHint = null;
+        }
+
+        if (sourceRectHint == null) {
+            mSourceHintRectInsets = null;
+
+            // Create a new overlay layer
+            SurfaceSession session = new SurfaceSession();
+            mContentOverlay = new SurfaceControl.Builder(session)
+                    .setCallsite("SwipePipToHomeAnimator")
+                    .setName("PipContentOverlay")
+                    .setColorLayer()
+                    .build();
+            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            t.show(mContentOverlay);
+            t.setLayer(mContentOverlay, Integer.MAX_VALUE);
+            int color = Themes.getColorBackground(view.getContext());
+            float[] bgColor = new float[] {Color.red(color) / 255f, Color.green(color) / 255f,
+                    Color.blue(color) / 255f};
+            t.setColor(mContentOverlay, bgColor);
+            t.setAlpha(mContentOverlay, 0f);
+            t.reparent(mContentOverlay, mLeash);
+            t.apply();
+
+            addOnUpdateListener((values, currentRect, progress) -> {
+                float alpha = progress < 0.5f
+                        ? 0
+                        : Utilities.mapToRange(Math.min(progress, 1f), 0.5f, 1f,
+                                0f, 1f, Interpolators.FAST_OUT_SLOW_IN);
+                t.setAlpha(mContentOverlay, alpha);
+                t.apply();
+            });
+        } else {
+            mSourceHintRectInsets = new Rect(sourceRectHint.left - appBounds.left,
+                    sourceRectHint.top - appBounds.top,
+                    appBounds.right - sourceRectHint.right,
+                    appBounds.bottom - sourceRectHint.bottom);
+        }
+
+        addAnimatorListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                InteractionJankMonitorWrapper.begin(view, CUJ_APP_CLOSE_TO_PIP);
+                super.onAnimationStart(animation);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                super.onAnimationCancel(animation);
+                InteractionJankMonitorWrapper.cancel(CUJ_APP_CLOSE_TO_PIP);
+            }
+
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                InteractionJankMonitorWrapper.end(CUJ_APP_CLOSE_TO_PIP);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mHasAnimationEnded) return;
+                super.onAnimationEnd(animation);
+                mHasAnimationEnded = true;
+            }
+        });
+        addOnUpdateListener(this::onAnimationUpdate);
+    }
+
+    private void onAnimationUpdate(@Nullable AppCloseConfig values, RectF currentRect,
+            float progress) {
+        if (mHasAnimationEnded) return;
+        final SurfaceControl.Transaction tx =
+                PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+        mHomeToWindowPositionMap.mapRect(mCurrentBoundsF, currentRect);
+        onAnimationUpdate(tx, mCurrentBoundsF, progress);
+        tx.apply();
+    }
+
+    private PictureInPictureSurfaceTransaction onAnimationUpdate(SurfaceControl.Transaction tx,
+            RectF currentRect, float progress) {
+        currentRect.round(mCurrentBounds);
+        final PictureInPictureSurfaceTransaction op;
+        if (mSourceHintRectInsets == null) {
+            // no source rect hint been set, directly scale the window down
+            op = onAnimationScale(progress, tx, mCurrentBounds);
+        } else {
+            // scale and crop according to the source rect hint
+            op = onAnimationScaleAndCrop(progress, tx, mCurrentBounds);
+        }
+        return op;
+    }
+
+    /** scale the window directly with no source rect hint being set */
+    private PictureInPictureSurfaceTransaction onAnimationScale(
+            float progress, SurfaceControl.Transaction tx, Rect bounds) {
+        if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
+            final RotatedPosition rotatedPosition = getRotatedPosition(progress);
+            return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds,
+                    rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
+        } else {
+            return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds);
+        }
+    }
+
+    /** scale and crop the window with source rect hint */
+    private PictureInPictureSurfaceTransaction onAnimationScaleAndCrop(
+            float progress, SurfaceControl.Transaction tx,
+            Rect bounds) {
+        final Rect insets = mInsetsEvaluator.evaluate(progress, mSourceInsets,
+                mSourceHintRectInsets);
+        if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
+            final RotatedPosition rotatedPosition = getRotatedPosition(progress);
+            return mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
+                    rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
+        } else {
+            return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
+        }
+    }
+
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    public Rect getDestinationBounds() {
+        return mDestinationBounds;
+    }
+
+    @Nullable
+    public SurfaceControl getContentOverlay() {
+        return mContentOverlay;
+    }
+
+    /** @return {@link PictureInPictureSurfaceTransaction} for the final leash transaction. */
+    public PictureInPictureSurfaceTransaction getFinishTransaction() {
+        // get the final leash operations but do not apply to the leash.
+        final SurfaceControl.Transaction tx =
+                PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+        return onAnimationUpdate(tx, new RectF(mDestinationBounds), END_PROGRESS);
+    }
+
+    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;
+        } 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);
+    }
+
+    /** Builder class for {@link SwipePipToHomeAnimator} */
+    public static class Builder {
+        private Context mContext;
+        private int mTaskId;
+        private ComponentName mComponentName;
+        private SurfaceControl mLeash;
+        private Rect mSourceRectHint;
+        private Rect mDisplayCutoutInsets;
+        private Rect mAppBounds;
+        private Matrix mHomeToWindowPositionMap;
+        private RectF mStartBounds;
+        private Rect mDestinationBounds;
+        private int mCornerRadius;
+        private View mAttachedView;
+        private @RecentsOrientedState.SurfaceRotation int mFromRotation = Surface.ROTATION_0;
+        private final Rect mDestinationBoundsTransformed = new Rect();
+
+        public Builder setContext(Context context) {
+            mContext = context;
+            return this;
+        }
+
+        public Builder setTaskId(int taskId) {
+            mTaskId = taskId;
+            return this;
+        }
+
+        public Builder setComponentName(ComponentName componentName) {
+            mComponentName = componentName;
+            return this;
+        }
+
+        public Builder setLeash(SurfaceControl leash) {
+            mLeash = leash;
+            return this;
+        }
+
+        public Builder setSourceRectHint(Rect sourceRectHint) {
+            mSourceRectHint = new Rect(sourceRectHint);
+            return this;
+        }
+
+        public Builder setAppBounds(Rect appBounds) {
+            mAppBounds = new Rect(appBounds);
+            return this;
+        }
+
+        public Builder setHomeToWindowPositionMap(Matrix homeToWindowPositionMap) {
+            mHomeToWindowPositionMap = new Matrix(homeToWindowPositionMap);
+            return this;
+        }
+
+        public Builder setStartBounds(RectF startBounds) {
+            mStartBounds = new RectF(startBounds);
+            return this;
+        }
+
+        public Builder setDestinationBounds(Rect destinationBounds) {
+            mDestinationBounds = new Rect(destinationBounds);
+            return this;
+        }
+
+        public Builder setCornerRadius(int cornerRadius) {
+            mCornerRadius = cornerRadius;
+            return this;
+        }
+
+        public Builder setAttachedView(View attachedView) {
+            mAttachedView = attachedView;
+            return this;
+        }
+
+        public Builder setFromRotation(TaskViewSimulator taskViewSimulator,
+                @RecentsOrientedState.SurfaceRotation int fromRotation,
+                Rect displayCutoutInsets) {
+            if (fromRotation != Surface.ROTATION_90 && fromRotation != Surface.ROTATION_270) {
+                Log.wtf(TAG, "Not a supported rotation, rotation=" + fromRotation);
+                return this;
+            }
+            final Matrix matrix = new Matrix();
+            taskViewSimulator.applyWindowToHomeRotation(matrix);
+
+            // map the destination bounds into window space. mDestinationBounds is always calculated
+            // in the final home space and the animation runs in original window space.
+            final RectF transformed = new RectF(mDestinationBounds);
+            matrix.mapRect(transformed, new RectF(mDestinationBounds));
+            transformed.round(mDestinationBoundsTransformed);
+
+            mFromRotation = fromRotation;
+            if (displayCutoutInsets != null) {
+                mDisplayCutoutInsets = new Rect(displayCutoutInsets);
+            }
+            return this;
+        }
+
+        public SwipePipToHomeAnimator build() {
+            if (mDestinationBoundsTransformed.isEmpty()) {
+                mDestinationBoundsTransformed.set(mDestinationBounds);
+            }
+            // adjust the mSourceRectHint / mAppBounds by display cutout if applicable.
+            if (mSourceRectHint != null && mDisplayCutoutInsets != null) {
+                if (mFromRotation == Surface.ROTATION_90) {
+                    mSourceRectHint.offset(mDisplayCutoutInsets.left, mDisplayCutoutInsets.top);
+                } else if (mFromRotation == Surface.ROTATION_270) {
+                    mAppBounds.inset(mDisplayCutoutInsets);
+                }
+            }
+            return new SwipePipToHomeAnimator(mContext, mTaskId, mComponentName, mLeash,
+                    mSourceRectHint, mAppBounds,
+                    mHomeToWindowPositionMap, mStartBounds, mDestinationBounds,
+                    mFromRotation, mDestinationBoundsTransformed,
+                    mCornerRadius, mAttachedView);
+        }
+    }
+
+    private static class RotatedPosition {
+        private final float degree;
+        private final float positionX;
+        private final float positionY;
+
+        private RotatedPosition(float degree, float positionX, float positionY) {
+            this.degree = degree;
+            this.positionX = positionX;
+            this.positionY = positionY;
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java b/quickstep/src/com/android/quickstep/util/TaskCornerRadius.java
similarity index 68%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java
rename to quickstep/src/com/android/quickstep/util/TaskCornerRadius.java
index 3ddf1b6..6f9c99a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java
+++ b/quickstep/src/com/android/quickstep/util/TaskCornerRadius.java
@@ -18,6 +18,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
 
 import android.content.Context;
+import android.content.res.Resources;
 
 import com.android.launcher3.R;
 import com.android.launcher3.util.Themes;
@@ -25,8 +26,13 @@
 public class TaskCornerRadius {
 
     public static float get(Context context) {
-        return supportsRoundedCornersOnWindows(context.getResources()) ?
-                Themes.getDialogCornerRadius(context):
-                context.getResources().getDimension(R.dimen.task_corner_radius_small);
+        Resources resources = context.getResources();
+        if (!supportsRoundedCornersOnWindows(resources)) {
+            return resources.getDimension(R.dimen.task_corner_radius_small);
+        }
+
+        float overriddenRadius =
+                resources.getDimension(R.dimen.task_corner_radius_override);
+        return (overriddenRadius > 0) ? overriddenRadius : Themes.getDialogCornerRadius(context);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
similarity index 69%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
rename to quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index c060145..cceb872 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,30 +15,31 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.states.RotationHelper.deltaRotation;
 import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
 import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
+import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
 import android.animation.TimeInterpolator;
 import android.content.Context;
-import android.content.res.Configuration;
+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.IntProperty;
+
+import androidx.annotation.NonNull;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
-import com.android.quickstep.views.RecentsView.ScrollState;
 import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
-import com.android.quickstep.views.TaskView;
 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -49,32 +50,25 @@
  */
 public class TaskViewSimulator implements TransformParams.BuilderProxy {
 
-    public static final IntProperty<TaskViewSimulator> SCROLL =
-            new IntProperty<TaskViewSimulator>("scroll") {
-        @Override
-        public void setValue(TaskViewSimulator simulator, int i) {
-            simulator.setScroll(i);
-        }
-
-        @Override
-        public Integer get(TaskViewSimulator simulator) {
-            return simulator.mScrollState.scroll;
-        }
-    };
-
     private final Rect mTmpCropRect = new Rect();
     private final RectF mTempRectF = new RectF();
     private final float[] mTempPoint = new float[2];
 
-    private final RecentsOrientedState mOrientationState;
     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;
 
     private final Matrix mMatrix = new Matrix();
+    private final Matrix mMatrixTmp = new Matrix();
     private final Point mRunningTargetWindowPosition = new Point();
 
     // Thumbnail view properties
@@ -85,26 +79,38 @@
 
     // TaskView properties
     private final FullscreenDrawParams mCurrentFullscreenParams;
-    private float mCurveScale = 1;
+    public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat();
+    public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat();
 
     // RecentsView properties
     public final AnimatedFloat recentsViewScale = new AnimatedFloat();
     public final AnimatedFloat fullScreenProgress = new AnimatedFloat();
     public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat();
-    private final ScrollState mScrollState = new ScrollState();
+    public final AnimatedFloat recentsViewPrimaryTranslation = new AnimatedFloat();
+    public final AnimatedFloat recentsViewScroll = new AnimatedFloat();
 
     // Cached calculations
     private boolean mLayoutValid = false;
-    private boolean mScrollValid = false;
+    private int mOrientationStateId;
 
     public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
+        this(context, sizeStrategy, false);
+    }
+
+    public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy,
+            boolean isForLiveTile) {
         mContext = context;
         mSizeStrategy = sizeStrategy;
+        mIsForLiveTile = isForLiveTile;
 
-        mOrientationState = new RecentsOrientedState(context, sizeStrategy, i -> { });
+        // TODO(b/187074722): Don't create this per-TaskViewSimulator
+        mOrientationState = TraceHelper.allowIpcs("",
+                () -> new RecentsOrientedState(context, sizeStrategy, i -> { }));
         mOrientationState.setGestureActive(true);
-
         mCurrentFullscreenParams = new FullscreenDrawParams(context);
+        mOrientationStateId = mOrientationState.getStateId();
+        Resources resources = context.getResources();
+        mIsRecentsRtl = mOrientationState.getOrientationHandler().getRecentsRtlSetting(resources);
     }
 
     /**
@@ -112,23 +118,15 @@
      */
     public void setDp(DeviceProfile dp) {
         mDp = dp;
-        mOrientationState.setMultiWindowMode(mDp.isMultiWindowMode);
         mLayoutValid = false;
+        mOrientationState.setDeviceProfile(dp);
     }
 
     /**
-     * @see com.android.quickstep.views.RecentsView#setLayoutRotation(int, int)
+     * Sets the orientation state used for this animation
      */
-    public void setLayoutRotation(int touchRotation, int displayRotation) {
-        mOrientationState.update(touchRotation, displayRotation);
-        mLayoutValid = false;
-    }
-
-    /**
-     * @see com.android.quickstep.views.RecentsView#onConfigurationChanged(Configuration)
-     */
-    public void setRecentsRotation(int recentsRotation) {
-        mOrientationState.setRecentsRotation(recentsRotation);
+    public void setOrientationState(@NonNull RecentsOrientedState orientationState) {
+        mOrientationState = orientationState;
         mLayoutValid = false;
     }
 
@@ -168,15 +166,16 @@
     /**
      * Updates the scroll for RecentsView
      */
-    public void setScroll(int scroll) {
-        if (mScrollState.scroll != scroll) {
-            mScrollState.scroll = scroll;
-            mScrollValid = false;
-        }
+    public void setScroll(float scroll) {
+        recentsViewScroll.value = scroll;
+    }
+
+    public void setDrawsBelowRecents(boolean drawsBelowRecents) {
+        mDrawsBelowRecents = drawsBelowRecents;
     }
 
     /**
-     * Adds animation for all the components corresponding to transition from an app to overview
+     * Adds animation for all the components corresponding to transition from an app to overview.
      */
     public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
         pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
@@ -184,6 +183,14 @@
     }
 
     /**
+     * Adds animation for all the components corresponding to transition from overview to the app.
+     */
+    public void addOverviewToAppAnim(PendingAnimation pa, TimeInterpolator interpolator) {
+        pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 0, 1, interpolator);
+        pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, 1, getFullScreenScale(), interpolator);
+    }
+
+    /**
      * Returns the current clipped/visible window bounds in the window coordinate space
      */
     public RectF getCurrentCropRect() {
@@ -195,6 +202,18 @@
         return mTempRectF;
     }
 
+    /**
+     * Returns the current task bounds in the Launcher coordinate space.
+     */
+    public RectF getCurrentRect() {
+        RectF result = getCurrentCropRect();
+        mMatrixTmp.set(mMatrix);
+        preDisplayRotation(mOrientationState.getDisplayRotation(), mDp.widthPx, mDp.heightPx,
+                mMatrixTmp);
+        mMatrixTmp.mapRect(result);
+        return result;
+    }
+
     public RecentsOrientedState getOrientationState() {
         return mOrientationState;
     }
@@ -226,37 +245,26 @@
         if (mDp == null || mThumbnailPosition.isEmpty()) {
             return;
         }
-        if (!mLayoutValid) {
+        if (!mLayoutValid || mOrientationStateId != mOrientationState.getStateId()) {
             mLayoutValid = true;
+            mOrientationStateId = mOrientationState.getStateId();
 
             getFullScreenScale();
             mThumbnailData.rotation = mOrientationState.getDisplayRotation();
 
+            // mIsRecentsRtl is the inverse of TaskView RTL.
+            boolean isRtlEnabled = !mIsRecentsRtl;
             mPositionHelper.updateThumbnailMatrix(
                     mThumbnailPosition, mThumbnailData,
                     mTaskRect.width(), mTaskRect.height(),
-                    mDp, mOrientationState.getRecentsActivityRotation());
+                    mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
             mPositionHelper.getMatrix().invert(mInversePositionMatrix);
-
-            PagedOrientationHandler poh = mOrientationState.getOrientationHandler();
-            mScrollState.halfPageSize =
-                    poh.getPrimaryValue(mTaskRect.width(), mTaskRect.height()) / 2;
-            mScrollState.halfScreenSize = poh.getPrimaryValue(mDp.widthPx, mDp.heightPx) / 2;
-            mScrollValid = false;
         }
 
-        if (!mScrollValid) {
-            mScrollValid = true;
-            int start = mOrientationState.getOrientationHandler()
-                    .getPrimaryValue(mTaskRect.left, mTaskRect.top);
-            mScrollState.screenCenter = start + mScrollState.scroll + mScrollState.halfPageSize;
-            mScrollState.updateInterpolation(start);
-            mCurveScale = TaskView.getCurveScaleForInterpolation(mScrollState.linearInterpolation);
-        }
-
-        float progress = Utilities.boundToRange(fullScreenProgress.value, 0, 1);
+        float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);
         mCurrentFullscreenParams.setProgress(
-                progress, recentsViewScale.value, mTaskRect.width(), mDp, mPositionHelper);
+                fullScreenProgress, recentsViewScale.value, mTaskRect.width(), mDp,
+                mPositionHelper);
 
         // Apply thumbnail matrix
         RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets;
@@ -268,16 +276,21 @@
         mMatrix.postTranslate(insets.left, insets.top);
         mMatrix.postScale(scale, scale);
 
-        // Apply TaskView matrix: translate, scale, scroll
+        // Apply TaskView matrix: translate, scroll
         mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
-        mMatrix.postScale(mCurveScale, mCurveScale, taskWidth / 2, taskHeight / 2);
+        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+                taskPrimaryTranslation.value);
+        mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
+                taskSecondaryTranslation.value);
         mOrientationState.getOrientationHandler().set(
-                mMatrix, MATRIX_POST_TRANSLATE, mScrollState.scroll);
+                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,
+                recentsViewPrimaryTranslation.value);
         applyWindowToHomeRotation(mMatrix);
 
         // Crop rect is the inverse of thumbnail matrix
@@ -295,6 +308,13 @@
         builder.withMatrix(mMatrix)
                 .withWindowCrop(mTmpCropRect)
                 .withCornerRadius(getCurrentCornerRadius());
+
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mIsForLiveTile
+                && params.getRecentsSurface() != null) {
+            // When relativeLayer = 0, it reverts the surfaces back to the original order.
+            builder.withRelativeLayerTo(params.getRecentsSurface(),
+                    mDrawsBelowRecents ? Integer.MIN_VALUE : 0);
+        }
     }
 
     /**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
similarity index 89%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
rename to quickstep/src/com/android/quickstep/util/TransformParams.java
index 0135f74..03d7a37 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -16,6 +16,7 @@
 package com.android.quickstep.util;
 
 import android.util.FloatProperty;
+import android.view.SurfaceControl;
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
@@ -58,6 +59,7 @@
     private float mCornerRadius;
     private RemoteAnimationTargets mTargetSet;
     private SurfaceTransactionApplier mSyncTransactionApplier;
+    private SurfaceControl mRecentsSurface;
 
     private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
     private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
@@ -138,6 +140,8 @@
     public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
         RemoteAnimationTargets targets = mTargetSet;
         SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length];
+        mRecentsSurface = getRecentsSurface(targets);
+
         for (int i = 0; i < targets.unfilteredApps.length; i++) {
             RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
             SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
@@ -165,6 +169,20 @@
         return surfaceParams;
     }
 
+    private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
+        for (int i = 0; i < targets.unfilteredApps.length; i++) {
+            RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
+            if (app.mode == targets.targetMode) {
+                if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
+                    return app.leash.getSurfaceControl();
+                }
+            } else {
+                return app.leash.getSurfaceControl();
+            }
+        }
+        return null;
+    }
+
     // Pubic getters so outside packages can read the values.
 
     public float getProgress() {
@@ -179,11 +197,15 @@
         return mCornerRadius;
     }
 
+    public SurfaceControl getRecentsSurface() {
+        return mRecentsSurface;
+    }
+
     public RemoteAnimationTargets getTargetSet() {
         return mTargetSet;
     }
 
-    public void applySurfaceParams(SurfaceParams[] params) {
+    public void applySurfaceParams(SurfaceParams... params) {
         if (mSyncTransactionApplier != null) {
             mSyncTransactionApplier.scheduleApply(params);
         } else {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
rename to quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
index 29b9558..7bbde30 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
+++ b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
@@ -21,13 +21,14 @@
 import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
 
 import android.content.Context;
 import android.graphics.PointF;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
 
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 
 /**
@@ -50,7 +51,8 @@
             NavBarPosition navBarPosition, Runnable onInterceptTouch,
             OnSwipeUpListener onSwipeUp) {
         mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
-        mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
+        mMinFlingVelocity = context.getResources().getDimension(
+                R.dimen.quickstep_fling_threshold_speed);
         mNavBarPosition = navBarPosition;
         mDisableHorizontalSwipe = disableHorizontalSwipe;
         mOnInterceptTouch = onInterceptTouch;
@@ -130,7 +132,7 @@
     }
 
     private void onGestureEnd(MotionEvent ev) {
-        mVelocityTracker.computeCurrentVelocity(1000);
+        mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
         float velocityX = mVelocityTracker.getXVelocity();
         float velocityY = mVelocityTracker.getYVelocity();
         float velocity = mNavBarPosition.isRightEdge()
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
new file mode 100644
index 0000000..df94d0b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java
@@ -0,0 +1,146 @@
+/*
+ * 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.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.util.DynamicResource;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.plugins.ResourceProvider;
+
+/**
+ * Creates an animation that reveals the workspace.
+ * This is used in conjunction with the swipe up to home animation.
+ */
+public class WorkspaceRevealAnim {
+
+    // Should be used for animations running alongside this WorkspaceRevealAnim.
+    public static final int DURATION_MS = 350;
+
+    private final float mScaleStart;
+    private final AnimatorSet mAnimators = new AnimatorSet();
+
+    public WorkspaceRevealAnim(Launcher launcher, boolean animateOverviewScrim) {
+        prepareToAnimate(launcher, animateOverviewScrim);
+
+        ResourceProvider rp = DynamicResource.provider(launcher);
+        mScaleStart = rp.getFloat(R.dimen.swipe_up_scale_start);
+
+        Workspace workspace = launcher.getWorkspace();
+        workspace.setPivotToScaleWithSelf(launcher.getHotseat());
+
+        // Add reveal animations.
+        addRevealAnimatorsForView(workspace);
+        addRevealAnimatorsForView(launcher.getHotseat());
+
+        // Add overview scrim animation.
+        if (animateOverviewScrim) {
+            PendingAnimation overviewScrimBuilder = new PendingAnimation(DURATION_MS);
+            launcher.getWorkspace().getStateTransitionAnimation()
+                    .setScrim(overviewScrimBuilder, NORMAL, new StateAnimationConfig());
+            mAnimators.play(overviewScrimBuilder.buildAnim());
+        }
+
+        // Add depth controller animation.
+        if (launcher instanceof BaseQuickstepLauncher) {
+            PendingAnimation depthBuilder = new PendingAnimation(DURATION_MS);
+            DepthController depth = ((BaseQuickstepLauncher) launcher).getDepthController();
+            depth.setStateWithAnimation(NORMAL, new StateAnimationConfig(), depthBuilder);
+            mAnimators.play(depthBuilder.buildAnim());
+        }
+
+        // Add sysui scrim animation.
+        mAnimators.play(launcher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f));
+
+        mAnimators.setDuration(DURATION_MS);
+        mAnimators.setInterpolator(Interpolators.DECELERATED_EASE);
+    }
+
+    private void addRevealAnimatorsForView(View v) {
+        ObjectAnimator scale = ObjectAnimator.ofFloat(v, SCALE_PROPERTY, mScaleStart, 1f);
+        scale.setDuration(DURATION_MS);
+        scale.setInterpolator(Interpolators.DECELERATED_EASE);
+        mAnimators.play(scale);
+
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0, 1f);
+        alpha.setDuration(DURATION_MS);
+        alpha.setInterpolator(Interpolators.DECELERATED_EASE);
+        mAnimators.play(alpha);
+
+        mAnimators.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                SCALE_PROPERTY.set(v, 1f);
+                v.setAlpha(1f);
+            }
+        });
+    }
+
+    /**
+     * Setup workspace with 0 duration.
+     */
+    private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
+        StateAnimationConfig config = new StateAnimationConfig();
+        config.animFlags = SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER | SKIP_SCRIM;
+        config.duration = 0;
+        // setRecentsAttachedToAppWindow() will animate recents out.
+        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);
+
+        if (animateOverviewScrim) {
+            launcher.getWorkspace().getStateTransitionAnimation()
+                    .setScrim(NO_ANIM_PROPERTY_SETTER, BACKGROUND_APP, config);
+        }
+    }
+
+    public AnimatorSet getAnimators() {
+        return mAnimators;
+    }
+
+    public WorkspaceRevealAnim addAnimatorListener(Animator.AnimatorListener listener) {
+        mAnimators.addListener(listener);
+        return this;
+    }
+
+    /**
+     * Starts the animation.
+     */
+    public void start() {
+        mAnimators.start();
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
similarity index 74%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
rename to quickstep/src/com/android/quickstep/views/AllAppsEduView.java
index c06dd9c..e9d7c3c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
+++ b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
@@ -17,12 +17,11 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_EDU_SHOWN;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -34,7 +33,7 @@
 import android.graphics.drawable.GradientDrawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
-import android.view.ViewGroup;
+import android.view.View;
 
 import androidx.core.graphics.ColorUtils;
 
@@ -43,22 +42,21 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
 import com.android.launcher3.util.Themes;
 import com.android.quickstep.util.MultiValueUpdateListener;
 
 /**
  * View used to educate the user on how to access All Apps when in No Nav Button navigation mode.
- * Consumes all touches until after the animation is completed and the view is removed.
+ * If the user drags on the view, the animation is overridden so the user can swipe to All Apps or
+ * Home.
  */
 public class AllAppsEduView extends AbstractFloatingView {
 
     private Launcher mLauncher;
+    private AllAppsEduTouchController mTouchController;
 
     private AnimatorSet mAnimation;
 
@@ -70,6 +68,8 @@
     private int mWidthPx;
     private int mMaxHeightPx;
 
+    private boolean mCanInterceptTouch;
+
     public AllAppsEduView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mCircle = (GradientDrawable) context.getDrawable(R.drawable.all_apps_edu_circle);
@@ -105,11 +105,6 @@
     }
 
     @Override
-    public void logActionCommand(int command) {
-        // TODO
-    }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_ALL_APPS_EDU) != 0;
     }
@@ -124,8 +119,49 @@
         return true;
     }
 
+
+    private boolean shouldInterceptTouch(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mCanInterceptTouch = (ev.getEdgeFlags() & EDGE_NAV_BAR) == 0;
+        }
+        return mCanInterceptTouch;
+    }
+
+    @Override
+    public boolean onControllerTouchEvent(MotionEvent ev) {
+        if (shouldInterceptTouch(ev)) {
+            mTouchController.onControllerTouchEvent(ev);
+            updateAnimationOnTouchEvent(ev);
+        }
+        return true;
+    }
+
+    private void updateAnimationOnTouchEvent(MotionEvent ev) {
+        if (mAnimation == null) {
+            return;
+        }
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mAnimation.pause();
+                return;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mAnimation.resume();
+                return;
+        }
+
+        if (mTouchController.isDraggingOrSettling()) {
+            mAnimation = null;
+            handleClose(false);
+        }
+    }
+
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (shouldInterceptTouch(ev)) {
+            mTouchController.onControllerInterceptTouchEvent(ev);
+            updateAnimationOnTouchEvent(ev);
+        }
         return true;
     }
 
@@ -146,19 +182,9 @@
         int secondPart = 1200;
         int introDuration = firstPart + secondPart;
 
-        StateAnimationConfig config = new StateAnimationConfig();
-        config.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
-                0, 0.08f));
-        config.duration = secondPart;
-        config.userControlled = false;
         AnimatorPlaybackController stateAnimationController =
-                mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, config);
-        float maxAllAppsProgress = mLauncher.getDeviceProfile().isLandscape ? 0.35f : 0.15f;
-
-        AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
-        PendingAnimation allAppsAlpha = new PendingAnimation(config.duration);
-        allAppsController.setAlphas(ALL_APPS, config, allAppsAlpha);
-        mAnimation.play(allAppsAlpha.buildAnim());
+                mTouchController.initAllAppsAnimation();
+        float maxAllAppsProgress = 0.75f;
 
         ValueAnimator intro = ValueAnimator.ofFloat(0, 1f);
         intro.setInterpolator(LINEAR);
@@ -170,7 +196,7 @@
             FloatProp mGradientAlpha = new FloatProp(0, 255, firstPart, secondPart * 0.3f, LINEAR);
 
             @Override
-            public void onUpdate(float progress) {
+            public void onUpdate(float progress, boolean initOnly) {
                 temp.set(circleBoundsOg);
                 temp.offset(0, (int) -mDeltaY.value);
                 Utilities.scaleRectAboutCenter(temp, mCircleScale.value);
@@ -195,6 +221,7 @@
                 mGradient.setAlpha(0);
             }
         });
+        mLauncher.getAppsView().setVisibility(View.VISIBLE);
         mAnimation.play(intro);
 
         ValueAnimator closeAllApps = ValueAnimator.ofFloat(maxAllAppsProgress, 0f);
@@ -220,15 +247,16 @@
 
     private void init(Launcher launcher) {
         mLauncher = launcher;
+        mTouchController = new AllAppsEduTouchController(mLauncher);
 
         int accentColor = Themes.getColorAccent(launcher);
         mGradient = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
                 Themes.getAttrBoolean(launcher, R.attr.isMainColorDark)
-                        ? new int[] {0xB3FFFFFF, 0x00FFFFFF}
-                        : new int[] {ColorUtils.setAlphaComponent(accentColor, 127),
+                        ? new int[]{0xB3FFFFFF, 0x00FFFFFF}
+                        : new int[]{ColorUtils.setAlphaComponent(accentColor, 127),
                                 ColorUtils.setAlphaComponent(accentColor, 0)});
         float r = mWidthPx / 2f;
-        mGradient.setCornerRadii(new float[] {r, r, r, r, 0, 0, 0, 0});
+        mGradient.setCornerRadii(new float[]{r, r, r, r, 0, 0, 0, 0});
 
         int top = mMaxHeightPx - mCircleSizePx + mPaddingPx;
         mCircle.setBounds(mPaddingPx, top, mPaddingPx + mCircleSizePx, top + mCircleSizePx);
@@ -247,9 +275,8 @@
      */
     public static void show(Launcher launcher) {
         final DragLayer dragLayer = launcher.getDragLayer();
-        ViewGroup parent = (ViewGroup) dragLayer.getParent();
-        AllAppsEduView view = launcher.getViewCache().getView(R.layout.all_apps_edu_view,
-                launcher, parent);
+        AllAppsEduView view = (AllAppsEduView) launcher.getLayoutInflater().inflate(
+                R.layout.all_apps_edu_view, dragLayer, false);
         view.init(launcher);
         launcher.getDragLayer().addView(view);
         launcher.getStatsLogManager().logger().log(LAUNCHER_ALL_APPS_EDU_SHOWN);
@@ -257,4 +284,27 @@
         view.requestLayout();
         view.playAnimation();
     }
+
+    private static class AllAppsEduTouchController extends PortraitStatesTouchController {
+
+        private AllAppsEduTouchController(Launcher l) {
+            super(l);
+        }
+
+        @Override
+        protected boolean canInterceptTouch(MotionEvent ev) {
+            return true;
+        }
+
+        private AnimatorPlaybackController initAllAppsAnimation() {
+            mFromState = NORMAL;
+            mToState = ALL_APPS;
+            mProgressMultiplier = initCurrentAnimation();
+            return mCurrentAnimation;
+        }
+
+        private boolean isDraggingOrSettling() {
+            return mDetector.isDraggingOrSettling();
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
new file mode 100644
index 0000000..b9a9006
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.widget.Button;
+
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.touch.PagedOrientationHandler;
+
+public class ClearAllButton extends Button {
+
+    public static final FloatProperty<ClearAllButton> VISIBILITY_ALPHA =
+            new FloatProperty<ClearAllButton>("visibilityAlpha") {
+                @Override
+                public Float get(ClearAllButton view) {
+                    return view.mVisibilityAlpha;
+                }
+
+                @Override
+                public void setValue(ClearAllButton view, float v) {
+                    view.setVisibilityAlpha(v);
+                }
+            };
+
+    private final StatefulActivity mActivity;
+    private float mScrollAlpha = 1;
+    private float mContentAlpha = 1;
+    private float mVisibilityAlpha = 1;
+    private float mFullscreenProgress = 1;
+    private float mGridProgress = 1;
+
+    private boolean mIsRtl;
+    private float mNormalTranslationPrimary;
+    private float mFullscreenTranslationPrimary;
+    private float mGridTranslationPrimary;
+    private float mGridScrollOffset;
+    private float mScrollOffsetPrimary;
+
+    private int mSidePadding;
+
+    public ClearAllButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        mActivity = StatefulActivity.fromContext(context);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
+        mSidePadding = orientationHandler.getClearAllSidePadding(getRecentsView(), mIsRtl);
+    }
+
+    private RecentsView getRecentsView() {
+        return (RecentsView) getParent();
+    }
+
+    @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        super.onRtlPropertiesChanged(layoutDirection);
+        mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    public void setContentAlpha(float alpha) {
+        if (mContentAlpha != alpha) {
+            mContentAlpha = alpha;
+            updateAlpha();
+        }
+    }
+
+    public void setVisibilityAlpha(float alpha) {
+        if (mVisibilityAlpha != alpha) {
+            mVisibilityAlpha = alpha;
+            updateAlpha();
+        }
+    }
+
+    public void onRecentsViewScroll(int scroll, boolean gridEnabled) {
+        RecentsView recentsView = getRecentsView();
+        if (recentsView == null) {
+            return;
+        }
+
+        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+        float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight());
+        if (orientationSize == 0) {
+            return;
+        }
+
+        int clearAllScroll = recentsView.getClearAllScroll();
+        int adjustedScrollFromEdge = Math.abs(scroll - clearAllScroll);
+        float shift = Math.min(adjustedScrollFromEdge, orientationSize);
+        mNormalTranslationPrimary = mIsRtl ? -shift : shift;
+        if (!gridEnabled) {
+            mNormalTranslationPrimary += mSidePadding;
+        }
+        applyPrimaryTranslation();
+        applySecondaryTranslation();
+        mScrollAlpha = 1 - shift / orientationSize;
+        updateAlpha();
+    }
+
+    private void updateAlpha() {
+        final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha;
+        setAlpha(alpha);
+        setClickable(Math.min(alpha, 1) == 1);
+    }
+
+    public void setFullscreenTranslationPrimary(float fullscreenTranslationPrimary) {
+        mFullscreenTranslationPrimary = fullscreenTranslationPrimary;
+        applyPrimaryTranslation();
+    }
+
+    public void setGridTranslationPrimary(float gridTranslationPrimary) {
+        mGridTranslationPrimary = gridTranslationPrimary;
+        applyPrimaryTranslation();
+    }
+
+    public void setGridScrollOffset(float gridScrollOffset) {
+        mGridScrollOffset = gridScrollOffset;
+    }
+
+    public void setScrollOffsetPrimary(float scrollOffsetPrimary) {
+        mScrollOffsetPrimary = scrollOffsetPrimary;
+    }
+
+    public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+        float scrollAdjustment = 0;
+        if (fullscreenEnabled) {
+            scrollAdjustment += mFullscreenTranslationPrimary;
+        }
+        if (gridEnabled) {
+            scrollAdjustment += mGridTranslationPrimary + mGridScrollOffset;
+        }
+        scrollAdjustment += mScrollOffsetPrimary;
+        return scrollAdjustment;
+    }
+
+    public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+        return getScrollAdjustment(fullscreenEnabled, gridEnabled);
+    }
+
+    /**
+     * Adjust translation when this TaskView is about to be shown fullscreen.
+     *
+     * @param progress: 0 = no translation; 1 = translate according to TaskVIew translations.
+     */
+    public void setFullscreenProgress(float progress) {
+        mFullscreenProgress = progress;
+        applyPrimaryTranslation();
+    }
+
+    /**
+     * Moves ClearAllButton between carousel and 2 row grid.
+     *
+     * @param gridProgress 0 = carousel; 1 = 2 row grid.
+     */
+    public void setGridProgress(float gridProgress) {
+        mGridProgress = gridProgress;
+        applyPrimaryTranslation();
+    }
+
+    private void applyPrimaryTranslation() {
+        RecentsView recentsView = getRecentsView();
+        if (recentsView == null) {
+            return;
+        }
+
+        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+        orientationHandler.getPrimaryViewTranslate().set(this,
+                orientationHandler.getPrimaryValue(0f, getOriginalTranslationY())
+                        + mNormalTranslationPrimary + getFullscreenTrans(
+                        mFullscreenTranslationPrimary) + getGridTrans(mGridTranslationPrimary));
+    }
+
+    private void applySecondaryTranslation() {
+        RecentsView recentsView = getRecentsView();
+        if (recentsView == null) {
+            return;
+        }
+
+        PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+        orientationHandler.getSecondaryViewTranslate().set(this,
+                orientationHandler.getSecondaryValue(0f, getOriginalTranslationY()));
+    }
+
+    private float getFullscreenTrans(float endTranslation) {
+        return mFullscreenProgress > 0 ? endTranslation : 0;
+    }
+
+    private float getGridTrans(float endTranslation) {
+        return mGridProgress > 0 ? endTranslation : 0;
+    }
+
+    /**
+     * Get the Y translation that is set in the original layout position, before scrolling.
+     */
+    private float getOriginalTranslationY() {
+        return mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx / 2.0f;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
similarity index 74%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
rename to quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index b06d4bc..7c8041c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -17,6 +17,8 @@
 package com.android.quickstep.views;
 
 import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.CENTER_HORIZONTAL;
 
 import static com.android.launcher3.Utilities.prefixTextWithIcon;
 import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
@@ -27,6 +29,8 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.AppUsageLimit;
+import android.graphics.Outline;
+import android.graphics.Paint;
 import android.icu.text.MeasureFormat;
 import android.icu.text.MeasureFormat.FormatWidth;
 import android.icu.util.Measure;
@@ -35,6 +39,9 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import androidx.annotation.StringRes;
@@ -42,7 +49,7 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.Utilities;
 import com.android.systemui.shared.recents.model.Task;
 
 import java.time.Duration;
@@ -62,6 +69,10 @@
     private Task mTask;
     private boolean mHasLimit;
     private long mAppRemainingTimeMs;
+    private View mBanner;
+    private ViewOutlineProvider mOldBannerOutlineProvider;
+    private float mBannerOffsetPercentage;
+    private float mVerticalOffset = 0f;
 
     public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) {
         mActivity = activity;
@@ -69,18 +80,10 @@
         mLauncherApps = activity.getSystemService(LauncherApps.class);
     }
 
-    private void setTaskFooter(View view) {
-        View oldFooter = mTaskView.setFooter(TaskView.INDEX_DIGITAL_WELLBEING_TOAST, view);
-        if (oldFooter != null) {
-            oldFooter.setOnClickListener(null);
-            mActivity.getViewCache().recycleView(R.layout.digital_wellbeing_toast, oldFooter);
-        }
-    }
-
     private void setNoLimit() {
         mHasLimit = false;
         mTaskView.setContentDescription(mTask.titleDescription);
-        setTaskFooter(null);
+        replaceBanner(null);
         mAppRemainingTimeMs = 0;
     }
 
@@ -91,14 +94,10 @@
                 mActivity, mTaskView);
         toast.setText(prefixTextWithIcon(mActivity, R.drawable.ic_hourglass_top, getText()));
         toast.setOnClickListener(this::openAppUsageSettings);
-        setTaskFooter(toast);
+        replaceBanner(toast);
 
         mTaskView.setContentDescription(
                 getContentDescriptionForTask(mTask, appUsageLimitTimeMs, appRemainingTimeMs));
-        RecentsView rv = mTaskView.getRecentsView();
-        if (rv != null) {
-            rv.onDigitalWellbeingToastShown();
-        }
     }
 
     public String getText() {
@@ -217,8 +216,8 @@
                     view, 0, 0,
                     view.getWidth(), view.getHeight());
             activity.startActivity(intent, options.toBundle());
-            activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
-                    LauncherLogProto.ControlType.APP_USAGE_SETTINGS, view);
+
+            // TODO: add WW logging on the app usage settings click.
         } catch (ActivityNotFoundException e) {
             Log.e(TAG, "Failed to open app usage settings for task "
                     + mTask.getTopComponent().getPackageName(), e);
@@ -234,4 +233,70 @@
                         getText(appRemainingTimeMs)) :
                 task.titleDescription;
     }
+
+    private void replaceBanner(View view) {
+        resetOldBanner();
+        setBanner(view);
+    }
+
+    private void resetOldBanner() {
+        if (mBanner != null) {
+            mBanner.setOutlineProvider(mOldBannerOutlineProvider);
+            mTaskView.removeView(mBanner);
+            mBanner.setOnClickListener(null);
+            mActivity.getViewCache().recycleView(R.layout.digital_wellbeing_toast, mBanner);
+        }
+    }
+
+    private void setBanner(View view) {
+        mBanner = view;
+        if (view != null) {
+            setupAndAddBanner();
+            setBannerOutline();
+        }
+    }
+
+    private void setupAndAddBanner() {
+        FrameLayout.LayoutParams layoutParams =
+                (FrameLayout.LayoutParams) mBanner.getLayoutParams();
+        layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+        layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams)
+                mTaskView.getThumbnail().getLayoutParams()).bottomMargin;
+        mBanner.setTranslationY(mBannerOffsetPercentage * mBanner.getHeight());
+        mTaskView.addView(mBanner);
+    }
+
+    private void setBannerOutline() {
+        mOldBannerOutlineProvider = mBanner.getOutlineProvider();
+        mBanner.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                mOldBannerOutlineProvider.getOutline(view, outline);
+                outline.offset(0, Math.round(-view.getTranslationY() + mVerticalOffset));
+            }
+        });
+        mBanner.setClipToOutline(true);
+    }
+
+    void updateBannerOffset(float offsetPercentage, float verticalOffset) {
+        if (mBanner != null && mBannerOffsetPercentage != offsetPercentage) {
+            mVerticalOffset = verticalOffset;
+            mBannerOffsetPercentage = offsetPercentage;
+            mBanner.setTranslationY(offsetPercentage * mBanner.getHeight() + mVerticalOffset);
+            mBanner.invalidateOutline();
+        }
+    }
+
+    void setBannerColorTint(int color, float amount) {
+        if (mBanner == null) {
+            return;
+        }
+        if (amount == 0) {
+            mBanner.setLayerType(View.LAYER_TYPE_NONE, null);
+        }
+        Paint layerPaint = new Paint();
+        layerPaint.setColorFilter(Utilities.makeColorTintingColorFilter(color, amount));
+        mBanner.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint);
+        mBanner.setLayerPaint(layerPaint);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
new file mode 100644
index 0000000..1548268
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
@@ -0,0 +1,197 @@
+/*
+ * 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.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Outline;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.RemoteViews.RemoteViewOutlineProvider;
+
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.RoundedCornerEnforcement;
+
+import java.util.stream.IntStream;
+
+/**
+ * Mimics the appearance of the background view of a {@link LauncherAppWidgetHostView} through a
+ * an App Widget activity launch animation.
+ */
+@TargetApi(Build.VERSION_CODES.S)
+final class FloatingWidgetBackgroundView extends View {
+    private final ColorDrawable mFallbackDrawable = new ColorDrawable();
+    private final DrawableProperties mForegroundProperties = new DrawableProperties();
+    private final DrawableProperties mBackgroundProperties = new DrawableProperties();
+
+    private Drawable mOriginalForeground;
+    private Drawable mOriginalBackground;
+    private float mFinalRadius;
+    private float mInitialOutlineRadius;
+    private float mOutlineRadius;
+    private boolean mIsUsingFallback;
+    private View mSourceView;
+
+    FloatingWidgetBackgroundView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
+            }
+        });
+        setClipToOutline(true);
+    }
+
+    void init(LauncherAppWidgetHostView hostView, View backgroundView, float finalRadius,
+            int fallbackBackgroundColor) {
+        mFinalRadius = finalRadius;
+        mSourceView = backgroundView;
+        mInitialOutlineRadius = getOutlineRadius(hostView, backgroundView);
+        mIsUsingFallback = false;
+        if (isSupportedDrawable(backgroundView.getForeground())) {
+            mOriginalForeground = backgroundView.getForeground();
+            mForegroundProperties.init(
+                    mOriginalForeground.getConstantState().newDrawable().mutate());
+            setForeground(mForegroundProperties.mDrawable);
+            Drawable clipPlaceholder =
+                    mOriginalForeground.getConstantState().newDrawable().mutate();
+            clipPlaceholder.setAlpha(0);
+            mSourceView.setForeground(clipPlaceholder);
+        }
+        if (isSupportedDrawable(backgroundView.getBackground())) {
+            mOriginalBackground = backgroundView.getBackground();
+            mBackgroundProperties.init(
+                    mOriginalBackground.getConstantState().newDrawable().mutate());
+            setBackground(mBackgroundProperties.mDrawable);
+            Drawable clipPlaceholder =
+                    mOriginalBackground.getConstantState().newDrawable().mutate();
+            clipPlaceholder.setAlpha(0);
+            mSourceView.setBackground(clipPlaceholder);
+        } else if (mOriginalForeground == null) {
+            mFallbackDrawable.setColor(fallbackBackgroundColor);
+            setBackground(mFallbackDrawable);
+            mIsUsingFallback = true;
+        }
+    }
+
+    /** Update the animated properties of the drawables. */
+    void update(float cornerRadiusProgress, float fallbackAlpha) {
+        if (isUninitialized()) return;
+        mOutlineRadius = mInitialOutlineRadius + (mFinalRadius - mInitialOutlineRadius)
+                * cornerRadiusProgress;
+        mForegroundProperties.updateDrawable(mFinalRadius, cornerRadiusProgress);
+        mBackgroundProperties.updateDrawable(mFinalRadius, cornerRadiusProgress);
+        setAlpha(mIsUsingFallback ? fallbackAlpha : 1f);
+    }
+
+    /** Restores the drawables to the source view. */
+    void finish() {
+        if (isUninitialized()) return;
+        if (mOriginalForeground != null) mSourceView.setForeground(mOriginalForeground);
+        if (mOriginalBackground != null) mSourceView.setBackground(mOriginalBackground);
+    }
+
+    void recycle() {
+        mSourceView = null;
+        mOriginalForeground = null;
+        mOriginalBackground = null;
+        mOutlineRadius = 0;
+        mFinalRadius = 0;
+        setForeground(null);
+        setBackground(null);
+    }
+
+    /** Get the largest of drawable corner radii or background view outline radius. */
+    float getMaximumRadius() {
+        if (isUninitialized()) return 0;
+        return Math.max(mInitialOutlineRadius, Math.max(getMaxRadius(mOriginalForeground),
+                getMaxRadius(mOriginalBackground)));
+    }
+
+    private boolean isUninitialized() {
+        return mSourceView == null;
+    }
+
+    /** Returns the maximum corner radius of {@param drawable}. */
+    private static float getMaxRadius(Drawable drawable) {
+        if (!(drawable instanceof GradientDrawable)) return 0;
+        float[] cornerRadii = ((GradientDrawable) drawable).getCornerRadii();
+        float cornerRadius = ((GradientDrawable) drawable).getCornerRadius();
+        double radiiMax = cornerRadii == null ? 0 : IntStream.range(0, cornerRadii.length)
+                .mapToDouble(i -> cornerRadii[i]).max().orElse(0);
+        return Math.max(cornerRadius, (float) radiiMax);
+    }
+
+    /** Returns whether the given drawable type is supported. */
+    private static boolean isSupportedDrawable(Drawable drawable) {
+        return drawable instanceof ColorDrawable || (drawable instanceof GradientDrawable
+                && ((GradientDrawable) drawable).getShape() == GradientDrawable.RECTANGLE);
+    }
+
+    /** Corner radius from source view's outline, or enforced view. */
+    private static float getOutlineRadius(LauncherAppWidgetHostView hostView, View v) {
+        if (RoundedCornerEnforcement.isRoundedCornerEnabled()
+                && hostView.hasEnforcedCornerRadius()) {
+            return hostView.getEnforcedCornerRadius();
+        } else if (v.getOutlineProvider() instanceof RemoteViewOutlineProvider
+                && v.getClipToOutline()) {
+            return ((RemoteViewOutlineProvider) v.getOutlineProvider()).getRadius();
+        }
+        return 0;
+    }
+
+    /** Stores and modifies a drawable's properties through an animation. */
+    private static class DrawableProperties {
+        private Drawable mDrawable;
+        private float mOriginalRadius;
+        private float[] mOriginalRadii;
+        private final float[] mTmpRadii = new float[8];
+
+        /** Store a drawable's animated properties. */
+        void init(Drawable drawable) {
+            mDrawable = drawable;
+            if (!(drawable instanceof GradientDrawable)) return;
+            mOriginalRadius = ((GradientDrawable) drawable).getCornerRadius();
+            mOriginalRadii = ((GradientDrawable) drawable).getCornerRadii();
+        }
+
+        /**
+         * Update the drawable for the given animation state.
+         *
+         * @param finalRadius the radius of each corner when {@param progress} is 1
+         * @param progress    the linear progress of the corner radius from its original value to
+         *                    {@param finalRadius}
+         */
+        void updateDrawable(float finalRadius, float progress) {
+            if (!(mDrawable instanceof GradientDrawable)) return;
+            GradientDrawable d = (GradientDrawable) mDrawable;
+            if (mOriginalRadii != null) {
+                for (int i = 0; i < mOriginalRadii.length; i++) {
+                    mTmpRadii[i] = mOriginalRadii[i] + (finalRadius - mOriginalRadii[i]) * progress;
+                }
+                d.setCornerRadii(mTmpRadii);
+            } else {
+                d.setCornerRadius(mOriginalRadius + (finalRadius - mOriginalRadius) * progress);
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
new file mode 100644
index 0000000..88b11a0
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -0,0 +1,317 @@
+/*
+ * 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.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Size;
+import android.view.GhostView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.FloatingView;
+import com.android.launcher3.views.ListenerView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.RoundedCornerEnforcement;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/** A view that mimics an App Widget through a launch animation. */
+@TargetApi(Build.VERSION_CODES.S)
+public class FloatingWidgetView extends FrameLayout implements AnimatorListener,
+        OnGlobalLayoutListener, FloatingView {
+    private static final Matrix sTmpMatrix = new Matrix();
+
+    private final Launcher mLauncher;
+    private final ListenerView mListenerView;
+    private final FloatingWidgetBackgroundView mBackgroundView;
+    private final RectF mBackgroundOffset = new RectF();
+
+    private LauncherAppWidgetHostView mAppWidgetView;
+    private View mAppWidgetBackgroundView;
+    private RectF mBackgroundPosition;
+    private GhostView mForegroundOverlayView;
+
+    private Runnable mEndRunnable;
+    private Runnable mFastFinishRunnable;
+    private Runnable mOnTargetChangeRunnable;
+    private boolean mAppTargetIsTranslucent;
+
+    private float mIconOffsetY;
+
+    public FloatingWidgetView(Context context) {
+        this(context, null);
+    }
+
+    public FloatingWidgetView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FloatingWidgetView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+        mListenerView = new ListenerView(context, attrs);
+        mBackgroundView = new FloatingWidgetBackgroundView(context, attrs, defStyleAttr);
+        addView(mBackgroundView);
+        setWillNotDraw(false);
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animator) {
+        Runnable endRunnable = mEndRunnable;
+        mEndRunnable = null;
+        if (endRunnable != null) {
+            endRunnable.run();
+        }
+    }
+
+    @Override
+    public void onAnimationStart(Animator animator) {
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animator) {
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animator) {
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getViewTreeObserver().addOnGlobalLayoutListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        getViewTreeObserver().removeOnGlobalLayoutListener(this);
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    public void onGlobalLayout() {
+        if (isUninitialized()) return;
+        positionViews();
+        if (mOnTargetChangeRunnable != null) {
+            mOnTargetChangeRunnable.run();
+        }
+    }
+
+    /** Sets a runnable that is called on global layout change. */
+    public void setOnTargetChangeListener(Runnable onTargetChangeListener) {
+        mOnTargetChangeRunnable = onTargetChangeListener;
+    }
+
+    /** Sets a runnable that is called after a call to {@link #fastFinish()}. */
+    public void setFastFinishRunnable(Runnable runnable) {
+        mFastFinishRunnable = runnable;
+    }
+
+    /** Callback at the end or early exit of the animation. */
+    @Override
+    public void fastFinish() {
+        if (isUninitialized()) return;
+        Runnable fastFinishRunnable = mFastFinishRunnable;
+        if (fastFinishRunnable != null) {
+            fastFinishRunnable.run();
+        }
+        Runnable endRunnable = mEndRunnable;
+        mEndRunnable = null;
+        if (endRunnable != null) {
+            endRunnable.run();
+        }
+    }
+
+    private void init(DragLayer dragLayer, LauncherAppWidgetHostView originalView,
+            RectF widgetBackgroundPosition, Size windowSize, float windowCornerRadius,
+            boolean appTargetIsTranslucent, int fallbackBackgroundColor) {
+        mAppWidgetView = originalView;
+        // Deferrals must begin before GhostView is created. See b/190818220
+        mAppWidgetView.beginDeferringUpdates();
+        mBackgroundPosition = widgetBackgroundPosition;
+        mAppTargetIsTranslucent = appTargetIsTranslucent;
+        mEndRunnable = () -> finish(dragLayer);
+
+        mAppWidgetBackgroundView = RoundedCornerEnforcement.findBackground(mAppWidgetView);
+        if (mAppWidgetBackgroundView == null) {
+            mAppWidgetBackgroundView = mAppWidgetView;
+        }
+
+        getRelativePosition(mAppWidgetBackgroundView, dragLayer, mBackgroundPosition);
+        getRelativePosition(mAppWidgetBackgroundView, mAppWidgetView, mBackgroundOffset);
+        if (!mAppTargetIsTranslucent) {
+            mBackgroundView.init(mAppWidgetView, mAppWidgetBackgroundView, windowCornerRadius,
+                    fallbackBackgroundColor);
+            // Layout call before GhostView creation so that the overlaid view isn't clipped
+            layout(0, 0, windowSize.getWidth(), windowSize.getHeight());
+            mForegroundOverlayView = GhostView.addGhost(mAppWidgetView, this);
+            positionViews();
+        }
+
+        mListenerView.setListener(this::fastFinish);
+        dragLayer.addView(mListenerView);
+    }
+
+    /**
+     * Updates the position and opacity of the floating widget's components.
+     *
+     * @param backgroundPosition      the new position of the widget's background relative to the
+     *                                {@link FloatingWidgetView}'s parent
+     * @param floatingWidgetAlpha     the overall opacity of the {@link FloatingWidgetView}
+     * @param foregroundAlpha         the opacity of the foreground layer
+     * @param fallbackBackgroundAlpha the opacity of the fallback background used when the App
+     *                                Widget doesn't have a background
+     * @param cornerRadiusProgress    progress of the corner radius animation, where 0 is the
+     *                                original radius and 1 is the window radius
+     */
+    public void update(RectF backgroundPosition, float floatingWidgetAlpha, float foregroundAlpha,
+            float fallbackBackgroundAlpha, float cornerRadiusProgress) {
+        if (isUninitialized() || mAppTargetIsTranslucent) return;
+        setAlpha(floatingWidgetAlpha);
+        mBackgroundView.update(cornerRadiusProgress, fallbackBackgroundAlpha);
+        mAppWidgetView.setAlpha(foregroundAlpha);
+        mBackgroundPosition = backgroundPosition;
+        positionViews();
+    }
+
+    @Override
+    public void setPositionOffsetY(float y) {
+        mIconOffsetY = y;
+        onGlobalLayout();
+    }
+
+    /** Sets the layout parameters of the floating view and its background view child. */
+    private void positionViews() {
+        LayoutParams layoutParams = (LayoutParams) getLayoutParams();
+        layoutParams.setMargins(0, 0, 0, 0);
+        setLayoutParams(layoutParams);
+
+        // FloatingWidgetView layout is forced LTR
+        mBackgroundView.setTranslationX(mBackgroundPosition.left);
+        mBackgroundView.setTranslationY(mBackgroundPosition.top + mIconOffsetY);
+        LayoutParams backgroundParams = (LayoutParams) mBackgroundView.getLayoutParams();
+        backgroundParams.leftMargin = 0;
+        backgroundParams.topMargin = 0;
+        backgroundParams.width = (int) mBackgroundPosition.width();
+        backgroundParams.height = (int) mBackgroundPosition.height();
+        mBackgroundView.setLayoutParams(backgroundParams);
+
+        if (mForegroundOverlayView != null) {
+            sTmpMatrix.reset();
+            float foregroundScale =
+                    mBackgroundPosition.width() / mAppWidgetBackgroundView.getWidth();
+            sTmpMatrix.setTranslate(-mBackgroundOffset.left - mAppWidgetView.getLeft(),
+                    -mBackgroundOffset.top - mAppWidgetView.getTop());
+            sTmpMatrix.postScale(foregroundScale, foregroundScale);
+            sTmpMatrix.postTranslate(mBackgroundPosition.left, mBackgroundPosition.top
+                    + mIconOffsetY);
+            mForegroundOverlayView.setMatrix(sTmpMatrix);
+        }
+    }
+
+    private void finish(DragLayer dragLayer) {
+        mAppWidgetView.setAlpha(1f);
+        GhostView.removeGhost(mAppWidgetView);
+        ((ViewGroup) dragLayer.getParent()).removeView(this);
+        dragLayer.removeView(mListenerView);
+        mBackgroundView.finish();
+        // Removing GhostView must occur before ending deferrals. See b/190818220
+        mAppWidgetView.endDeferringUpdates();
+        recycle();
+        mLauncher.getViewCache().recycleView(R.layout.floating_widget_view, this);
+    }
+
+    public float getInitialCornerRadius() {
+        return mBackgroundView.getMaximumRadius();
+    }
+
+    private boolean isUninitialized() {
+        return mForegroundOverlayView == null;
+    }
+
+    private void recycle() {
+        mIconOffsetY = 0;
+        mEndRunnable = null;
+        mFastFinishRunnable = null;
+        mOnTargetChangeRunnable = null;
+        mBackgroundPosition = null;
+        mListenerView.setListener(null);
+        mAppWidgetView = null;
+        mForegroundOverlayView = null;
+        mAppWidgetBackgroundView = null;
+        mBackgroundView.recycle();
+    }
+
+    /**
+     * Configures and returns a an instance of {@link FloatingWidgetView} matching the appearance of
+     * {@param originalView}.
+     *
+     * @param widgetBackgroundPosition a {@link RectF} that will be updated with the widget's
+     *                                 background bounds
+     * @param windowSize               the size of the window when launched
+     * @param windowCornerRadius       the corner radius of the window
+     */
+    public static FloatingWidgetView getFloatingWidgetView(Launcher launcher,
+            LauncherAppWidgetHostView originalView, RectF widgetBackgroundPosition,
+            Size windowSize, float windowCornerRadius, boolean appTargetsAreTranslucent,
+            int fallbackBackgroundColor) {
+        final DragLayer dragLayer = launcher.getDragLayer();
+        ViewGroup parent = (ViewGroup) dragLayer.getParent();
+        FloatingWidgetView floatingView =
+                launcher.getViewCache().getView(R.layout.floating_widget_view, launcher, parent);
+        floatingView.recycle();
+
+        floatingView.init(dragLayer, originalView, widgetBackgroundPosition, windowSize,
+                windowCornerRadius, appTargetsAreTranslucent, fallbackBackgroundColor);
+        parent.addView(floatingView);
+        return floatingView;
+    }
+
+    /**
+     * Extract a background color from a target's task description, or fall back to the given
+     * context's theme background color.
+     */
+    public static int getDefaultBackgroundColor(
+            Context context, RemoteAnimationTargetCompat target) {
+        return (target != null && target.taskInfo.taskDescription != null)
+                ? target.taskInfo.taskDescription.getBackgroundColor()
+                : Themes.getColorBackground(context);
+    }
+
+    private static void getRelativePosition(View descendant, View ancestor, RectF position) {
+        float[] points = new float[]{0, 0, descendant.getWidth(), descendant.getHeight()};
+        Utilities.getDescendantCoordRelativeToAncestor(descendant, ancestor, points,
+                false /* includeRootScroll */, true /* ignoreTransform */);
+        position.set(
+                Math.min(points[0], points[2]),
+                Math.min(points[1], points[3]),
+                Math.max(points[0], points[2]),
+                Math.max(points[1], points[3]));
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java b/quickstep/src/com/android/quickstep/views/IconView.java
similarity index 70%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
rename to quickstep/src/com/android/quickstep/views/IconView.java
index 7cc00b7..5b0ade0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
+++ b/quickstep/src/com/android/quickstep/views/IconView.java
@@ -21,11 +21,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.FastBitmapDrawable;
-
-import java.util.ArrayList;
+import com.android.launcher3.Utilities;
 
 /**
  * A view which draws a drawable stretched to fit its size. Unlike ImageView, it avoids relayout
@@ -33,14 +29,8 @@
  */
 public class IconView extends View {
 
-    public interface OnScaleUpdateListener {
-        public void onScaleUpdate(float scale);
-    }
-
     private Drawable mDrawable;
 
-    private ArrayList<OnScaleUpdateListener> mScaleListeners;
-
     public IconView(Context context) {
         super(context);
     }
@@ -94,16 +84,6 @@
     }
 
     @Override
-    public void invalidateDrawable(@NonNull Drawable drawable) {
-        super.invalidateDrawable(drawable);
-        if (drawable instanceof FastBitmapDrawable && mScaleListeners != null) {
-            for (OnScaleUpdateListener listener : mScaleListeners) {
-                listener.onScaleUpdate(((FastBitmapDrawable) drawable).getScale());
-            }
-        }
-    }
-
-    @Override
     protected void onDraw(Canvas canvas) {
         if (mDrawable != null) {
             mDrawable.draw(canvas);
@@ -115,22 +95,6 @@
         return false;
     }
 
-    public void addUpdateScaleListener(OnScaleUpdateListener listener) {
-        if (mScaleListeners == null) {
-            mScaleListeners = new ArrayList<>();
-        }
-        mScaleListeners.add(listener);
-        if (mDrawable instanceof FastBitmapDrawable) {
-            listener.onScaleUpdate(((FastBitmapDrawable) mDrawable).getScale());
-        }
-    }
-
-    public void removeUpdateScaleListener(OnScaleUpdateListener listener) {
-        if (mScaleListeners != null) {
-            mScaleListeners.remove(listener);
-        }
-    }
-
     @Override
     public void setAlpha(float alpha) {
         super.setAlpha(alpha);
@@ -140,4 +104,16 @@
             setVisibility(INVISIBLE);
         }
     }
+
+    /**
+     * Set the tint color of the icon, useful for scrimming or dimming.
+     *
+     * @param color to blend in.
+     * @param amount [0,1] 0 no tint, 1 full tint
+     */
+    public void setIconColorTint(int color, float amount) {
+        if (mDrawable != null) {
+            mDrawable.setColorFilter(Utilities.makeColorTintingColorFilter(color, amount));
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
new file mode 100644
index 0000000..65956d5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views;
+
+import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
+
+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 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;
+
+/**
+ * {@link RecentsView} used in Launcher activity
+ */
+@TargetApi(Build.VERSION_CODES.O)
+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) {
+        this(context, attrs, 0);
+    }
+
+    public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
+        mActivity.getStateManager().addStateListener(this);
+    }
+
+    @Override
+    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+        super.init(actionsView, splitPlaceholderView);
+        setContentAlpha(0);
+    }
+
+    @Override
+    public void startHome() {
+        mActivity.getStateManager().goToState(NORMAL);
+    }
+
+    @Override
+    protected void onTaskLaunchAnimationEnd(boolean success) {
+        if (success) {
+            mActivity.getStateManager().moveToRestState();
+        } else {
+            LauncherState state = mActivity.getStateManager().getState();
+            mActivity.getAllAppsController().setState(state);
+        }
+        super.onTaskLaunchAnimationEnd(success);
+    }
+
+    @Override
+    public void reset() {
+        super.reset();
+
+        setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
+    }
+
+    @Override
+    public void onStateTransitionStart(LauncherState toState) {
+        setOverviewStateEnabled(toState.overviewUi);
+        setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
+        setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
+        setFreezeViewVisibility(true);
+    }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        if (finalState == NORMAL || finalState == SPRING_LOADED) {
+            // Clean-up logic that occurs when recents is no longer in use/visible.
+            reset();
+        }
+        setOverlayEnabled(finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK);
+        setFreezeViewVisibility(false);
+    }
+
+    @Override
+    public void setOverviewStateEnabled(boolean enabled) {
+        super.setOverviewStateEnabled(enabled);
+        if (enabled) {
+            LauncherState state = mActivity.getStateManager().getState();
+            boolean hasClearAllButton = (state.getVisibleElements(mActivity)
+                    & CLEAR_ALL_BUTTON) != 0;
+            setDisallowScrollToClearAll(!hasClearAllButton);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean result = super.onTouchEvent(ev);
+        // Do not let touch escape to siblings below this view.
+        return result || mActivity.getStateManager().getState().overviewUi;
+    }
+
+    @Override
+    protected void 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();
+    }
+
+    @Override
+    public void setModalStateEnabled(boolean isModalState) {
+        super.setModalStateEnabled(isModalState);
+        if (isModalState) {
+            mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK);
+        } else {
+            if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
+                mActivity.getStateManager().goToState(LauncherState.OVERVIEW);
+            }
+        }
+    }
+
+    @Override
+    protected void onDismissAnimationEnds() {
+        super.onDismissAnimationEnds();
+        if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
+            // We want to keep the tasks translations in this temporary state
+            // after resetting the rest above
+            setTaskViewsPrimarySplitTranslation(mTaskViewsPrimarySplitTranslation);
+            setTaskViewsSecondarySplitTranslation(mTaskViewsSecondarySplitTranslation);
+        }
+    }
+
+    @Override
+    public void initiateSplitSelect(TaskView taskView,
+            SplitConfigurationOptions.SplitPositionOption splitPositionOption) {
+        super.initiateSplitSelect(taskView, splitPositionOption);
+        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/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
similarity index 65%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
rename to quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 1bf2fbf..8c115e5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -16,9 +16,8 @@
 
 package com.android.quickstep.views;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SHARE;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -31,8 +30,10 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
+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;
@@ -52,21 +53,15 @@
     private final Rect mInsets = new Rect();
 
     @IntDef(flag = true, value = {
-            HIDDEN_UNSUPPORTED_NAVIGATION,
-            HIDDEN_DISABLED_FEATURE,
             HIDDEN_NON_ZERO_ROTATION,
             HIDDEN_NO_TASKS,
-            HIDDEN_GESTURE_RUNNING,
             HIDDEN_NO_RECENTS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionsHiddenFlags { }
 
-    public static final int HIDDEN_UNSUPPORTED_NAVIGATION = 1 << 0;
-    public static final int HIDDEN_DISABLED_FEATURE = 1 << 1;
-    public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 2;
-    public static final int HIDDEN_NO_TASKS = 1 << 3;
-    public static final int HIDDEN_GESTURE_RUNNING = 1 << 4;
-    public static final int HIDDEN_NO_RECENTS = 1 << 5;
+    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;
 
     @IntDef(flag = true, value = {
             DISABLED_SCROLLING,
@@ -83,6 +78,7 @@
     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;
 
@@ -94,6 +90,11 @@
 
     protected T mCallbacks;
 
+    private float mModalness;
+    private float mModalTransformY;
+
+    protected DeviceProfile mDp;
+
     public OverviewActionsView(Context context) {
         this(context, null);
     }
@@ -104,7 +105,7 @@
 
     public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr, 0);
-        mMultiValueAlpha = new MultiValueAlpha(this, 4);
+        mMultiValueAlpha = new MultiValueAlpha(this, 5);
         mMultiValueAlpha.setUpdateVisibility(true);
     }
 
@@ -116,7 +117,7 @@
         findViewById(R.id.action_screenshot).setOnClickListener(this);
         if (ENABLE_OVERVIEW_SHARE.get()) {
             share.setVisibility(VISIBLE);
-            findViewById(R.id.share_space).setVisibility(VISIBLE);
+            findViewById(R.id.oav_three_button_space).setVisibility(VISIBLE);
         }
     }
 
@@ -143,13 +144,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        updateHiddenFlags(HIDDEN_DISABLED_FEATURE, !ENABLE_OVERVIEW_ACTIONS.get());
-        updateHiddenFlags(HIDDEN_UNSUPPORTED_NAVIGATION, !removeShelfFromOverview(getContext()));
-    }
-
-    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
@@ -159,6 +153,7 @@
     public void setInsets(Rect insets) {
         mInsets.set(insets);
         updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
+        updateHorizontalPadding();
     }
 
     public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) {
@@ -202,22 +197,86 @@
         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);
+    }
+
     /** Updates vertical margins for different navigation mode or configuration changes. */
     public void updateVerticalMargin(Mode mode) {
-        int bottomMargin;
-        int orientation = getResources().getConfiguration().orientation;
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            bottomMargin = 0;
-        } else if (mode == Mode.THREE_BUTTONS) {
-            bottomMargin = getResources()
-                    .getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_three_button);
-        } else {
-            bottomMargin = getResources()
-                    .getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_gesture);
+        if (mDp == null) {
+            return;
         }
-        bottomMargin += mInsets.bottom;
-        LayoutParams params = (LayoutParams) getLayoutParams();
-        params.setMargins(
-                params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
+        LayoutParams actionParams = (LayoutParams) findViewById(
+                R.id.action_buttons).getLayoutParams();
+        actionParams.setMargins(
+                actionParams.leftMargin, getOverviewActionsTopMarginPx(mode, mDp),
+                actionParams.rightMargin, getOverviewActionsBottomMarginPx(mode, mDp));
+    }
+
+    /**
+     * Set the device profile for this view to draw with.
+     */
+    public void setDp(DeviceProfile dp) {
+        mDp = dp;
+        updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
+        requestLayout();
+    }
+
+    /**
+     * The current task is fully modal (modalness = 1) when it is shown on its own in a modal
+     * way. Modalness 0 means the task is shown in context with all the other tasks.
+     */
+    public void setTaskModalness(float modalness) {
+        mModalness = modalness;
+        applyTranslationY();
+    }
+
+    public void 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);
+    }
+
+    /** Get the top margin associated with the action buttons in Overview. */
+    public static int getOverviewActionsTopMarginPx(
+            SysUINavigationMode.Mode mode, DeviceProfile dp) {
+        // In vertical bar, use the smaller task margin for the top regardless of mode
+        if (dp.isVerticalBarLayout()) {
+            return dp.overviewTaskMarginPx;
+        }
+
+        if (mode == SysUINavigationMode.Mode.THREE_BUTTONS) {
+            return dp.overviewActionsMarginThreeButtonPx;
+        }
+
+        return dp.overviewActionsMarginGesturePx;
+    }
+
+    /** Get the bottom margin associated with the action buttons in Overview. */
+    public static int getOverviewActionsBottomMarginPx(
+            SysUINavigationMode.Mode mode, DeviceProfile dp) {
+        int inset = dp.getInsets().bottom;
+
+        if (dp.isVerticalBarLayout()) {
+            return inset;
+        }
+
+        if (mode == SysUINavigationMode.Mode.THREE_BUTTONS) {
+            return dp.overviewActionsMarginThreeButtonPx + inset;
+        }
+
+        return dp.overviewActionsMarginGesturePx + inset;
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsExtraViewContainer.java
similarity index 93%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java
rename to quickstep/src/com/android/quickstep/views/RecentsExtraViewContainer.java
index 1ea6d4a..16bc3bc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsExtraViewContainer.java
@@ -23,7 +23,7 @@
 /**
  * Empty view to house recents overview extra card
  */
-public class RecentsExtraViewContainer extends FrameLayout implements RecentsView.PageCallbacks {
+public class RecentsExtraViewContainer extends FrameLayout {
 
     private boolean mScrollable = false;
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
new file mode 100644
index 0000000..ce79125
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -0,0 +1,3942 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
+import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
+import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.mapToRange;
+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.LINEAR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
+import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
+import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.quickstep.views.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 android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.LayoutTransition;
+import android.animation.LayoutTransition.TransitionListener;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.content.LocusId;
+import android.content.res.Configuration;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+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.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.SparseBooleanArray;
+import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnScrollChangedListener;
+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 androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.core.graphics.ColorUtils;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.SpringProperty;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.DynamicResource;
+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.Themes;
+import com.android.launcher3.util.TranslateEdgeEffect;
+import com.android.launcher3.util.ViewPool;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.GestureState;
+import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
+import com.android.quickstep.RemoteAnimationTargets;
+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.LayoutUtils;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.SplitScreenBounds;
+import com.android.quickstep.util.SplitSelectStateController;
+import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.TransformParams;
+import com.android.systemui.plugins.ResourceProvider;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.pip.IPipAnimationListener;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A list of recent tasks.
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
+        STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
+        TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
+        TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
+
+    // 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;
+
+    public static final FloatProperty<RecentsView> CONTENT_ALPHA =
+            new FloatProperty<RecentsView>("contentAlpha") {
+                @Override
+                public void setValue(RecentsView view, float v) {
+                    view.setContentAlpha(v);
+                }
+
+                @Override
+                public Float get(RecentsView view) {
+                    return view.getContentAlpha();
+                }
+            };
+
+    public static final FloatProperty<RecentsView> FULLSCREEN_PROGRESS =
+            new FloatProperty<RecentsView>("fullscreenProgress") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setFullscreenProgress(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mFullscreenProgress;
+                }
+            };
+
+    public static final FloatProperty<RecentsView> TASK_MODALNESS =
+            new FloatProperty<RecentsView>("taskModalness") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setTaskModalness(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mTaskModalness;
+                }
+            };
+
+    public static final FloatProperty<RecentsView> ADJACENT_PAGE_HORIZONTAL_OFFSET =
+            new FloatProperty<RecentsView>("adjacentPageHorizontalOffset") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    if (recentsView.mAdjacentPageHorizontalOffset != v) {
+                        recentsView.mAdjacentPageHorizontalOffset = v;
+                        recentsView.updatePageOffsets();
+                    }
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mAdjacentPageHorizontalOffset;
+                }
+            };
+
+    /**
+     * 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.
+     * TODO(b/187528071): Remove this and replace with a real scrim.
+     */
+    private static final FloatProperty<RecentsView> COLOR_TINT =
+            new FloatProperty<RecentsView>("colorTint") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setColorTint(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.getColorTint();
+                }
+            };
+
+    /**
+     * Even though {@link TaskView} has distinct offsetTranslationX/Y and resistance property, they
+     * are currently both used to apply secondary translation. Should their use cases change to be
+     * more specific, we'd want to create a similar FloatProperty just for a TaskView's
+     * offsetX/Y property
+     */
+    public static final FloatProperty<RecentsView> TASK_SECONDARY_TRANSLATION =
+            new FloatProperty<RecentsView>("taskSecondaryTranslation") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setTaskViewsResistanceTranslation(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mTaskViewsSecondaryTranslation;
+                }
+            };
+
+    /**
+     * Even though {@link TaskView} has distinct offsetTranslationX/Y and resistance property, they
+     * are currently both used to apply secondary translation. Should their use cases change to be
+     * more specific, we'd want to create a similar FloatProperty just for a TaskView's
+     * offsetX/Y property
+     */
+    public static final FloatProperty<RecentsView> TASK_PRIMARY_SPLIT_TRANSLATION =
+            new FloatProperty<RecentsView>("taskPrimarySplitTranslation") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setTaskViewsPrimarySplitTranslation(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mTaskViewsPrimarySplitTranslation;
+                }
+            };
+
+    public static final FloatProperty<RecentsView> TASK_SECONDARY_SPLIT_TRANSLATION =
+            new FloatProperty<RecentsView>("taskSecondarySplitTranslation") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setTaskViewsSecondarySplitTranslation(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mTaskViewsSecondarySplitTranslation;
+                }
+            };
+
+    /** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */
+    public static final FloatProperty<RecentsView> RECENTS_SCALE_PROPERTY =
+            new FloatProperty<RecentsView>("recentsScale") {
+                @Override
+                public void setValue(RecentsView view, float scale) {
+                    view.setScaleX(scale);
+                    view.setScaleY(scale);
+                    view.mLastComputedTaskStartPushOutDistance = null;
+                    view.mLastComputedTaskEndPushOutDistance = null;
+                    view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
+                    view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
+                    view.updatePageOffsets();
+                }
+
+                @Override
+                public Float get(RecentsView view) {
+                    return view.getScaleX();
+                }
+            };
+
+    public static final FloatProperty<RecentsView> RECENTS_GRID_PROGRESS =
+            new FloatProperty<RecentsView>("recentsGrid") {
+                @Override
+                public void setValue(RecentsView view, float gridProgress) {
+                    view.setGridProgress(gridProgress);
+                }
+
+                @Override
+                public Float get(RecentsView view) {
+                    return view.mGridProgress;
+                }
+            };
+
+    // OverScroll constants
+    private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
+
+    private static final int DISMISS_TASK_DURATION = 300;
+    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;
+
+    protected final RecentsOrientedState mOrientationState;
+    protected final BaseActivityInterface<STATE_TYPE, ACTIVITY_TYPE> mSizeStrategy;
+    protected RecentsAnimationController mRecentsAnimationController;
+    protected SurfaceTransactionApplier mSyncTransactionApplier;
+    protected int mTaskWidth;
+    protected int mTaskHeight;
+    protected final TransformParams mLiveTileParams = new TransformParams();
+    protected final TaskViewSimulator mLiveTileTaskViewSimulator;
+    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.
+    protected Float mLastComputedTaskStartPushOutDistance = null;
+    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 float[] mTempFloat = new float[1];
+    private final List<OnScrollChangedListener> mScrollListeners = new ArrayList<>();
+
+    // The threshold at which we update the SystemUI flags when animating from the task into the app
+    public static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.85f;
+
+    protected final ACTIVITY_TYPE mActivity;
+    private final float mFastFlingVelocity;
+    private final RecentsModel mModel;
+    private final int mRowSpacing;
+    private final int mGridSideMargin;
+    private final ClearAllButton mClearAllButton;
+    private final Rect mClearAllButtonDeadZoneRect = new Rect();
+    private final Rect mTaskViewDeadZoneRect = new Rect();
+    /**
+     * Reflects if Recents is currently in the middle of a gesture
+     */
+    private boolean mGestureActive;
+
+    // Keeps track of the previously known visible tasks for purposes of loading/unloading task data
+    private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
+
+    private final InvariantDeviceProfile mIdp;
+
+    private final ViewPool<TaskView> mTaskViewPool;
+
+    private final TaskOverlayFactory mTaskOverlayFactory;
+
+    protected boolean mDisallowScrollToClearAll;
+    private boolean mOverlayEnabled;
+    protected boolean mFreezeViewVisibility;
+    private boolean mOverviewGridEnabled;
+    private boolean mOverviewFullscreenEnabled;
+
+    private float mAdjacentPageHorizontalOffset = 0;
+    protected float mTaskViewsSecondaryTranslation = 0;
+    protected float mTaskViewsPrimarySplitTranslation = 0;
+    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 final IntSet mTopRowIdSet = new IntSet();
+
+    // The GestureEndTarget that is still in progress.
+    protected GestureState.GestureEndTarget mCurrentGestureEndTarget;
+
+    // TODO(b/187528071): Remove these and replace with a real scrim.
+    private float mColorTint;
+    private final int mTintingColor;
+    private ObjectAnimator mTintingAnimator;
+
+    private int mOverScrollShift = 0;
+
+    /**
+     * TODO: Call reloadIdNeeded in onTaskStackChanged.
+     */
+    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+        @Override
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+            if (!mHandleTaskStackChanges) {
+                return;
+            }
+            // Check this is for the right user
+            if (!checkCurrentOrManagedUserId(userId, getContext())) {
+                return;
+            }
+
+            // Remove the task immediately from the task list
+            TaskView taskView = getTaskView(taskId);
+            if (taskView != null) {
+                removeView(taskView);
+            }
+        }
+
+        @Override
+        public void onActivityUnpinned() {
+            if (!mHandleTaskStackChanges) {
+                return;
+            }
+
+            reloadIfNeeded();
+            enableLayoutTransitions();
+        }
+
+        @Override
+        public void onTaskRemoved(int taskId) {
+            if (!mHandleTaskStackChanges) {
+                return;
+            }
+
+            TaskView taskView = getTaskView(taskId);
+            if (taskView == null) {
+                return;
+            }
+            Task.TaskKey taskKey = taskView.getTask().key;
+            UI_HELPER_EXECUTOR.execute(new HandlerRunnable<>(
+                    UI_HELPER_EXECUTOR.getHandler(),
+                    () -> PackageManagerWrapper.getInstance()
+                            .getActivityInfo(taskKey.getComponent(), taskKey.userId) == null,
+                    MAIN_EXECUTOR,
+                    apkRemoved -> {
+                        if (apkRemoved) {
+                            dismissTask(taskId);
+                        } else {
+                            mModel.isTaskRemoved(taskKey.id, taskRemoved -> {
+                                if (taskRemoved) {
+                                    dismissTask(taskId);
+                                }
+                            });
+                        }
+                    }));
+        }
+    };
+
+    private final PinnedStackAnimationListener mIPipAnimationListener =
+            new PinnedStackAnimationListener();
+    private int mPipCornerRadius;
+
+    // Used to keep track of the last requested task list id, so that we do not request to load the
+    // tasks again if we have already requested it and the task list has not changed
+    private int mTaskListChangeId = -1;
+
+    // Only valid until the launcher state changes to NORMAL
+    protected int mRunningTaskId = -1;
+    protected boolean mRunningTaskTileHidden;
+    private Task mTmpRunningTask;
+    protected int mFocusedTaskId = -1;
+    private float mFocusedTaskRatio;
+
+    private boolean mRunningTaskIconScaledDown = false;
+    private boolean mRunningTaskShowScreenshot = false;
+
+    private boolean mOverviewStateEnabled;
+    private boolean mHandleTaskStackChanges;
+    private boolean mSwipeDownShouldLaunchApp;
+    private boolean mTouchDownToStartHome;
+    private final float mSquaredTouchSlop;
+    private int mDownX;
+    private int mDownY;
+
+    private PendingAnimation mPendingAnimation;
+    private LayoutTransition mLayoutTransition;
+
+    @ViewDebug.ExportedProperty(category = "launcher")
+    protected float mContentAlpha = 1;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    protected float mFullscreenProgress = 0;
+    /**
+     * How modal is the current task to be displayed, 1 means the task is fully modal and no other
+     * tasks are show. 0 means the task is displays in context in the list with other tasks.
+     */
+    @ViewDebug.ExportedProperty(category = "launcher")
+    protected float mTaskModalness = 0;
+
+    // Keeps track of task id whose visual state should not be reset
+    private int mIgnoreResetTaskId = -1;
+
+    // Variables for empty state
+    private final Drawable mEmptyIcon;
+    private final CharSequence mEmptyMessage;
+    private final TextPaint mEmptyMessagePaint;
+    private final Point mLastMeasureSize = new Point();
+    private final int mEmptyMessagePadding;
+    private boolean mShowEmptyMessage;
+    private OnEmptyMessageUpdatedListener mOnEmptyMessageUpdatedListener;
+    private Layout mEmptyTextLayout;
+
+    /**
+     * Placeholder view indicating where the first split screen selected app will be placed
+     */
+    private SplitPlaceholderView mSplitPlaceholderView;
+    /**
+     * 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)}
+     */
+    private TaskView mSplitHiddenTaskView;
+    /**
+     * 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
+     * screen.
+     * NOTE: This index is the index while {@link #mSplitHiddenTaskView} was a child of recentsView,
+     * this doesn't get adjusted to reflect the new child count after the taskView is dismissed/
+     * removed from recentsView
+     */
+    private int mSplitHiddenTaskViewIndex;
+
+    // Keeps track of the index where the first TaskView should be
+    private int mTaskViewStartIndex = 0;
+    private OverviewActionsView mActionsView;
+
+    private MultiWindowModeChangedListener mMultiWindowModeChangedListener =
+            new MultiWindowModeChangedListener() {
+                @Override
+                public void onMultiWindowModeChanged(boolean inMultiWindowMode) {
+                    mOrientationState.setMultiWindowMode(inMultiWindowMode);
+                    setLayoutRotation(mOrientationState.getTouchRotation(),
+                            mOrientationState.getDisplayRotation());
+                    updateChildTaskOrientations();
+                    if (!inMultiWindowMode && mOverviewStateEnabled) {
+                        // TODO: Re-enable layout transitions for addition of the unpinned task
+                        reloadIfNeeded();
+                    }
+                }
+            };
+
+    private RunnableList mSideTaskLaunchCallback;
+
+    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
+            BaseActivityInterface sizeStrategy) {
+        super(context, attrs, defStyleAttr);
+        setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
+        setEnableFreeScroll(true);
+        mSizeStrategy = sizeStrategy;
+        mActivity = BaseActivity.fromContext(context);
+        mOrientationState = new RecentsOrientedState(
+                context, mSizeStrategy, this::animateRecentsRotationInPlace);
+        final int rotation = mActivity.getDisplay().getRotation();
+        mOrientationState.setRecentsRotation(rotation);
+
+        mFastFlingVelocity = getResources()
+                .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
+        mModel = RecentsModel.INSTANCE.get(context);
+        mIdp = InvariantDeviceProfile.INSTANCE.get(context);
+
+        mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
+                .inflate(R.layout.overview_clear_all_button, this, false);
+        mClearAllButton.setOnClickListener(this::dismissAllTasks);
+        mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 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);
+        mSquaredTouchSlop = squaredTouchSlop(context);
+
+        mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
+        mEmptyIcon.setCallback(this);
+        mEmptyMessage = context.getText(R.string.recents_empty_message);
+        mEmptyMessagePaint = new TextPaint();
+        mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
+        mEmptyMessagePaint.setTextSize(getResources()
+                .getDimension(R.dimen.recents_empty_message_text_size));
+        mEmptyMessagePaint.setTypeface(Typeface.create(Themes.getDefaultBodyFont(context),
+                Typeface.NORMAL));
+        mEmptyMessagePaint.setAntiAlias(true);
+        mEmptyMessagePadding = getResources()
+                .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
+        setWillNotDraw(false);
+        updateEmptyMessage();
+        mOrientationHandler = mOrientationState.getOrientationHandler();
+
+        mTaskOverlayFactory = Overrides.getObject(
+                TaskOverlayFactory.class,
+                context.getApplicationContext(),
+                R.string.task_overlay_factory_class);
+
+        // 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);
+    }
+
+    public OverScroller getScroller() {
+        return mScroller;
+    }
+
+    public boolean isRtl() {
+        return mIsRtl;
+    }
+
+    @Override
+    protected void initEdgeEffect() {
+        mEdgeGlowLeft = new TranslateEdgeEffect(getContext());
+        mEdgeGlowRight = new TranslateEdgeEffect(getContext());
+    }
+
+    @Override
+    protected void drawEdgeEffect(Canvas canvas) {
+        // Do not draw edge effect
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        // Draw overscroll
+        if (mAllowOverScroll && (!mEdgeGlowRight.isFinished() || !mEdgeGlowLeft.isFinished())) {
+            final int restoreCount = canvas.save();
+
+            int primarySize = mOrientationHandler.getPrimaryValue(getWidth(), getHeight());
+            int scroll = OverScroll.dampedScroll(getUndampedOverScrollShift(), primarySize);
+            mOrientationHandler.set(canvas, CANVAS_TRANSLATE, scroll);
+
+            if (mOverScrollShift != scroll) {
+                mOverScrollShift = scroll;
+                dispatchScrollChanged();
+            }
+
+            super.dispatchDraw(canvas);
+            canvas.restoreToCount(restoreCount);
+        } else {
+            if (mOverScrollShift != 0) {
+                mOverScrollShift = 0;
+                dispatchScrollChanged();
+            }
+            super.dispatchDraw(canvas);
+        }
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
+                && mLiveTileParams.getTargetSet() != null) {
+            redrawLiveTile();
+        }
+    }
+
+    private float getUndampedOverScrollShift() {
+        final int width = getWidth();
+        final int height = getHeight();
+        int primarySize = mOrientationHandler.getPrimaryValue(width, height);
+        int secondarySize = mOrientationHandler.getSecondaryValue(width, height);
+
+        float effectiveShift = 0;
+        if (!mEdgeGlowLeft.isFinished()) {
+            mEdgeGlowLeft.setSize(secondarySize, primarySize);
+            if (((TranslateEdgeEffect) mEdgeGlowLeft).getTranslationShift(mTempFloat)) {
+                effectiveShift = mTempFloat[0];
+                postInvalidateOnAnimation();
+            }
+        }
+        if (!mEdgeGlowRight.isFinished()) {
+            mEdgeGlowRight.setSize(secondarySize, primarySize);
+            if (((TranslateEdgeEffect) mEdgeGlowRight).getTranslationShift(mTempFloat)) {
+                effectiveShift -= mTempFloat[0];
+                postInvalidateOnAnimation();
+            }
+        }
+
+        return effectiveShift * primarySize;
+    }
+
+    /**
+     * Returns the view shift due to overscroll
+     */
+    public int getOverScrollShift() {
+        return mOverScrollShift;
+    }
+
+    @Override
+    public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
+        if (mHandleTaskStackChanges) {
+            TaskView taskView = getTaskView(taskId);
+            if (taskView != null) {
+                Task task = taskView.getTask();
+                taskView.getThumbnail().setThumbnail(task, thumbnailData);
+                return task;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void onTaskIconChanged(String pkg, UserHandle user) {
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView tv = getTaskViewAt(i);
+            Task task = tv.getTask();
+            if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
+                    && task.key.userId == user.getIdentifier()) {
+                task.icon = null;
+                if (tv.getIconView().getDrawable() != null) {
+                    tv.onTaskListVisibilityChanged(true /* visible */);
+                }
+            }
+        }
+    }
+
+    /**
+     * Update the thumbnail of the task.
+     * @param refreshNow Refresh immediately if it's true.
+     */
+    public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData, boolean refreshNow) {
+        TaskView taskView = getTaskView(taskId);
+        if (taskView != null) {
+            taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData, refreshNow);
+        }
+        return taskView;
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        updateTaskStackListenerState();
+    }
+
+    public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+        mActionsView = actionsView;
+        mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
+        mSplitPlaceholderView = splitPlaceholderView;
+    }
+
+    public SplitPlaceholderView getSplitPlaceholder() {
+        return mSplitPlaceholderView;
+    }
+
+    public boolean isSplitSelectionActive() {
+        return mSplitPlaceholderView.getSplitController().isSplitSelectActive();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateTaskStackListenerState();
+        mModel.getThumbnailCache().getHighResLoadingState().addCallback(this);
+        mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+        mSyncTransactionApplier = new SurfaceTransactionApplier(this);
+        mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
+        RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
+        mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
+        SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
+                mIPipAnimationListener);
+        mOrientationState.initListeners();
+        SplitScreenBounds.INSTANCE.addOnChangeListener(this);
+        mTaskOverlayFactory.initListeners();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        updateTaskStackListenerState();
+        mModel.getThumbnailCache().getHighResLoadingState().removeCallback(this);
+        mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        mSyncTransactionApplier = null;
+        mLiveTileParams.setSyncTransactionApplier(null);
+        executeSideTaskLaunchCallback();
+        RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
+        SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
+        SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
+        mIPipAnimationListener.setActivityAndRecentsView(null, null);
+        mOrientationState.destroyListeners();
+        mTaskOverlayFactory.removeListeners();
+    }
+
+    @Override
+    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) {
+            TaskView taskView = (TaskView) child;
+            mHasVisibleTaskData.delete(taskView.getTask().key.id);
+            mTaskViewPool.recycle(taskView);
+            mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
+        }
+        updateTaskStartIndex(child);
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        child.setAlpha(mContentAlpha);
+        // 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();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        maybeDrawEmptyMessage(canvas);
+        super.draw(canvas);
+    }
+
+    public void addSideTaskLaunchCallback(RunnableList callback) {
+        if (mSideTaskLaunchCallback == null) {
+            mSideTaskLaunchCallback = new RunnableList();
+        }
+        mSideTaskLaunchCallback.add(callback::executeAllAndDestroy);
+    }
+
+    private void executeSideTaskLaunchCallback() {
+        if (mSideTaskLaunchCallback != null) {
+            mSideTaskLaunchCallback.executeAllAndDestroy();
+            mSideTaskLaunchCallback = null;
+        }
+    }
+
+    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);
+            }
+        }
+    }
+
+    public void launchSideTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps,
+            RemoteAnimationTargetCompat[] wallpaper, RemoteAnimationTargetCompat[] nonApps) {
+        AnimatorSet anim = new AnimatorSet();
+        TaskView taskView = getTaskView(taskId);
+        if (taskView == null || !isTaskViewVisible(taskView)) {
+            // TODO: Refine this animation.
+            SurfaceTransactionApplier surfaceApplier =
+                    new SurfaceTransactionApplier(mActivity.getDragLayer());
+            ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+            appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+            appAnimator.setInterpolator(ACCEL_DEACCEL);
+            appAnimator.addUpdateListener(valueAnimator -> {
+                float percent = valueAnimator.getAnimatedFraction();
+                SurfaceParams.Builder builder = new SurfaceParams.Builder(
+                        apps[apps.length - 1].leash);
+                Matrix matrix = new Matrix();
+                matrix.postScale(percent, percent);
+                matrix.postTranslate(mActivity.getDeviceProfile().widthPx * (1 - percent) / 2,
+                        mActivity.getDeviceProfile().heightPx * (1 - percent) / 2);
+                builder.withAlpha(percent).withMatrix(matrix);
+                surfaceApplier.scheduleApply(builder.build());
+            });
+            anim.play(appAnimator);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    finishRecentsAnimation(false /* toRecents */, null);
+                }
+            });
+        } else {
+            TaskViewUtils.composeRecentsLaunchAnimator(anim, taskView, apps, wallpaper, nonApps,
+                    true /* launcherClosing */, mActivity.getStateManager(), this,
+                    getDepthController());
+        }
+        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);
+            int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
+            return isTaskViewWithinBounds(tv, screenStart, screenEnd);
+        } else {
+            // For now, just check if it's the active task or an adjacent task
+            return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
+        }
+    }
+
+    private boolean isTaskViewWithinBounds(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 && taskStart <= end) || (taskEnd >= start
+                && taskEnd <= end);
+    }
+
+    public TaskView getTaskView(int taskId) {
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView taskView = getTaskViewAt(i);
+            if (taskView.hasTaskId(taskId)) {
+                return taskView;
+            }
+        }
+        return null;
+    }
+
+    public void setOverviewStateEnabled(boolean enabled) {
+        mOverviewStateEnabled = enabled;
+        updateTaskStackListenerState();
+        mOrientationState.setRotationWatcherEnabled(enabled);
+        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);
+            }
+        }
+        updateLocusId();
+    }
+
+    /**
+     * Whether the Clear All button is hidden or fully visible. Used to determine if center
+     * displayed page is a task or the Clear All button.
+     *
+     * @return True = Clear All button not fully visible, center page is a task. False = Clear All
+     * button fully visible, center page is Clear All button.
+     */
+    public boolean isClearAllHidden() {
+        return mClearAllButton.getAlpha() != 1f;
+    }
+
+    @Override
+    protected void onPageBeginTransition() {
+        super.onPageBeginTransition();
+        mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
+    }
+
+    @Override
+    protected void onPageEndTransition() {
+        super.onPageEndTransition();
+        if (isClearAllHidden()) {
+            mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
+        }
+        if (getNextPage() > 0) {
+            setSwipeDownShouldLaunchApp(true);
+        }
+    }
+
+    @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);
+                if (isTaskViewVisible(taskView) && taskView.offerTouchToChildren(ev)) {
+                    // Keep consuming events to pass to delegate
+                    return true;
+                }
+            }
+        } else {
+            TaskView taskView = getCurrentPageTaskView();
+            if (taskView != null && taskView.offerTouchToChildren(ev)) {
+                // Keep consuming events to pass to delegate
+                return true;
+            }
+        }
+
+        final int x = (int) ev.getX();
+        final int y = (int) ev.getY();
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_UP:
+                if (mTouchDownToStartHome) {
+                    startHome();
+                }
+                mTouchDownToStartHome = false;
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                mTouchDownToStartHome = false;
+                break;
+            case MotionEvent.ACTION_MOVE:
+                // Passing the touch slop will not allow dismiss to home
+                if (mTouchDownToStartHome &&
+                        (isHandlingTouch() ||
+                                squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop)) {
+                    mTouchDownToStartHome = false;
+                }
+                break;
+            case MotionEvent.ACTION_DOWN:
+                // Touch down anywhere but the deadzone around the visible clear all button and
+                // between the task views will start home on touch up
+                if (!isHandlingTouch() && !isModal()) {
+                    if (mShowEmptyMessage) {
+                        mTouchDownToStartHome = true;
+                    } else {
+                        updateDeadZoneRects();
+                        final boolean clearAllButtonDeadZoneConsumed =
+                                mClearAllButton.getAlpha() == 1
+                                        && mClearAllButtonDeadZoneRect.contains(x, y);
+                        final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+                        if (!clearAllButtonDeadZoneConsumed && !cameFromNavBar
+                                && !mTaskViewDeadZoneRect.contains(x + getScrollX(), y)) {
+                            mTouchDownToStartHome = true;
+                        }
+                    }
+                }
+                mDownX = x;
+                mDownY = y;
+                break;
+        }
+
+        return isHandlingTouch();
+    }
+
+    @Override
+    protected void onNotSnappingToPageInFreeScroll() {
+        int finalPos = mScroller.getFinalX();
+        if (!showAsGrid() && finalPos > mMinScroll && finalPos < mMaxScroll) {
+            int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
+            int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
+
+            // If scrolling ends in the half of the added space that is closer to
+            // the end, settle to the end. Otherwise snap to the nearest page.
+            // If flinging past one of the ends, don't change the velocity as it
+            // will get stopped at the end anyway.
+            int pageSnapped = finalPos < (firstPageScroll + mMinScroll) / 2
+                    ? mMinScroll
+                    : finalPos > (lastPageScroll + mMaxScroll) / 2
+                            ? mMaxScroll
+                            : getScrollForPage(mNextPage);
+
+            mScroller.setFinalX(pageSnapped);
+            // Ensure the scroll/snap doesn't happen too fast;
+            int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
+                    - mScroller.getDuration();
+            if (extraScrollDuration > 0) {
+                mScroller.extendDuration(extraScrollDuration);
+            }
+        }
+    }
+
+    @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()) {
+            super.determineScrollingStart(ev, touchSlopScale);
+        }
+    }
+
+    protected void applyLoadPlan(ArrayList<Task> tasks) {
+        if (mPendingAnimation != null) {
+            mPendingAnimation.addEndListener(success -> applyLoadPlan(tasks));
+            return;
+        }
+
+        if (tasks == null || tasks.isEmpty()) {
+            removeTasksViewsAndClearAllButton();
+            onTaskStackUpdated();
+            return;
+        }
+
+        int currentTaskId = -1;
+        TaskView currentTaskView = getTaskViewAtByAbsoluteIndex(mCurrentPage);
+        if (currentTaskView != null) {
+            currentTaskId = currentTaskView.getTask().key.id;
+        }
+
+        // Unload existing visible task data
+        unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
+
+        TaskView ignoreResetTaskView =
+                mIgnoreResetTaskId == -1 ? null : getTaskView(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);
+            }
+        }
+
+        // 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);
+        }
+        updateTaskSize();
+
+        if (mNextPage == INVALID_PAGE) {
+            // Set the current page to the running task, but not if settling on new task.
+            TaskView runningTaskView = getRunningTaskView();
+            if (runningTaskView != null) {
+                setCurrentPage(indexOfChild(runningTaskView));
+            } else if (getTaskViewCount() > 0) {
+                setCurrentPage(indexOfChild(getTaskViewAt(0)));
+            }
+        } else if (currentTaskId != -1) {
+            currentTaskView = getTaskView(currentTaskId);
+            if (currentTaskView != null) {
+                setCurrentPage(indexOfChild(currentTaskView));
+            }
+        }
+
+        if (mIgnoreResetTaskId != -1 && getTaskView(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.
+            mIgnoreResetTaskId = -1;
+        }
+        resetTaskVisuals();
+        onTaskStackUpdated();
+        updateEnabledOverlays();
+    }
+
+    private boolean isModal() {
+        return mTaskModalness > 0;
+    }
+
+    public boolean isLoadingTasks() {
+        return mModel.isLoadingTasksInBackground();
+    }
+
+    private void removeTasksViewsAndClearAllButton() {
+        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
+            removeView(getTaskViewAt(i));
+        }
+        if (indexOfChild(mClearAllButton) != -1) {
+            removeView(mClearAllButton);
+        }
+    }
+
+    public int getTaskViewCount() {
+        int taskViewCount = getChildCount() - mTaskViewStartIndex;
+        if (indexOfChild(mClearAllButton) != -1) {
+            taskViewCount--;
+        }
+        return taskViewCount;
+    }
+
+    protected void onTaskStackUpdated() {
+        // Lazily update the empty message only when the task stack is reapplied
+        updateEmptyMessage();
+    }
+
+    public void resetTaskVisuals() {
+        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
+            TaskView taskView = getTaskViewAt(i);
+            if (mIgnoreResetTaskId != taskView.getTask().key.id) {
+                taskView.resetViewTransforms();
+                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;
+
+            // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
+            // null.
+            if (!mRunningTaskShowScreenshot) {
+                setRunningTaskViewShowScreenshot(mRunningTaskShowScreenshot);
+            }
+        }
+        if (mRunningTaskTileHidden) {
+            setRunningTaskHidden(mRunningTaskTileHidden);
+        }
+
+        // Force apply the scale.
+        if (mIgnoreResetTaskId != mRunningTaskId) {
+            applyRunningTaskIconScale();
+        }
+
+        updateCurveProperties();
+        // Update the set of visible task's data
+        loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
+        setTaskModalness(0);
+        setColorTint(0);
+    }
+
+    public void setFullscreenProgress(float fullscreenProgress) {
+        mFullscreenProgress = fullscreenProgress;
+        int taskCount = getTaskViewCount();
+        for (int i = 0; i < taskCount; i++) {
+            getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
+        }
+        mClearAllButton.setFullscreenProgress(fullscreenProgress);
+
+        // Fade out the actions view quickly (0.1 range)
+        mActionsView.getFullscreenAlpha().setValue(
+                mapToRange(fullscreenProgress, 0, 0.1f, 1f, 0f, LINEAR));
+    }
+
+    private void updateTaskStackListenerState() {
+        boolean handleTaskStackChanges = mOverviewStateEnabled && isAttachedToWindow()
+                && getWindowVisibility() == VISIBLE;
+        if (handleTaskStackChanges != mHandleTaskStackChanges) {
+            mHandleTaskStackChanges = handleTaskStackChanges;
+            if (handleTaskStackChanges) {
+                reloadIfNeeded();
+            }
+        }
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        mInsets.set(insets);
+
+        // Update DeviceProfile dependant state.
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        setOverviewGridEnabled(
+                mActivity.getStateManager().getState().displayOverviewTasksAsGrid(dp));
+
+        // Propagate DeviceProfile change event.
+        mLiveTileTaskViewSimulator.setDp(dp);
+        mActionsView.setDp(dp);
+        mOrientationState.setDeviceProfile(dp);
+
+        // Update RecentsView adn TaskView's DeviceProfile dependent layout.
+        updateOrientationHandler();
+    }
+
+    private void updateOrientationHandler() {
+        updateOrientationHandler(true);
+    }
+
+    private void updateOrientationHandler(boolean forceRecreateDragLayerControllers) {
+        // Handle orientation changes.
+        PagedOrientationHandler oldOrientationHandler = mOrientationHandler;
+        mOrientationHandler = mOrientationState.getOrientationHandler();
+
+        mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
+        setLayoutDirection(mIsRtl
+                ? View.LAYOUT_DIRECTION_RTL
+                : View.LAYOUT_DIRECTION_LTR);
+        mClearAllButton.setLayoutDirection(mIsRtl
+                ? View.LAYOUT_DIRECTION_LTR
+                : View.LAYOUT_DIRECTION_RTL);
+        mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
+
+        if (forceRecreateDragLayerControllers
+                || !mOrientationHandler.equals(oldOrientationHandler)) {
+            // Changed orientations, update controllers so they intercept accordingly.
+            mActivity.getDragLayer().recreateControllers();
+        }
+
+        boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0
+                || mOrientationState.getRecentsActivityRotation() != ROTATION_0;
+        mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
+                !mOrientationState.canRecentsActivityRotate() && isInLandscape);
+
+        // Update TaskView's DeviceProfile dependent layout.
+        updateChildTaskOrientations();
+
+        // Recalculate DeviceProfile dependent layout.
+        updateSizeAndPadding();
+
+        requestLayout();
+        // Reapply the current page to update page scrolls.
+        setCurrentPage(mCurrentPage);
+    }
+
+    // Update task size and padding that are dependent on DeviceProfile and insets.
+    private void updateSizeAndPadding() {
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        getTaskSize(mTempRect);
+        mTaskWidth = mTempRect.width();
+        mTaskHeight = mTempRect.height();
+
+        mTempRect.top -= dp.overviewTaskThumbnailTopMarginPx;
+        setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
+                dp.widthPx - mInsets.right - mTempRect.right,
+                dp.heightPx - mInsets.bottom - mTempRect.bottom);
+
+        mSizeStrategy.calculateGridSize(mActivity, mActivity.getDeviceProfile(),
+                mLastComputedGridSize);
+        mSizeStrategy.calculateGridTaskSize(mActivity, mActivity.getDeviceProfile(),
+                mLastComputedGridTaskSize, mOrientationHandler);
+
+        // 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() {
+        final int taskCount = getTaskViewCount();
+        if (taskCount == 0) {
+            return;
+        }
+
+        float accumulatedTranslationX = 0;
+        for (int i = 0; i < taskCount; i++) {
+            TaskView taskView = getTaskViewAt(i);
+            taskView.updateTaskSize();
+            taskView.getPrimaryFullscreenTranslationProperty().set(taskView,
+                    accumulatedTranslationX);
+            taskView.getSecondaryFullscreenTranslationProperty().set(taskView, 0f);
+            // Compensate space caused by TaskView scaling.
+            float widthDiff =
+                    taskView.getLayoutParams().width * (1 - taskView.getFullscreenScale());
+            accumulatedTranslationX += mIsRtl ? widthDiff : -widthDiff;
+        }
+
+        mClearAllButton.setFullscreenTranslationPrimary(accumulatedTranslationX);
+
+        updateGridProperties();
+    }
+
+    public void getTaskSize(Rect outRect) {
+        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect,
+                mOrientationHandler);
+        mLastComputedTaskSize.set(outRect);
+    }
+
+    /**
+     * 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);
+    }
+
+    /** Gets the last computed task size */
+    public Rect getLastComputedTaskSize() {
+        return mLastComputedTaskSize;
+    }
+
+    public Rect getLastComputedGridTaskSize() {
+        return mLastComputedGridTaskSize;
+    }
+
+    /** Gets the task size for modal state. */
+    public void getModalTaskSize(Rect outRect) {
+        mSizeStrategy.calculateModalTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
+    }
+
+    @Override
+    protected boolean computeScrollHelper() {
+        boolean scrolling = super.computeScrollHelper();
+        boolean isFlingingFast = false;
+        updateCurveProperties();
+        if (scrolling || isHandlingTouch()) {
+            if (scrolling) {
+                // Check if we are flinging quickly to disable high res thumbnail loading
+                isFlingingFast = mScroller.getCurrVelocity() > mFastFlingVelocity;
+            }
+
+            // 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 the high res thumbnail loader state
+        mModel.getThumbnailCache().getHighResLoadingState().setFlingingFast(isFlingingFast);
+        return scrolling;
+    }
+
+    /**
+     * Scales and adjusts translation of adjacent pages as if on a curved carousel.
+     */
+    public void updateCurveProperties() {
+        if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
+            return;
+        }
+        int scroll = mOrientationHandler.getPrimaryScroll(this);
+        mClearAllButton.onRecentsViewScroll(scroll, mOverviewGridEnabled);
+    }
+
+    @Override
+    protected int getDestinationPage(int scaledScroll) {
+        if (!(mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get())) {
+            return super.getDestinationPage(scaledScroll);
+        }
+
+        final int childCount = getChildCount();
+        if (mPageScrolls == null || childCount != mPageScrolls.length) {
+            return -1;
+        }
+
+        // When in tablet with variable task width, return the page which scroll is closest to
+        // screenStart instead of page nearest to center of screen.
+        int minDistanceFromScreenStart = Integer.MAX_VALUE;
+        int minDistanceFromScreenStartIndex = -1;
+        for (int i = 0; i < childCount; ++i) {
+            int distanceFromScreenStart = Math.abs(mPageScrolls[i] - scaledScroll);
+            if (distanceFromScreenStart < minDistanceFromScreenStart) {
+                minDistanceFromScreenStart = distanceFromScreenStart;
+                minDistanceFromScreenStartIndex = i;
+            }
+        }
+        return minDistanceFromScreenStartIndex;
+    }
+
+    /**
+     * Iterates through all the tasks, and loads the associated task data for newly visible tasks,
+     * and unloads the associated task data for tasks that are no longer visible.
+     */
+    public void loadVisibleTaskData(@TaskView.TaskDataChanges int dataChanges) {
+        if (!mOverviewStateEnabled || 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;
+        }
+
+        int lower = 0;
+        int upper = 0;
+        int visibleStart = 0;
+        int visibleEnd = 0;
+        if (showAsGrid()) {
+            int screenStart = mOrientationHandler.getPrimaryScroll(this);
+            int pageOrientedSize = mOrientationHandler.getMeasuredSize(this);
+            int halfScreenSize = pageOrientedSize / 2;
+            // Use +/- 50% screen width as visible area.
+            visibleStart = screenStart - halfScreenSize;
+            visibleEnd = screenStart + pageOrientedSize + halfScreenSize;
+        } else {
+            int centerPageIndex = getPageNearestToCenterOfScreen();
+            int numChildren = getChildCount();
+            lower = Math.max(0, centerPageIndex - 2);
+            upper = Math.min(centerPageIndex + 2, numChildren - 1);
+        }
+
+        // Update the task data for the in/visible children
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView taskView = getTaskViewAt(i);
+            Task task = taskView.getTask();
+            int index = indexOfChild(taskView);
+            boolean visible;
+            if (showAsGrid()) {
+                visible = isTaskViewWithinBounds(taskView, visibleStart, visibleEnd);
+            } else {
+                visible = lower <= index && index <= upper;
+            }
+            if (visible) {
+                if (task == mTmpRunningTask) {
+                    // Skip loading if this is the task that we are animating into
+                    continue;
+                }
+                if (!mHasVisibleTaskData.get(task.key.id)) {
+                    // Ignore thumbnail update if it's current running task during the gesture
+                    // We snapshot at end of gesture, it will update then
+                    int changes = dataChanges;
+                    if (taskView == getRunningTaskView() && mGestureActive) {
+                        changes &= ~TaskView.FLAG_UPDATE_THUMBNAIL;
+                    }
+                    taskView.onTaskListVisibilityChanged(true /* visible */, changes);
+                }
+                mHasVisibleTaskData.put(task.key.id, visible);
+            } else {
+                if (mHasVisibleTaskData.get(task.key.id)) {
+                    taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
+                }
+                mHasVisibleTaskData.delete(task.key.id);
+            }
+        }
+    }
+
+    /**
+     * Unloads any associated data from the currently visible tasks
+     */
+    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));
+                if (taskView != null) {
+                    taskView.onTaskListVisibilityChanged(false /* visible */, dataChanges);
+                }
+            }
+        }
+        mHasVisibleTaskData.clear();
+    }
+
+    @Override
+    public void onHighResLoadingStateChanged(boolean enabled) {
+        // Whenever the high res loading state changes, poke each of the visible tasks to see if
+        // 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));
+                if (taskView != null) {
+                    // Poke the view again, which will trigger it to load high res if the state
+                    // is enabled
+                    taskView.onTaskListVisibilityChanged(true /* visible */);
+                }
+            }
+        }
+    }
+
+    public abstract void startHome();
+
+    /** `true` if there is a +1 space available in overview. */
+    public boolean hasRecentsExtraCard() {
+        return false;
+    }
+
+    public void reset() {
+        setCurrentTask(-1);
+        mIgnoreResetTaskId = -1;
+        mTaskListChangeId = -1;
+        mFocusedTaskId = -1;
+
+        if (mRecentsAnimationController != null) {
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
+                // We are still drawing the live tile, finish it now to clean up.
+                finishRecentsAnimation(true /* toRecents */, null);
+            } else {
+                mRecentsAnimationController = null;
+            }
+        }
+        setEnableDrawingLiveTile(false);
+        mLiveTileParams.setTargetSet(null);
+        mLiveTileTaskViewSimulator.setDrawsBelowRecents(true);
+
+        // 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.
+        post(() -> {
+            unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
+            setCurrentPage(0);
+            LayoutUtils.setViewEnabled(mActionsView, true);
+            if (mOrientationState.setGestureActive(false)) {
+                updateOrientationHandler(/* forceRecreateDragLayerControllers = */ false);
+            }
+        });
+    }
+
+    public int getRunningTaskId() {
+        return mRunningTaskId;
+    }
+
+    public @Nullable TaskView getRunningTaskView() {
+        return getTaskView(mRunningTaskId);
+    }
+
+    public int getRunningTaskIndex() {
+        return getTaskIndexForId(mRunningTaskId);
+    }
+
+    public @Nullable TaskView getFocusedTaskView() {
+        return getTaskView(mFocusedTaskId);
+    }
+
+    /**
+     * Returns the width to height ratio of the focused {@link TaskView}.
+     */
+    public float getFocusedTaskRatio() {
+        return mFocusedTaskRatio;
+    }
+
+    /**
+     * Get the index of the task view whose id matches {@param taskId}.
+     * @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);
+        return tv == null ? -1 : indexOfChild(tv);
+    }
+
+    public int getTaskViewStartIndex() {
+        return mTaskViewStartIndex;
+    }
+
+    /**
+     * Reloads the view if anything in recents changed.
+     */
+    public void reloadIfNeeded() {
+        if (!mModel.isTaskListValid(mTaskListChangeId)) {
+            mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
+        }
+    }
+
+    /**
+     * Called when a gesture from an app is starting.
+     */
+    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)) {
+            updateOrientationHandler();
+        }
+
+        showCurrentTask(runningTaskInfo);
+        setEnableFreeScroll(false);
+        setEnableDrawingLiveTile(false);
+        setRunningTaskHidden(true);
+        setRunningTaskIconScaledDown(true);
+    }
+
+    /**
+     * Called only when a swipe-up gesture from an app has completed. Only called after
+     * {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}.
+     */
+    public void onSwipeUpAnimationSuccess() {
+        if (getRunningTaskView() != null) {
+            animateUpRunningTaskIconScale();
+        }
+        setSwipeDownShouldLaunchApp(true);
+    }
+
+    private void animateRecentsRotationInPlace(int newRotation) {
+        if (mOrientationState.canRecentsActivityRotate()) {
+            // Let system take care of the rotation
+            return;
+        }
+        AnimatorSet pa = setRecentsChangedOrientation(true);
+        pa.addListener(AnimatorListeners.forSuccessCallback(() -> {
+            setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
+            mActivity.getDragLayer().recreateControllers();
+            setRecentsChangedOrientation(false).start();
+        }));
+        pa.start();
+    }
+
+    public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) {
+        getRunningTaskIndex();
+        int runningIndex = getCurrentPage();
+        AnimatorSet as = new AnimatorSet();
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            if (runningIndex == i) {
+                continue;
+            }
+            View taskView = getTaskViewAt(i);
+            as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1));
+        }
+        return as;
+    }
+
+
+    private void updateChildTaskOrientations() {
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            getTaskViewAt(i).setOrientationState(mOrientationState);
+        }
+        TaskMenuView tv = (TaskMenuView) getTopOpenViewWithType(mActivity, TYPE_TASK_MENU);
+        if (tv != null) {
+            tv.onRotationChanged();
+        }
+    }
+
+    /**
+     * 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) {
+        if (mSizeStrategy.stateFromGestureEndTarget(endTarget)
+                .displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
+            if (animatorSet == null) {
+                setGridProgress(1);
+            } else {
+                animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+            }
+        }
+        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();
+        } else if (endTarget == GestureState.GestureEndTarget.RECENTS) {
+            setEnableFreeScroll(true);
+        }
+    }
+
+    /**
+     * Called when a gesture from an app has finished, and the animation to the target has ended.
+     */
+    public void onGestureAnimationEnd() {
+        mGestureActive = false;
+        if (mOrientationState.setGestureActive(false)) {
+            updateOrientationHandler();
+        }
+
+        setEnableFreeScroll(true);
+        setEnableDrawingLiveTile(mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS);
+        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            setRunningTaskViewShowScreenshot(true);
+        }
+        setRunningTaskHidden(false);
+        animateUpRunningTaskIconScale();
+
+        if (mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS
+                && (!showAsGrid() || getFocusedTaskView() != null)) {
+            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;
+    }
+
+    /**
+     * Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
+     *
+     * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
+     * is called.  Also scrolls the view to this task.
+     */
+    public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
+        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);
+            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());
+        }
+
+        boolean runningTaskTileHidden = mRunningTaskTileHidden;
+        int runningTaskId = runningTaskInfo == null ? -1 : runningTaskInfo.taskId;
+        setCurrentTask(runningTaskId);
+        if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+            setFocusedTask(runningTaskId);
+        }
+        setCurrentPage(getRunningTaskIndex());
+        setRunningTaskViewShowScreenshot(false);
+        setRunningTaskHidden(runningTaskTileHidden);
+        // Update task size after setting current task.
+        updateTaskSize();
+
+        // Reload the task list
+        mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
+    }
+
+    /**
+     * Sets the running task id, cleaning up the old running task if necessary.
+     */
+    public void setCurrentTask(int runningTaskId) {
+        if (mRunningTaskId == runningTaskId) {
+            return;
+        }
+
+        if (mRunningTaskId != -1) {
+            // Reset the state on the old running task view
+            setRunningTaskIconScaledDown(false);
+            setRunningTaskViewShowScreenshot(true);
+            setRunningTaskHidden(false);
+        }
+        mRunningTaskId = runningTaskId;
+    }
+
+    /**
+     * 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}
+     */
+    public void setRunningTaskHidden(boolean isHidden) {
+        mRunningTaskTileHidden = isHidden;
+        TaskView runningTask = getRunningTaskView();
+        if (runningTask != null) {
+            runningTask.setStableAlpha(isHidden ? 0 : mContentAlpha);
+            if (!isHidden) {
+                AccessibilityManagerCompat.sendCustomAccessibilityEvent(runningTask,
+                        AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
+            }
+        }
+    }
+
+    private void setRunningTaskViewShowScreenshot(boolean showScreenshot) {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mRunningTaskShowScreenshot = showScreenshot;
+            TaskView runningTaskView = getRunningTaskView();
+            if (runningTaskView != null) {
+                runningTaskView.setShowScreenshot(mRunningTaskShowScreenshot);
+            }
+        }
+    }
+
+    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);
+        }
+    }
+
+    private void animateActionsViewIn() {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(
+                mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, 0, 1);
+        anim.setDuration(TaskView.SCALE_ICON_DURATION);
+        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();
+        }
+    }
+
+    /** 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);
+    }
+
+    /**
+     * 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
+     */
+    private void updateGridProperties(boolean isTaskDismissal) {
+        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
+        IntSet topSet = new IntSet();
+        IntSet bottomSet = new IntSet();
+
+        // Horizontal grid translation for each task
+        float[] gridTranslations = new float[taskCount];
+
+        int focusedTaskIndex = Integer.MAX_VALUE;
+        int focusedTaskShift = 0;
+        int focusedTaskWidthAndSpacing = 0;
+        int snappedTaskRowWidth = 0;
+        int snappedPage = getNextPage();
+        TaskView snappedTaskView = getTaskViewAtByAbsoluteIndex(snappedPage);
+
+        if (!isTaskDismissal) {
+            mTopRowIdSet.clear();
+        }
+        for (int i = 0; i < taskCount; i++) {
+            TaskView taskView = getTaskViewAt(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
+            // the grid.
+            if (taskView.isFocusedTask()) {
+                topRowWidth += taskWidthAndSpacing;
+                bottomRowWidth += taskWidthAndSpacing;
+
+                focusedTaskIndex = i;
+                focusedTaskWidthAndSpacing = taskWidthAndSpacing;
+                gridTranslations[i] += focusedTaskShift;
+                gridTranslations[i] += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
+
+                // Center view vertically in case it's from different orientation.
+                taskView.setGridTranslationY((mLastComputedTaskSize.height() + taskTopMargin
+                        - taskView.getLayoutParams().height) / 2f);
+
+                if (taskView == snappedTaskView) {
+                    // If focused task is snapped, the row width is just task width and spacing.
+                    snappedTaskRowWidth = taskWidthAndSpacing;
+                }
+            } else {
+                if (i > focusedTaskIndex) {
+                    // For tasks after the focused task, shift by focused task's width and spacing.
+                    gridTranslations[i] +=
+                            mIsRtl ? focusedTaskWidthAndSpacing : -focusedTaskWidthAndSpacing;
+                } else {
+                    // For task before the focused task, accumulate the width and spacing to
+                    // 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);
+
+                    taskView.setGridTranslationY(taskGridVerticalDiff);
+
+                    // Move horizontally into empty space.
+                    float widthOffset = 0;
+                    for (int j = i - 1; !topSet.contains(j) && j >= 0; j--) {
+                        if (j == focusedTaskIndex) {
+                            continue;
+                        }
+                        widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+                    }
+
+                    float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
+                    gridTranslations[i] += topAccumulatedTranslationX + currentTaskTranslationX;
+                    topAccumulatedTranslationX += currentTaskTranslationX;
+                } else {
+                    bottomRowWidth += taskWidthAndSpacing;
+                    bottomSet.add(i);
+
+                    // Move into bottom row.
+                    taskView.setGridTranslationY(heightOffset + taskGridVerticalDiff);
+
+                    // Move horizontally into empty space.
+                    float widthOffset = 0;
+                    for (int j = i - 1; !bottomSet.contains(j) && j >= 0; j--) {
+                        if (j == focusedTaskIndex) {
+                            continue;
+                        }
+                        widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
+                    }
+
+                    float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
+                    gridTranslations[i] += bottomAccumulatedTranslationX + currentTaskTranslationX;
+                    bottomAccumulatedTranslationX += currentTaskTranslationX;
+                }
+                if (taskView == snappedTaskView) {
+                    snappedTaskRowWidth = isTopRow ? topRowWidth : bottomRowWidth;
+                }
+            }
+        }
+
+        // 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 snappedTaskGridTranslationX = 0;
+        if (snappedTaskView != null) {
+            snappedTaskFullscreenScrollAdjustment = 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);
+        }
+
+        // Use the accumulated translation of the row containing the last task.
+        float clearAllAccumulatedTranslation = topSet.contains(taskCount - 1)
+                ? topAccumulatedTranslationX : bottomAccumulatedTranslationX;
+
+        // If the last task is on the shorter row, ClearAllButton will embed into the shorter row
+        // which is not what we want. Compensate the width difference of the 2 rows in that case.
+        float shorterRowCompensation = 0;
+        if (topRowWidth <= bottomRowWidth) {
+            if (topSet.contains(taskCount - 1)) {
+                shorterRowCompensation = bottomRowWidth - topRowWidth;
+            }
+        } else {
+            if (bottomSet.contains(taskCount - 1)) {
+                shorterRowCompensation = topRowWidth - bottomRowWidth;
+            }
+        }
+        float clearAllShorterRowCompensation =
+                mIsRtl ? -shorterRowCompensation : shorterRowCompensation;
+
+        // If the total width is shorter than one grid's width, move ClearAllButton further away
+        // accordingly. Update longRowWidth if ClearAllButton has been moved.
+        float clearAllShortTotalCompensation = 0;
+        int longRowWidth = Math.max(topRowWidth, bottomRowWidth);
+        if (longRowWidth < mLastComputedGridSize.width()) {
+            float shortTotalCompensation = mLastComputedGridSize.width() - longRowWidth;
+            clearAllShortTotalCompensation =
+                    mIsRtl ? -shortTotalCompensation : shortTotalCompensation;
+            longRowWidth = mLastComputedGridSize.width();
+        }
+
+        float clearAllTotalTranslationX =
+                clearAllAccumulatedTranslation + clearAllShorterRowCompensation
+                        + clearAllShortTotalCompensation + snappedTaskFullscreenScrollAdjustment;
+        if (focusedTaskIndex < taskCount) {
+            // Shift by focused task's width and spacing if a task is focused.
+            clearAllTotalTranslationX +=
+                    mIsRtl ? focusedTaskWidthAndSpacing : -focusedTaskWidthAndSpacing;
+        }
+
+        // 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 minimumDistance =
+                    mLastComputedGridSize.width() - snappedTaskView.getLayoutParams().width;
+            if (distanceFromClearAll < minimumDistance) {
+                int distanceDifference = minimumDistance - distanceFromClearAll;
+                clearAllTotalTranslationX += mIsRtl ? -distanceDifference : distanceDifference;
+            }
+        }
+
+        mClearAllButton.setGridTranslationPrimary(
+                clearAllTotalTranslationX - snappedTaskGridTranslationX);
+        mClearAllButton.setGridScrollOffset(
+                mIsRtl ? mLastComputedTaskSize.left - mLastComputedGridSize.left
+                        : mLastComputedTaskSize.right - mLastComputedGridSize.right);
+
+        setGridProgress(mGridProgress);
+    }
+
+    private boolean isSameGridRow(TaskView taskView1, TaskView taskView2) {
+        if (taskView1 == null || taskView2 == null) {
+            return false;
+        }
+        int taskId1 = taskView1.getTask().key.id;
+        int taskId2 = taskView2.getTask().key.id;
+        if (taskId1 == mFocusedTaskId || taskId2 == mFocusedTaskId) {
+            return false;
+        }
+        return (mTopRowIdSet.contains(taskId1) && mTopRowIdSet.contains(taskId2)) || (
+                !mTopRowIdSet.contains(taskId1) && !mTopRowIdSet.contains(taskId2));
+    }
+
+    /**
+     * Moves TaskView and ClearAllButton between carousel and 2 row grid.
+     *
+     * @param gridProgress 0 = carousel; 1 = 2 row grid.
+     */
+    private void setGridProgress(float gridProgress) {
+        int taskCount = getTaskViewCount();
+        if (taskCount == 0) {
+            return;
+        }
+
+        mGridProgress = gridProgress;
+
+        for (int i = 0; i < taskCount; i++) {
+            getTaskViewAt(i).setGridProgress(gridProgress);
+        }
+        mClearAllButton.setGridProgress(gridProgress);
+    }
+
+    private void enableLayoutTransitions() {
+        if (mLayoutTransition == null) {
+            mLayoutTransition = new LayoutTransition();
+            mLayoutTransition.enableTransitionType(LayoutTransition.APPEARING);
+            mLayoutTransition.setDuration(ADDITION_TASK_DURATION);
+            mLayoutTransition.setStartDelay(LayoutTransition.APPEARING, 0);
+
+            mLayoutTransition.addTransitionListener(new TransitionListener() {
+                @Override
+                public void startTransition(LayoutTransition transition, ViewGroup viewGroup,
+                    View view, int i) {
+                }
+
+                @Override
+                public void endTransition(LayoutTransition transition, ViewGroup viewGroup,
+                    View view, int i) {
+                    // When the unpinned task is added, snap to first page and disable transitions
+                    if (view instanceof TaskView) {
+                        snapToPage(0);
+                        setLayoutTransition(null);
+                    }
+
+                }
+            });
+        }
+        setLayoutTransition(mLayoutTransition);
+    }
+
+    public void setSwipeDownShouldLaunchApp(boolean swipeDownShouldLaunchApp) {
+        mSwipeDownShouldLaunchApp = swipeDownShouldLaunchApp;
+    }
+
+    public boolean shouldSwipeDownLaunchApp() {
+        return mSwipeDownShouldLaunchApp;
+    }
+
+    public void setIgnoreResetTask(int taskId) {
+        mIgnoreResetTaskId = taskId;
+    }
+
+    public void clearIgnoreResetTask(int taskId) {
+        if (mIgnoreResetTaskId == taskId) {
+            mIgnoreResetTaskId = -1;
+        }
+    }
+
+    private void addDismissedTaskAnimations(TaskView taskView, long duration,
+            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();
+
+        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);
+
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
+                && taskView.isRunningTask()) {
+            anim.addOnFrameCallback(() -> {
+                mLiveTileTaskViewSimulator.taskSecondaryTranslation.value =
+                        mOrientationHandler.getSecondaryValue(
+                                taskView.getTranslationX(),
+                                taskView.getTranslationY());
+                redrawLiveTile();
+            });
+        }
+    }
+
+    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
+            boolean shouldRemoveTask, long duration) {
+        if (mPendingAnimation != null) {
+            mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
+        }
+        PendingAnimation anim = new PendingAnimation(duration);
+
+        int count = getPageCount();
+        if (count == 0) {
+            return anim;
+        }
+
+        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;
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            if (child == taskView) {
+                if (animateTaskView) {
+                    addDismissedTaskAnimations(taskView, duration, anim);
+                }
+            } 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:
+                // - In RTL, add page offset to all pages, since we want pages to move to the right
+                // Additionally, add a page offset if:
+                // - 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) {
+                    int lastPage = taskCount - 1;
+                    if (mCurrentPage == lastPage) {
+                        offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
+                    }
+                } else {
+                    // Dragging an adjacent page.
+                    int negativeAdjacent = mCurrentPage - 1; // (Right in RTL, left in LTR)
+                    if (draggedIndex == negativeAdjacent) {
+                        offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage;
+                    }
+                }
+
+                int scrollDiff = newScroll[i] - oldScroll[i] + offset;
+                if (scrollDiff != 0) {
+                    FloatProperty translationProperty = child instanceof TaskView
+                            ? ((TaskView) child).getPrimaryDismissTranslationProperty()
+                            : mOrientationHandler.getPrimaryViewTranslate();
+
+                    float additionalDismissDuration =
+                            ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET * Math.abs(
+                                    i - draggedIndex);
+                    anim.setFloat(child, translationProperty, scrollDiff, clampToProgress(LINEAR,
+                            Utilities.boundToRange(INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+                                    + additionalDismissDuration, 0f, 1f), 1));
+                    if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
+                            && child instanceof TaskView
+                            && ((TaskView) child).isRunningTask()) {
+                        anim.addOnFrameCallback(() -> {
+                            mLiveTileTaskViewSimulator.taskPrimaryTranslation.value =
+                                    mOrientationHandler.getPrimaryValue(child.getTranslationX(),
+                                            child.getTranslationY());
+                            redrawLiveTile();
+                        });
+                    }
+                    needsCurveUpdates = true;
+                }
+            } else if (child instanceof TaskView) {
+                // 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));
+                }
+            }
+        }
+
+        if (needsCurveUpdates) {
+            anim.addOnFrameCallback(this::updateCurveProperties);
+        }
+
+        // Add a tiny bit of translation Z, so that it draws on top of other views
+        if (animateTaskView) {
+            taskView.setTranslationZ(0.1f);
+        }
+
+        mPendingAnimation = anim;
+        mPendingAnimation.addEndListener(new Consumer<Boolean>() {
+            @Override
+            public void accept(Boolean success) {
+                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
+                        && taskView.isRunningTask() && success) {
+                    finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+                            () -> onEnd(success));
+                } else {
+                    onEnd(success);
+                }
+            }
+
+            @SuppressWarnings("WrongCall")
+            private void onEnd(boolean success) {
+                if (success) {
+                    if (shouldRemoveTask) {
+                        if (taskView.getTask() != null) {
+                            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && taskView.isRunningTask()) {
+                                finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+                                        () -> removeTaskInternal(taskView));
+                            } else {
+                                removeTaskInternal(taskView);
+                            }
+                            mActivity.getStatsLogManager().logger()
+                                    .withItemInfo(taskView.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);
+
+                    if (getTaskViewCount() == 0) {
+                        removeViewInLayout(mClearAllButton);
+                        startHome();
+                    } else {
+                        snapToPageImmediately(pageToSnapTo);
+                        dispatchScrollChanged();
+                        // Grid got messed up, reapply.
+                        updateGridProperties(true);
+                        if (showAsGrid() && getFocusedTaskView() == null
+                                && mActionsView.getVisibilityAlpha().getValue() == 1) {
+                            animateActionsViewOut();
+                        }
+                    }
+                    // Update the layout synchronously so that the position of next view is
+                    // immediately available.
+                    onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
+                }
+                onDismissAnimationEnds();
+                mPendingAnimation = null;
+            }
+        });
+        return anim;
+    }
+
+    private void removeTaskInternal(TaskView taskView) {
+        UI_HELPER_EXECUTOR.getHandler().postDelayed(() ->
+                        ActivityManagerWrapper.getInstance().removeTask(
+                                taskView.getTask().key.id),
+                REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
+    }
+
+    /**
+     * @return {@code true} if one of the task thumbnails would intersect/overlap with the
+     *         {@link #mSplitPlaceholderView}
+     */
+    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;
+    }
+
+    protected void onDismissAnimationEnds() {
+    }
+
+    public PendingAnimation createAllTasksDismissAnimation(long duration) {
+        if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
+            throw new IllegalStateException("Another pending animation is still running");
+        }
+        PendingAnimation anim = new PendingAnimation(duration);
+
+        int count = getTaskViewCount();
+        for (int i = 0; i < count; i++) {
+            addDismissedTaskAnimations(getTaskViewAt(i), duration, anim);
+        }
+
+        mPendingAnimation = anim;
+        mPendingAnimation.addEndListener(isSuccess -> {
+            if (isSuccess) {
+                // Remove all the task views now
+                finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
+                    UI_HELPER_EXECUTOR.getHandler().postDelayed(
+                            ActivityManagerWrapper.getInstance()::removeAllRecentTasks,
+                            REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
+                    removeTasksViewsAndClearAllButton();
+                    startHome();
+                });
+            }
+            mPendingAnimation = null;
+        });
+        return anim;
+    }
+
+    private boolean snapToPageRelative(int pageCount, int delta, boolean cycle) {
+        if (pageCount == 0) {
+            return false;
+        }
+        final int newPageUnbound = getNextPage() + delta;
+        if (!cycle && (newPageUnbound < 0 || newPageUnbound >= pageCount)) {
+            return false;
+        }
+        snapToPage((newPageUnbound + pageCount) % pageCount);
+        getChildAt(getNextPage()).requestFocus();
+        return true;
+    }
+
+    private void runDismissAnimation(PendingAnimation pendingAnim) {
+        AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
+        controller.dispatchOnStart();
+        controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
+        controller.start();
+    }
+
+    @UiThread
+    private void dismissTask(int taskId) {
+        TaskView taskView = getTaskView(taskId);
+        if (taskView == null) {
+            return;
+        }
+        dismissTask(taskView, true /* animate */, false /* removeTask */);
+    }
+
+    public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
+        runDismissAnimation(createTaskDismissAnimation(taskView, animateTaskView, removeTask,
+                DISMISS_TASK_DURATION));
+    }
+
+    @SuppressWarnings("unused")
+    private void dismissAllTasks(View view) {
+        runDismissAnimation(createAllTasksDismissAnimation(DISMISS_TASK_DURATION));
+        mActivity.getStatsLogManager().logger().log(LAUNCHER_TASK_CLEAR_ALL);
+    }
+
+    private void dismissCurrentTask() {
+        TaskView taskView = getNextPageTaskView();
+        if (taskView != null) {
+            dismissTask(taskView, true /*animateTaskView*/, true /*removeTask*/);
+        }
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            switch (event.getKeyCode()) {
+                case KeyEvent.KEYCODE_TAB:
+                    return snapToPageRelative(getTaskViewCount(), event.isShiftPressed() ? -1 : 1,
+                            event.isAltPressed() /* cycle */);
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                    return snapToPageRelative(getPageCount(), mIsRtl ? -1 : 1, false /* cycle */);
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                    return snapToPageRelative(getPageCount(), mIsRtl ? 1 : -1, false /* cycle */);
+                case KeyEvent.KEYCODE_DEL:
+                case KeyEvent.KEYCODE_FORWARD_DEL:
+                    dismissCurrentTask();
+                    return true;
+                case KeyEvent.KEYCODE_NUMPAD_DOT:
+                    if (event.isAltPressed()) {
+                        // Numpad DEL pressed while holding Alt.
+                        dismissCurrentTask();
+                        return true;
+                    }
+            }
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction,
+            @Nullable Rect previouslyFocusedRect) {
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+        if (gainFocus && getChildCount() > 0) {
+            switch (direction) {
+                case FOCUS_FORWARD:
+                    setCurrentPage(0);
+                    break;
+                case FOCUS_BACKWARD:
+                case FOCUS_RIGHT:
+                case FOCUS_LEFT:
+                    setCurrentPage(getChildCount() - 1);
+                    break;
+            }
+        }
+    }
+
+    public float getContentAlpha() {
+        return mContentAlpha;
+    }
+
+    public void setContentAlpha(float alpha) {
+        if (alpha == mContentAlpha) {
+            return;
+        }
+        alpha = Utilities.boundToRange(alpha, 0, 1);
+        mContentAlpha = alpha;
+        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
+            TaskView child = getTaskViewAt(i);
+            if (!mRunningTaskTileHidden || child.getTask().key.id != mRunningTaskId) {
+                child.setStableAlpha(alpha);
+            }
+        }
+        mClearAllButton.setContentAlpha(mContentAlpha);
+        int alphaInt = Math.round(alpha * 255);
+        mEmptyMessagePaint.setAlpha(alphaInt);
+        mEmptyIcon.setAlpha(alphaInt);
+        mActionsView.getContentAlpha().setValue(mContentAlpha);
+
+        if (alpha > 0) {
+            setVisibility(VISIBLE);
+        } else if (!mFreezeViewVisibility) {
+            setVisibility(INVISIBLE);
+        }
+    }
+
+    /**
+     * Freezes the view visibility change. When frozen, the view will not change its visibility
+     * to gone due to alpha changes.
+     */
+    public void setFreezeViewVisibility(boolean freezeViewVisibility) {
+        if (mFreezeViewVisibility != freezeViewVisibility) {
+            mFreezeViewVisibility = freezeViewVisibility;
+            if (!mFreezeViewVisibility) {
+                setVisibility(mContentAlpha > 0 ? VISIBLE : INVISIBLE);
+            }
+        }
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+        if (mActionsView != null) {
+            mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE);
+            if (visibility != VISIBLE) {
+                mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
+            }
+        }
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateRecentsRotation();
+    }
+
+    /**
+     * Updates {@link RecentsOrientedState}'s cached RecentsView rotation.
+     */
+    public void updateRecentsRotation() {
+        final int rotation = mActivity.getDisplay().getRotation();
+        mOrientationState.setRecentsRotation(rotation);
+    }
+
+    public void setLayoutRotation(int touchRotation, int displayRotation) {
+        if (mOrientationState.update(touchRotation, displayRotation)) {
+            updateOrientationHandler();
+        }
+    }
+
+    public RecentsOrientedState getPagedViewOrientedState() {
+        return mOrientationState;
+    }
+
+    public PagedOrientationHandler getPagedOrientationHandler() {
+        return mOrientationHandler;
+    }
+
+    @Nullable
+    public TaskView getNextTaskView() {
+        return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() + 1);
+    }
+
+    @Nullable
+    public TaskView getCurrentPageTaskView() {
+        return getTaskViewAtByAbsoluteIndex(getCurrentPage());
+    }
+
+    @Nullable
+    public TaskView getNextPageTaskView() {
+        return getTaskViewAtByAbsoluteIndex(getNextPage());
+    }
+
+    @Nullable
+    public TaskView getTaskViewNearestToCenterOfScreen() {
+        return getTaskViewAtByAbsoluteIndex(getPageNearestToCenterOfScreen());
+    }
+
+    /**
+     * Returns null instead of indexOutOfBoundsError when index is not in range
+     */
+    @Nullable
+    public TaskView getTaskViewAt(int index) {
+        return getTaskViewAtByAbsoluteIndex(index + mTaskViewStartIndex);
+    }
+
+    @Nullable
+    private TaskView getTaskViewAtByAbsoluteIndex(int index) {
+        if (index < getChildCount() && index >= 0) {
+            View child = getChildAt(index);
+            return child instanceof TaskView ? (TaskView) child : null;
+        }
+        return null;
+    }
+
+    public void setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener listener) {
+        mOnEmptyMessageUpdatedListener = listener;
+    }
+
+    public void updateEmptyMessage() {
+        boolean isEmpty = getTaskViewCount() == 0;
+        boolean hasSizeChanged = mLastMeasureSize.x != getWidth()
+                || mLastMeasureSize.y != getHeight();
+        if (isEmpty == mShowEmptyMessage && !hasSizeChanged) {
+            return;
+        }
+        setContentDescription(isEmpty ? mEmptyMessage : "");
+        mShowEmptyMessage = isEmpty;
+        updateEmptyStateUi(hasSizeChanged);
+        invalidate();
+
+        if (mOnEmptyMessageUpdatedListener != null) {
+            mOnEmptyMessageUpdatedListener.onEmptyMessageUpdated(mShowEmptyMessage);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        updateEmptyStateUi(changed);
+
+        // Update the pivots such that when the task is scaled, it fills the full page
+        getTaskSize(mTempRect);
+        getPagedViewOrientedState().getFullScreenScaleAndPivot(mTempRect,
+                mActivity.getDeviceProfile(), mTempPointF);
+        setPivotX(mTempPointF.x);
+        setPivotY(mTempPointF.y);
+        setTaskModalness(mTaskModalness);
+        mLastComputedTaskStartPushOutDistance = null;
+        mLastComputedTaskEndPushOutDistance = null;
+        updatePageOffsets();
+        setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
+                : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+    }
+
+    private void updatePageOffsets() {
+        float offset = mAdjacentPageHorizontalOffset;
+        float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
+        int count = getChildCount();
+
+        TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
+                ? null : getTaskView(mRunningTaskId);
+        int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
+        int modalMidpoint = getCurrentPage();
+
+        float midpointOffsetSize = 0;
+        float leftOffsetSize = midpoint - 1 >= 0
+                ? getHorizontalOffsetSize(midpoint - 1, midpoint, offset)
+                : 0;
+        float rightOffsetSize = midpoint + 1 < count
+                ? getHorizontalOffsetSize(midpoint + 1, midpoint, offset)
+                : 0;
+
+        boolean showAsGrid = showAsGrid();
+        float modalMidpointOffsetSize = 0;
+        float modalLeftOffsetSize = 0;
+        float modalRightOffsetSize = 0;
+        float gridOffsetSize = 0;
+
+        if (showAsGrid) {
+            // In grid, we only focus the task on the side. The reference index used for offset
+            // calculation is the task directly next to the focus task in the grid.
+            int referenceIndex = modalMidpoint == 0 ? 1 : 0;
+            gridOffsetSize = referenceIndex < count
+                    ? getHorizontalOffsetSize(referenceIndex, modalMidpoint, modalOffset)
+                    : 0;
+        } else {
+            modalLeftOffsetSize = modalMidpoint - 1 >= 0
+                    ? getHorizontalOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset)
+                    : 0;
+            modalRightOffsetSize = modalMidpoint + 1 < count
+                    ? getHorizontalOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset)
+                    : 0;
+        }
+
+        for (int i = 0; i < count; i++) {
+            float translation = i == midpoint
+                    ? midpointOffsetSize
+                    : i < midpoint
+                            ? leftOffsetSize
+                            : rightOffsetSize;
+            float modalTranslation = i == modalMidpoint
+                    ? modalMidpointOffsetSize
+                    : showAsGrid
+                            ? gridOffsetSize
+                            : i < modalMidpoint ? modalLeftOffsetSize : modalRightOffsetSize;
+            float totalTranslation = translation + modalTranslation;
+            View child = getChildAt(i);
+            FloatProperty translationProperty = child instanceof TaskView
+                    ? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
+                    : mOrientationHandler.getPrimaryViewTranslate();
+            translationProperty.set(child, totalTranslation);
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
+                    && i == getRunningTaskIndex()) {
+                mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = totalTranslation;
+                redrawLiveTile();
+            }
+        }
+        updateCurveProperties();
+    }
+
+    /**
+     * Computes the child position with persistent translation considered (see
+     * {@link TaskView#getPersistentTranslationX()}.
+     */
+    private void getPersistentChildPosition(int childIndex, int midPointScroll, RectF outRect) {
+        View child = getChildAt(childIndex);
+        outRect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom());
+        if (child instanceof TaskView) {
+            TaskView taskView = (TaskView) child;
+            outRect.offset(taskView.getPersistentTranslationX(),
+                    taskView.getPersistentTranslationY());
+            outRect.top += mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+        }
+        outRect.offset(mOrientationHandler.getPrimaryValue(-midPointScroll, 0),
+                mOrientationHandler.getSecondaryValue(-midPointScroll, 0));
+    }
+
+    /**
+     * Computes the distance to offset the given child such that it is completely offscreen when
+     * translating away from the given midpoint.
+     * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
+     */
+    private float getHorizontalOffsetSize(int childIndex, int midpointIndex, float offsetProgress) {
+        if (offsetProgress == 0) {
+            // Don't bother calculating everything below if we won't offset anyway.
+            return 0;
+        }
+
+        // First, get the position of the task relative to the midpoint. If there is no midpoint
+        // then we just use the normal (centered) task position.
+        RectF taskPosition = mTempRectF;
+        // Whether the task should be shifted to start direction (i.e. left edge for portrait, top
+        // edge for landscape/seascape).
+        boolean isStartShift;
+        if (midpointIndex > -1) {
+            // When there is a midpoint reference task, adjacent tasks have less distance to travel
+            // to reach offscreen. Offset the task position to the task's starting point.
+            int midpointScroll = getScrollForPage(midpointIndex);
+            getPersistentChildPosition(midpointIndex, midpointScroll, taskPosition);
+            float midpointStart = mOrientationHandler.getStart(taskPosition);
+
+            getPersistentChildPosition(childIndex, midpointScroll, taskPosition);
+            // Assume child does not overlap with midPointChild.
+            isStartShift = mOrientationHandler.getStart(taskPosition) < midpointStart;
+        } else {
+            // Position the task at scroll position.
+            getPersistentChildPosition(childIndex, getScrollForPage(childIndex), taskPosition);
+            isStartShift = mIsRtl;
+        }
+
+        // Next, calculate the distance to move the task off screen. We also need to account for
+        // RecentsView scale, because it moves tasks based on its pivot. To do this, we move the
+        // task position to where it would be offscreen at scale = 1 (computed above), then we
+        // apply the scale via getMatrix() to determine how much that moves the task from its
+        // desired position, and adjust the computed distance accordingly.
+        float distanceToOffscreen;
+        if (isStartShift) {
+            float desiredStart = -mOrientationHandler.getPrimarySize(taskPosition);
+            distanceToOffscreen = -mOrientationHandler.getEnd(taskPosition);
+            if (mLastComputedTaskStartPushOutDistance == null) {
+                taskPosition.offsetTo(
+                        mOrientationHandler.getPrimaryValue(desiredStart, 0f),
+                        mOrientationHandler.getSecondaryValue(desiredStart, 0f));
+                getMatrix().mapRect(taskPosition);
+                mLastComputedTaskStartPushOutDistance = mOrientationHandler.getEnd(taskPosition)
+                        / mOrientationHandler.getPrimaryScale(this);
+            }
+            distanceToOffscreen -= mLastComputedTaskStartPushOutDistance;
+        } else {
+            float desiredStart = mOrientationHandler.getPrimarySize(this);
+            distanceToOffscreen = desiredStart - mOrientationHandler.getStart(taskPosition);
+            if (mLastComputedTaskEndPushOutDistance == null) {
+                taskPosition.offsetTo(
+                        mOrientationHandler.getPrimaryValue(desiredStart, 0f),
+                        mOrientationHandler.getSecondaryValue(desiredStart, 0f));
+                getMatrix().mapRect(taskPosition);
+                mLastComputedTaskEndPushOutDistance = (mOrientationHandler.getStart(taskPosition)
+                        - desiredStart) / mOrientationHandler.getPrimaryScale(this);
+            }
+            distanceToOffscreen -= mLastComputedTaskEndPushOutDistance;
+        }
+        return distanceToOffscreen * offsetProgress;
+    }
+
+    protected void setTaskViewsResistanceTranslation(float translation) {
+        mTaskViewsSecondaryTranslation = translation;
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView task = getTaskViewAt(i);
+            task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
+        }
+        mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
+    }
+
+    protected void setTaskViewsPrimarySplitTranslation(float translation) {
+        mTaskViewsPrimarySplitTranslation = translation;
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView task = getTaskViewAt(i);
+            task.getPrimarySplitTranslationProperty().set(task, translation);
+        }
+    }
+
+    protected void setTaskViewsSecondarySplitTranslation(float translation) {
+        mTaskViewsSecondarySplitTranslation = translation;
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView task = getTaskViewAt(i);
+            task.getSecondarySplitTranslationProperty().set(task, translation);
+        }
+    }
+
+    /**
+     * Resets the visuals when exit modal state.
+     */
+    public void resetModalVisuals() {
+        TaskView taskView = getCurrentPageTaskView();
+        if (taskView != null) {
+            taskView.getThumbnail().getTaskOverlay().resetModalVisuals();
+        }
+    }
+
+    public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
+        mSplitHiddenTaskView = taskView;
+        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
+        Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
+                taskView.getBottom());
+        splitController.setInitialTaskSelect(taskView, splitPositionOption, initialBounds);
+        mSplitHiddenTaskViewIndex = indexOfChild(taskView);
+        mSplitPlaceholderView.setLayoutParams(
+                splitController.getLayoutParamsForActivePosition(getResources(),
+                        mActivity.getDeviceProfile()));
+        mSplitPlaceholderView.setIcon(taskView.getIconView());
+    }
+
+    public PendingAnimation createSplitSelectInitAnimation() {
+        int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
+        return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration);
+    }
+
+    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();
+        int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
+        PendingAnimation pendingAnim = new PendingAnimation(duration);
+        if (!animate) {
+            resetFromSplitSelectionState();
+            return pendingAnim;
+        }
+
+        addViewInLayout(mSplitHiddenTaskView, mSplitHiddenTaskViewIndex,
+                mSplitHiddenTaskView.getLayoutParams());
+        mSplitHiddenTaskView.setAlpha(0);
+        int[] oldScroll = new int[getChildCount()];
+        getPageScrolls(oldScroll, false,
+                view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
+
+        int[] newScroll = new int[getChildCount()];
+        getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
+
+        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;
+    }
+
+    private void resetFromSplitSelectionState() {
+        mSplitHiddenTaskView.setTranslationY(0);
+        if (!showAsGrid()) {
+            // TODO(b/186800707)
+            int pageToSnapTo = mCurrentPage;
+            if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
+                pageToSnapTo += 1;
+            } else {
+                pageToSnapTo = mSplitHiddenTaskViewIndex;
+            }
+            snapToPageImmediately(pageToSnapTo);
+        }
+        onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
+        resetTaskVisuals();
+        mSplitHiddenTaskView = null;
+        mSplitHiddenTaskViewIndex = -1;
+    }
+
+    private void updateDeadZoneRects() {
+        // Get the deadzone rect surrounding the clear all button to not dismiss overview to home
+        mClearAllButtonDeadZoneRect.setEmpty();
+        if (mClearAllButton.getWidth() > 0) {
+            int verticalMargin = getResources()
+                    .getDimensionPixelSize(R.dimen.recents_clear_all_deadzone_vertical_margin);
+            mClearAllButton.getHitRect(mClearAllButtonDeadZoneRect);
+            mClearAllButtonDeadZoneRect.inset(-getPaddingRight() / 2, -verticalMargin);
+        }
+
+        // Get the deadzone rect between the task views
+        mTaskViewDeadZoneRect.setEmpty();
+        int count = getTaskViewCount();
+        if (count > 0) {
+            final View taskView = getTaskViewAt(0);
+            getTaskViewAt(count - 1).getHitRect(mTaskViewDeadZoneRect);
+            mTaskViewDeadZoneRect.union(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
+                    taskView.getBottom());
+        }
+    }
+
+    private void updateEmptyStateUi(boolean sizeChanged) {
+        boolean hasValidSize = getWidth() > 0 && getHeight() > 0;
+        if (sizeChanged && hasValidSize) {
+            mEmptyTextLayout = null;
+            mLastMeasureSize.set(getWidth(), getHeight());
+        }
+
+        if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
+            int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
+            mEmptyTextLayout = StaticLayout.Builder.obtain(mEmptyMessage, 0, mEmptyMessage.length(),
+                    mEmptyMessagePaint, availableWidth)
+                    .setAlignment(Layout.Alignment.ALIGN_CENTER)
+                    .build();
+            int totalHeight = mEmptyTextLayout.getHeight()
+                    + mEmptyMessagePadding + mEmptyIcon.getIntrinsicHeight();
+
+            int top = (mLastMeasureSize.y - totalHeight) / 2;
+            int left = (mLastMeasureSize.x - mEmptyIcon.getIntrinsicWidth()) / 2;
+            mEmptyIcon.setBounds(left, top, left + mEmptyIcon.getIntrinsicWidth(),
+                    top + mEmptyIcon.getIntrinsicHeight());
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || (mShowEmptyMessage && who == mEmptyIcon);
+    }
+
+    protected void maybeDrawEmptyMessage(Canvas canvas) {
+        if (mShowEmptyMessage && mEmptyTextLayout != null) {
+            // Offset to center in the visible (non-padded) part of RecentsView
+            mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
+                    mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
+            canvas.save();
+            canvas.translate(getScrollX() + (mTempRect.left - mTempRect.right) / 2,
+                    (mTempRect.top - mTempRect.bottom) / 2);
+            mEmptyIcon.draw(canvas);
+            canvas.translate(mEmptyMessagePadding,
+                    mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
+            mEmptyTextLayout.draw(canvas);
+            canvas.restore();
+        }
+    }
+
+    /**
+     * Animate adjacent tasks off screen while scaling up.
+     *
+     * If launching one of the adjacent tasks, parallax the center task and other adjacent task
+     * to the right.
+     */
+    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
+        AnimatorSet anim = new AnimatorSet();
+
+        int taskIndex = indexOfChild(tv);
+        int centerTaskIndex = getCurrentPage();
+        boolean launchingCenterTask = taskIndex == centerTaskIndex;
+
+        float toScale = getMaxScaleForFullScreen();
+        RecentsView recentsView = tv.getRecentsView();
+        if (launchingCenterTask) {
+            anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale));
+            anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1));
+        } else {
+            // We are launching an adjacent task, so parallax the center and other adjacent task.
+            float displacementX = tv.getWidth() * (toScale - 1f);
+            float primaryTranslation = mIsRtl ? -displacementX : displacementX;
+            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));
+            }
+
+            int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
+            if (otherAdjacentTaskIndex >= 0 && otherAdjacentTaskIndex < getPageCount()) {
+                PropertyValuesHolder[] properties = new PropertyValuesHolder[3];
+                properties[0] = PropertyValuesHolder.ofFloat(
+                        mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation);
+                properties[1] = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+                properties[2] = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
+
+                anim.play(ObjectAnimator.ofPropertyValuesHolder(getPageAt(otherAdjacentTaskIndex),
+                        properties));
+            }
+        }
+        return anim;
+    }
+
+    /**
+     * Returns the scale up required on the view, so that it coves the screen completely
+     */
+    public float getMaxScaleForFullScreen() {
+        getTaskSize(mTempRect);
+        return getPagedViewOrientedState().getFullScreenScaleAndPivot(
+                mTempRect, mActivity.getDeviceProfile(), mTempPointF);
+    }
+
+    public PendingAnimation createTaskLaunchAnimation(
+            TaskView tv, long duration, Interpolator interpolator) {
+        if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
+            throw new IllegalStateException("Another pending animation is still running");
+        }
+
+        int count = getTaskViewCount();
+        if (count == 0) {
+            return new PendingAnimation(duration);
+        }
+
+        // When swiping down from overview to tasks, ensures the snapped page's scroll maintain
+        // invariant between quick switch and overview, to ensure a smooth animation transition.
+        updateGridProperties();
+        updateScrollSynchronously();
+
+        int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
+        final boolean[] passedOverviewThreshold = new boolean[] {false};
+        ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
+        progressAnim.addUpdateListener(animator -> {
+            // Once we pass a certain threshold, update the sysui flags to match the target
+            // tasks' flags
+            if (animator.getAnimatedFraction() > UPDATE_SYSUI_FLAGS_THRESHOLD) {
+                mActivity.getSystemUiController().updateUiState(
+                        UI_STATE_FULLSCREEN_TASK, targetSysUiFlags);
+            } else {
+                mActivity.getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
+            }
+
+            // Passing the threshold from taskview to fullscreen app will vibrate
+            final boolean passed = animator.getAnimatedFraction() >=
+                    SUCCESS_TRANSITION_PROGRESS;
+            if (passed != passedOverviewThreshold[0]) {
+                passedOverviewThreshold[0] = passed;
+                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+            }
+        });
+
+        AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv);
+
+        DepthController depthController = getDepthController();
+        if (depthController != null) {
+            ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController, DEPTH,
+                    BACKGROUND_APP.getDepth(mActivity));
+            anim.play(depthAnimator);
+        }
+        anim.play(progressAnim);
+        anim.setInterpolator(interpolator);
+
+        mPendingAnimation = new PendingAnimation(duration);
+        mPendingAnimation.add(anim);
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mLiveTileTaskViewSimulator.addOverviewToAppAnim(mPendingAnimation, interpolator);
+            mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
+        }
+        mPendingAnimation.addEndListener(isSuccess -> {
+            if (isSuccess) {
+                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && tv.isRunningTask()) {
+                    finishRecentsAnimation(false /* toRecents */, null);
+                    onTaskLaunchAnimationEnd(true /* success */);
+                } else {
+                    tv.launchTask(this::onTaskLaunchAnimationEnd);
+                }
+                Task task = tv.getTask();
+                if (task != null) {
+                    mActivity.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
+                            .log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
+                }
+            } else {
+                onTaskLaunchAnimationEnd(false);
+            }
+            mPendingAnimation = null;
+        });
+        return mPendingAnimation;
+    }
+
+    protected void onTaskLaunchAnimationEnd(boolean success) {
+        if (success) {
+            resetTaskVisuals();
+        }
+    }
+
+    @Override
+    protected void notifyPageSwitchListener(int prevPage) {
+        super.notifyPageSwitchListener(prevPage);
+        loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
+        updateEnabledOverlays();
+    }
+
+    @Override
+    protected String getCurrentPageDescription() {
+        return "";
+    }
+
+    @Override
+    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
+        // Add children in reverse order
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            outChildren.add(getChildAt(i));
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        final AccessibilityNodeInfo.CollectionInfo
+                collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain(
+                1, getTaskViewCount(), false,
+                AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE);
+        info.setCollectionInfo(collectionInfo);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+
+        final int taskViewCount = getTaskViewCount();
+        event.setScrollable(taskViewCount > 0);
+
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            final int[] visibleTasks = getVisibleChildrenRange();
+            event.setFromIndex(taskViewCount - visibleTasks[1]);
+            event.setToIndex(taskViewCount - visibleTasks[0]);
+            event.setItemCount(taskViewCount);
+        }
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        // To hear position-in-list related feedback from Talkback.
+        return ListView.class.getName();
+    }
+
+    @Override
+    protected boolean isPageOrderFlipped() {
+        return true;
+    }
+
+    public void setEnableDrawingLiveTile(boolean enableDrawingLiveTile) {
+        mEnableDrawingLiveTile = enableDrawingLiveTile;
+    }
+
+    public void redrawLiveTile() {
+        if (mLiveTileParams.getTargetSet() != null) {
+            mLiveTileTaskViewSimulator.apply(mLiveTileParams);
+        }
+    }
+
+    public TaskViewSimulator getLiveTileTaskViewSimulator() {
+        return mLiveTileTaskViewSimulator;
+    }
+
+    public TransformParams getLiveTileParams() {
+        return mLiveTileParams;
+    }
+
+    // TODO: To be removed in a follow up CL
+    public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
+            RecentsAnimationTargets recentsAnimationTargets) {
+        mRecentsAnimationController = recentsAnimationController;
+        if (recentsAnimationTargets != null && recentsAnimationTargets.apps.length > 0) {
+            if (mSyncTransactionApplier != null) {
+                recentsAnimationTargets.addReleaseCheck(mSyncTransactionApplier);
+            }
+            mLiveTileTaskViewSimulator.setPreview(
+                    recentsAnimationTargets.apps[recentsAnimationTargets.apps.length - 1]);
+            mLiveTileParams.setTargetSet(recentsAnimationTargets);
+        }
+    }
+
+    public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
+        finishRecentsAnimation(toRecents, true /* shouldPip */, onFinishComplete);
+    }
+
+    public void finishRecentsAnimation(boolean toRecents, boolean shouldPip,
+            Runnable onFinishComplete) {
+        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
+            // cases where we haven't already reset it.
+            SystemUiProxy p = SystemUiProxy.INSTANCE.getNoCreate();
+            if (p != null) {
+                p.setSplitScreenMinimized(false);
+            }
+        }
+
+        if (mRecentsAnimationController == null) {
+            if (onFinishComplete != null) {
+                onFinishComplete.run();
+            }
+            return;
+        }
+
+        final boolean sendUserLeaveHint = toRecents && shouldPip;
+        if (sendUserLeaveHint) {
+            // Notify the SysUI to use fade-in animation when entering PiP from live tile.
+            final SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(getContext());
+            systemUiProxy.notifySwipeToHomeFinished();
+            systemUiProxy.setShelfHeight(true, mActivity.getDeviceProfile().hotseatBarSizePx);
+        }
+        mRecentsAnimationController.finish(toRecents, () -> {
+            if (onFinishComplete != null) {
+                onFinishComplete.run();
+            }
+            onRecentsAnimationComplete();
+        }, sendUserLeaveHint);
+    }
+
+    /**
+     * Called when a running recents animation has finished or canceled.
+     */
+    public void onRecentsAnimationComplete() {
+        // At this point, the recents animation is not running and if the animation was canceled
+        // by a display rotation then reset this state to show the screenshot
+        setRunningTaskViewShowScreenshot(true);
+        // After we finish the recents animation, the current task id should be correctly
+        // reset so that when the task is launched from Overview later, it goes through the
+        // flow of starting a new task instead of finishing recents animation to app. A
+        // typical example of this is (1) user swipes up from app to Overview (2) user
+        // taps on QSB (3) user goes back to Overview and launch the most recent task.
+        setCurrentTask(-1);
+        mRecentsAnimationController = null;
+        executeSideTaskLaunchCallback();
+    }
+
+    public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
+        if (mDisallowScrollToClearAll != disallowScrollToClearAll) {
+            mDisallowScrollToClearAll = disallowScrollToClearAll;
+            updateMinAndMaxScrollX();
+        }
+    }
+
+    /**
+     * Updates page scroll synchronously and layout child views.
+     */
+    public void updateScrollSynchronously() {
+        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);
+            }
+        }
+        return super.computeMinScroll();
+    }
+
+    @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));
+            }
+        }
+        return super.computeMaxScroll();
+    }
+
+    /**
+     * Returns page scroll of ClearAllButton.
+     */
+    public int getClearAllScroll() {
+        return getScrollForPage(indexOfChild(mClearAllButton));
+    }
+
+    @Override
+    protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren,
+            ComputePageScrollsLogic scrollLogic) {
+        int[] newPageScrolls = new int[outPageScrolls.length];
+        super.getPageScrolls(newPageScrolls, layoutChildren, scrollLogic);
+        boolean showAsFullscreen = showAsFullscreen();
+        boolean showAsGrid = showAsGrid();
+
+        // Align ClearAllButton to the left (RTL) or right (non-RTL), which is different from other
+        // TaskViews. This must be called after laying out ClearAllButton.
+        if (layoutChildren) {
+            int clearAllWidthDiff = mOrientationHandler.getPrimaryValue(mTaskWidth, mTaskHeight)
+                    - mOrientationHandler.getPrimarySize(mClearAllButton);
+            mClearAllButton.setScrollOffsetPrimary(mIsRtl ? clearAllWidthDiff : -clearAllWidthDiff);
+        }
+
+        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;
+            if (outPageScrolls[i] != pageScroll) {
+                pageScrollChanged = true;
+                outPageScrolls[i] = pageScroll;
+            }
+        }
+        return pageScrollChanged;
+    }
+
+    @Override
+    protected int getChildOffset(int index) {
+        int childOffset = super.getChildOffset(index);
+        View child = getChildAt(index);
+        if (child instanceof TaskView) {
+            childOffset += ((TaskView) child).getOffsetAdjustment(showAsFullscreen(),
+                    showAsGrid());
+        } else if (child instanceof ClearAllButton) {
+            childOffset += ((ClearAllButton) child).getOffsetAdjustment(mOverviewFullscreenEnabled,
+                    showAsGrid());
+        }
+        return childOffset;
+    }
+
+    @Override
+    protected int getChildVisibleSize(int index) {
+        final TaskView taskView = getTaskViewAtByAbsoluteIndex(index);
+        if (taskView == null) {
+            return super.getChildVisibleSize(index);
+        }
+        return (int) (super.getChildVisibleSize(index) * taskView.getSizeAdjustment(
+                showAsFullscreen()));
+    }
+
+    public ClearAllButton getClearAllButton() {
+        return mClearAllButton;
+    }
+
+    /**
+     * @return How many pixels the running task is offset on the currently laid out dominant axis.
+     */
+    public int getScrollOffset() {
+        return getScrollOffset(getRunningTaskIndex());
+    }
+
+    /**
+     * Returns how many pixels the page is offset on the currently laid out dominant axis.
+     */
+    public int getScrollOffset(int pageIndex) {
+        if (pageIndex == -1) {
+            return 0;
+        }
+
+        int overScrollShift = getOverScrollShift();
+        if (mAdjacentPageHorizontalOffset > 0) {
+            // Don't dampen the scroll (due to overscroll) if the adjacent tasks are offscreen, so
+            // that the page can move freely given there's no visual indication why it shouldn't.
+            overScrollShift = (int) Utilities.mapRange(mAdjacentPageHorizontalOffset,
+                    overScrollShift, getUndampedOverScrollShift());
+        }
+        return getScrollForPage(pageIndex) - mOrientationHandler.getPrimaryScroll(this)
+                + overScrollShift;
+    }
+
+    /**
+     * Returns how many pixels the task is offset on the currently laid out secondary axis
+     * according to {@link #mGridProgress}.
+     */
+    public float getGridTranslationSecondary(int pageIndex) {
+        TaskView taskView = getTaskViewAtByAbsoluteIndex(pageIndex);
+        if (taskView == null) {
+            return 0;
+        }
+
+        return mOrientationHandler.getSecondaryValue(taskView.getGridTranslationX(),
+                taskView.getGridTranslationY());
+    }
+
+    public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
+        float degreesRotated;
+        if (navbarRotation == 0) {
+            degreesRotated = mOrientationHandler.getDegreesRotated();
+        } else {
+            degreesRotated = -navbarRotation;
+        }
+        if (degreesRotated == 0) {
+            return super::onTouchEvent;
+        }
+
+        // At this point the event coordinates have already been transformed, so we need to
+        // undo that transformation since PagedView also accommodates for the transformation via
+        // PagedOrientationHandler
+        return e -> {
+            if (navbarRotation != 0
+                    && mOrientationState.isMultipleOrientationSupportedByDevice()
+                    && !mOrientationState.getOrientationHandler().isLayoutNaturalToLauncher()) {
+                mOrientationState.flipVertical(e);
+                super.onTouchEvent(e);
+                mOrientationState.flipVertical(e);
+                return;
+            }
+            mOrientationState.transformEvent(-degreesRotated, e, true);
+            super.onTouchEvent(e);
+            mOrientationState.transformEvent(-degreesRotated, e, false);
+        };
+    }
+
+    private void updateEnabledOverlays() {
+        int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
+        int taskCount = getTaskViewCount();
+        for (int i = mTaskViewStartIndex; i < mTaskViewStartIndex + taskCount; i++) {
+            getTaskViewAtByAbsoluteIndex(i).setOverlayEnabled(i == overlayEnabledPage);
+        }
+    }
+
+    public void setOverlayEnabled(boolean overlayEnabled) {
+        if (mOverlayEnabled != overlayEnabled) {
+            mOverlayEnabled = overlayEnabled;
+            updateEnabledOverlays();
+        }
+    }
+
+    public void setOverviewGridEnabled(boolean overviewGridEnabled) {
+        if (mOverviewGridEnabled != overviewGridEnabled) {
+            mOverviewGridEnabled = overviewGridEnabled;
+            // Request layout to ensure scroll position is recalculated with updated mGridProgress.
+            requestLayout();
+        }
+    }
+
+    public void setOverviewFullscreenEnabled(boolean overviewFullscreenEnabled) {
+        if (mOverviewFullscreenEnabled != overviewFullscreenEnabled) {
+            mOverviewFullscreenEnabled = overviewFullscreenEnabled;
+            // Request layout to ensure scroll position is recalculated with updated
+            // mFullscreenProgress.
+            requestLayout();
+        }
+    }
+
+    /**
+     * Switch the current running task view to static snapshot mode,
+     * capturing the snapshot at the same time.
+     */
+    public void switchToScreenshot(Runnable onFinishRunnable) {
+        if (mRecentsAnimationController == null) {
+            if (onFinishRunnable != null) {
+                onFinishRunnable.run();
+            }
+            return;
+        }
+        switchToScreenshot(mRunningTaskId == -1 ? null
+                : mRecentsAnimationController.screenshotTask(mRunningTaskId), onFinishRunnable);
+    }
+
+    /**
+     * Switch the current running task view to static snapshot mode, using the
+     * provided thumbnail data as the snapshot.
+     */
+    public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
+        TaskView taskView = getRunningTaskView();
+        if (taskView != null) {
+            taskView.setShowScreenshot(true);
+            if (thumbnailData != null) {
+                taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
+            } else {
+                taskView.getThumbnail().refresh();
+            }
+            ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
+        } else {
+            onFinishRunnable.run();
+        }
+    }
+
+    /**
+     * 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.
+     */
+    private void setTaskModalness(float modalness) {
+        mTaskModalness = modalness;
+        updatePageOffsets();
+        if (getCurrentPageTaskView() != null) {
+            getCurrentPageTaskView().setModalness(modalness);
+        }
+        // Only show actions view when it's modal for in-place landscape mode.
+        boolean inPlaceLandscape = !mOrientationState.canRecentsActivityRotate()
+                && mOrientationState.getTouchRotation() != ROTATION_0;
+        mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
+        mActionsView.setTaskModalness(modalness);
+    }
+
+    @Nullable
+    protected DepthController getDepthController() {
+        return null;
+    }
+
+    @Override
+    public void onSecondaryWindowBoundsChanged() {
+        // Invalidate the task view size
+        setInsets(mInsets);
+    }
+
+    /**
+     * Enables or disables modal state for RecentsView
+     * @param isModalState
+     */
+    public void setModalStateEnabled(boolean isModalState) { }
+
+    public TaskOverlayFactory getTaskOverlayFactory() {
+        return mTaskOverlayFactory;
+    }
+
+    public BaseActivityInterface getSizeStrategy() {
+        return mSizeStrategy;
+    }
+
+    /**
+     * Set all the task views to color tint scrim mode, dimming or tinting them all. Allows the
+     * tasks to be dimmed while other elements in the recents view are left alone.
+     */
+    public void showForegroundScrim(boolean show) {
+        if (!show && mColorTint == 0) {
+            if (mTintingAnimator != null) {
+                mTintingAnimator.cancel();
+                mTintingAnimator = null;
+            }
+            return;
+        }
+
+        mTintingAnimator = ObjectAnimator.ofFloat(this, COLOR_TINT, show ? 0.5f : 0f);
+        mTintingAnimator.setAutoCancel(true);
+        mTintingAnimator.start();
+    }
+
+    /** Tint the RecentsView and TaskViews in to simulate a scrim. */
+    // TODO(b/187528071): Replace this tinting with a scrim on top of RecentsView
+    private void setColorTint(float tintAmount) {
+        mColorTint = tintAmount;
+
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            getTaskViewAt(i).setColorTint(mColorTint, mTintingColor);
+        }
+
+        Drawable scrimBg = mActivity.getScrimView().getBackground();
+        if (scrimBg != null) {
+            if (tintAmount == 0f) {
+                scrimBg.setTintList(null);
+            } else {
+                scrimBg.setTintBlendMode(BlendMode.SRC_OVER);
+                scrimBg.setTint(
+                        ColorUtils.setAlphaComponent(mTintingColor, (int) (255 * tintAmount)));
+            }
+        }
+    }
+
+    private float getColorTint() {
+        return mColorTint;
+    }
+
+    /** Returns {@code true} if the overview tasks are displayed as a grid. */
+    public boolean showAsGrid() {
+        return mOverviewGridEnabled || (mCurrentGestureEndTarget != null
+                && mSizeStrategy.stateFromGestureEndTarget(
+                mCurrentGestureEndTarget).displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
+    }
+
+    private boolean showAsFullscreen() {
+        return mOverviewFullscreenEnabled
+                && mCurrentGestureEndTarget != GestureState.GestureEndTarget.RECENTS;
+    }
+
+    public boolean shouldShowOverviewActionsForState(STATE_TYPE state) {
+        return !state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())
+                || getFocusedTaskView() != null;
+    }
+
+    /**
+     * Used to register callbacks for when our empty message state changes.
+     *
+     * @see #setOnEmptyMessageUpdatedListener(OnEmptyMessageUpdatedListener)
+     * @see #updateEmptyMessage()
+     */
+    public interface OnEmptyMessageUpdatedListener {
+        /** @param isEmpty Whether RecentsView is empty (i.e. has no children) */
+        void onEmptyMessageUpdated(boolean isEmpty);
+    }
+
+    /**
+     * Adds a listener for scroll changes
+     */
+    public void addOnScrollChangedListener(OnScrollChangedListener listener) {
+        mScrollListeners.add(listener);
+    }
+
+    /**
+     * Removes a previously added scroll change listener
+     */
+    public void removeOnScrollChangedListener(OnScrollChangedListener listener) {
+        mScrollListeners.remove(listener);
+    }
+
+    /**
+     * @return Corner radius in pixel value for PiP window, which is updated via
+     *         {@link #mIPipAnimationListener}
+     */
+    public int getPipCornerRadius() {
+        return mPipCornerRadius;
+    }
+
+    @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());
+        for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
+            mScrollListeners.get(i).onScrollChanged();
+        }
+    }
+
+    private static class PinnedStackAnimationListener<T extends BaseActivity> extends
+            IPipAnimationListener.Stub {
+        private T mActivity;
+        private RecentsView mRecentsView;
+
+        public void setActivityAndRecentsView(T activity, RecentsView recentsView) {
+            mActivity = activity;
+            mRecentsView = recentsView;
+        }
+
+        @Override
+        public void onPipAnimationStarted() {
+            MAIN_EXECUTOR.execute(() -> {
+                // Needed for activities that auto-enter PiP, which will not trigger a remote
+                // animation to be created
+                if (mActivity != null) {
+                    mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
+                }
+            });
+        }
+
+        @Override
+        public void onPipCornerRadiusChanged(int cornerRadius) {
+            if (mRecentsView != null) {
+                mRecentsView.mPipCornerRadius = cornerRadius;
+            }
+        }
+    }
+
+    /** Get the color used for foreground scrimming the RecentsView for sharing. */
+    public static int getForegroundScrimDimColor(Context context) {
+        int baseColor = Themes.getAttrColor(context, R.attr.overviewScrimColor);
+        // The Black blending is temporary until we have the proper color token.
+        return ColorUtils.blendARGB(Color.BLACK, baseColor, 0.25f);
+    }
+
+    /** Get the RecentsAnimationController */
+    @Nullable
+    public RecentsAnimationController getRecentsAnimationController() {
+        return mRecentsAnimationController;
+    }
+
+    /** Update the current activity locus id to show the enabled state of Overview */
+    public void updateLocusId() {
+        String locusId = "Overview";
+
+        if (mOverviewStateEnabled && mActivity.isStarted()) {
+            locusId += "|ENABLED";
+        } else {
+            locusId += "|DISABLED";
+        }
+
+        final LocusId id = new LocusId(locusId);
+        // 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));
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
deleted file mode 100644
index 19e278b..0000000
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.views;
-
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.QUICK_SWITCH;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Path.Direction;
-import android.graphics.Path.Op;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.DeviceProfile;
-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.config.FeatureFlags;
-import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.ScrimView;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.util.LayoutUtils;
-
-/**
- * Scrim used for all-apps and shelf in Overview
- * In transposed layout, it behaves as a simple color scrim.
- * In portrait layout, it draws a rounded rect such that
- *    From normal state to overview state, the shelf just fades in and does not move
- *    From overview state to all-apps state the shelf moves up and fades in to cover the screen
- */
-public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
-        implements NavigationModeChangeListener {
-
-    // If the progress is more than this, shelf follows the finger, otherwise it moves faster to
-    // cover the whole screen
-    private static final float SCRIM_CATCHUP_THRESHOLD = 0.2f;
-
-    // Temporarily needed until android.R.attr.bottomDialogCornerRadius becomes public
-    private static final float BOTTOM_CORNER_RADIUS_RATIO = 2f;
-
-    // In transposed layout, we simply draw a flat color.
-    private boolean mDrawingFlatColor;
-
-    // For shelf mode
-    private final int mEndAlpha;
-    private final float mRadius;
-    private final int mMaxScrimAlpha;
-    private final Paint mPaint;
-    private final OnboardingPrefs mOnboardingPrefs;
-
-    // Mid point where the alpha changes
-    private int mMidAlpha;
-    private float mMidProgress;
-
-    // The progress at which the drag handle starts moving up with the shelf.
-    private float mDragHandleProgress;
-
-    private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
-    private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
-
-    private float mShiftRange;
-
-    private float mTopOffset;
-    private float mShelfTop;
-    private float mShelfTopAtThreshold;
-
-    private int mShelfColor;
-    private int mRemainingScreenColor;
-
-    private final Path mTempPath = new Path();
-    private final Path mRemainingScreenPath = new Path();
-    private boolean mRemainingScreenPathValid = false;
-
-    private Mode mSysUINavigationMode;
-    private boolean mIsTwoZoneSwipeModel;
-
-    public ShelfScrimView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mMaxScrimAlpha = Math.round(OVERVIEW.getOverviewScrimAlpha(mLauncher) * 255);
-
-        mEndAlpha = Color.alpha(mEndScrim);
-        mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
-        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mOnboardingPrefs = mLauncher.getOnboardingPrefs();
-
-        // Just assume the easiest UI for now, until we have the proper layout information.
-        mDrawingFlatColor = true;
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mRemainingScreenPathValid = false;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(getContext())
-                .addModeChangeListener(this));
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        SysUINavigationMode.INSTANCE.get(getContext()).removeModeChangeListener(this);
-    }
-
-    @Override
-    public void onNavigationModeChanged(Mode newMode) {
-        mSysUINavigationMode = newMode;
-        // Note that these interpolators are inverted because progress goes 1 to 0.
-        if (mSysUINavigationMode == Mode.NO_BUTTON) {
-            // Show the shelf more quickly before reaching overview progress.
-            mBeforeMidProgressColorInterpolator = ACCEL_2;
-            mAfterMidProgressColorInterpolator = ACCEL;
-            mIsTwoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get();
-        } else {
-            mBeforeMidProgressColorInterpolator = ACCEL;
-            mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
-            mIsTwoZoneSwipeModel = false;
-        }
-    }
-
-    @Override
-    public void reInitUi() {
-        DeviceProfile dp = mLauncher.getDeviceProfile();
-        mDrawingFlatColor = dp.isVerticalBarLayout();
-
-        if (!mDrawingFlatColor) {
-            mRemainingScreenPathValid = false;
-            mShiftRange = mLauncher.getAllAppsController().getShiftRange();
-
-            Context context = getContext();
-            if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
-                mDragHandleProgress = 1;
-                if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
-                        && SysUINavigationMode.removeShelfFromOverview(context)) {
-                    // Fade in all apps background quickly to distinguish from swiping from nav bar.
-                    mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
-                    mMidProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
-                } else {
-                    mMidAlpha = 0;
-                    mMidProgress = 1;
-                }
-            } else {
-                mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
-                mMidProgress =  OVERVIEW.getVerticalProgress(mLauncher);
-                Rect hotseatPadding = dp.getHotseatLayoutPadding();
-                int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
-                        + hotseatPadding.bottom + hotseatPadding.top;
-                float dragHandleTop =
-                        Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp));
-                mDragHandleProgress =  1 - (dragHandleTop / mShiftRange);
-            }
-            mTopOffset = dp.getInsets().top - mDragHandleSize.y;
-            mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
-        }
-        updateColors();
-        updateSysUiColors();
-        updateDragHandleAlpha();
-        invalidate();
-    }
-
-    @Override
-    public void updateColors() {
-        super.updateColors();
-        mDragHandleOffset = 0;
-        if (mDrawingFlatColor) {
-            return;
-        }
-
-        if (mProgress < mDragHandleProgress) {
-            mDragHandleOffset = mShiftRange * (mDragHandleProgress - mProgress);
-        }
-
-        if (mProgress >= SCRIM_CATCHUP_THRESHOLD) {
-            mShelfTop = mShiftRange * mProgress + mTopOffset;
-        } else {
-            mShelfTop = Utilities.mapRange(mProgress / SCRIM_CATCHUP_THRESHOLD, -mRadius,
-                    mShelfTopAtThreshold);
-        }
-
-        if (mProgress >= 1) {
-            mRemainingScreenColor = 0;
-            mShelfColor = 0;
-            LauncherState state = mLauncher.getStateManager().getState();
-            if (mSysUINavigationMode == Mode.NO_BUTTON
-                    && (state == BACKGROUND_APP || state == QUICK_SWITCH)
-                    && mLauncher.getShelfPeekAnim().isPeeking()) {
-                // Show the shelf background when peeking during swipe up.
-                mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
-            }
-        } else if (mProgress >= mMidProgress) {
-            mRemainingScreenColor = 0;
-
-            int alpha = Math.round(Utilities.mapToRange(
-                    mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator));
-            mShelfColor = setColorAlphaBound(mEndScrim, alpha);
-        } else {
-            // Note that these ranges and interpolators are inverted because progress goes 1 to 0.
-            int alpha = Math.round(
-                    Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha,
-                            (float) mMidAlpha, mAfterMidProgressColorInterpolator));
-            mShelfColor = setColorAlphaBound(mEndScrim, alpha);
-
-            int remainingScrimAlpha = Math.round(
-                    Utilities.mapToRange(mProgress, (float) 0, mMidProgress, mMaxScrimAlpha,
-                            (float) 0, LINEAR));
-            mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha);
-        }
-    }
-
-    @Override
-    protected void updateSysUiColors() {
-        if (mDrawingFlatColor) {
-            super.updateSysUiColors();
-        } else {
-            // Use a light system UI (dark icons) if all apps is behind at least half of the
-            // status bar.
-            boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
-            if (forceChange) {
-                mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
-            } else {
-                mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
-            }
-        }
-    }
-
-    @Override
-    protected boolean shouldDragHandleBeVisible() {
-        boolean needsAllAppsEdu = mIsTwoZoneSwipeModel
-                && !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT);
-        return needsAllAppsEdu || super.shouldDragHandleBeVisible();
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        drawBackground(canvas);
-        drawDragHandle(canvas);
-    }
-
-    private void drawBackground(Canvas canvas) {
-        if (mDrawingFlatColor) {
-            if (mCurrentFlatColor != 0) {
-                canvas.drawColor(mCurrentFlatColor);
-            }
-            return;
-        }
-
-        if (Color.alpha(mShelfColor) == 0) {
-            return;
-        } else if (mProgress <= 0) {
-            canvas.drawColor(mShelfColor);
-            return;
-        }
-
-        int height = getHeight();
-        int width = getWidth();
-        // Draw the scrim over the remaining screen if needed.
-        if (mRemainingScreenColor != 0) {
-            if (!mRemainingScreenPathValid) {
-                mTempPath.reset();
-                // Using a arbitrary '+10' in the bottom to avoid any left-overs at the
-                // corners due to rounding issues.
-                mTempPath.addRoundRect(0, height - mRadius, width, height + mRadius + 10,
-                        mRadius, mRadius, Direction.CW);
-                mRemainingScreenPath.reset();
-                mRemainingScreenPath.addRect(0, 0, width, height, Direction.CW);
-                mRemainingScreenPath.op(mTempPath, Op.DIFFERENCE);
-            }
-
-            float offset = height - mRadius - mShelfTop;
-            canvas.translate(0, -offset);
-            mPaint.setColor(mRemainingScreenColor);
-            canvas.drawPath(mRemainingScreenPath, mPaint);
-            canvas.translate(0, offset);
-        }
-
-        mPaint.setColor(mShelfColor);
-        canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint);
-    }
-
-    @Override
-    public float getVisualTop() {
-        return mShelfTop;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
new file mode 100644
index 0000000..bb8bc11
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -0,0 +1,68 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.Gravity;
+import android.widget.FrameLayout;
+
+import com.android.quickstep.util.SplitSelectStateController;
+
+public class SplitPlaceholderView extends FrameLayout {
+
+    public static final FloatProperty<SplitPlaceholderView> ALPHA_FLOAT =
+            new FloatProperty<SplitPlaceholderView>("SplitViewAlpha") {
+                @Override
+                public void setValue(SplitPlaceholderView splitPlaceholderView, float v) {
+                    splitPlaceholderView.setVisibility(v != 0 ? VISIBLE : GONE);
+                    splitPlaceholderView.setAlpha(v);
+                }
+
+                @Override
+                public Float get(SplitPlaceholderView splitPlaceholderView) {
+                    return splitPlaceholderView.getAlpha();
+                }
+            };
+
+    private SplitSelectStateController mSplitController;
+    private IconView mIcon;
+
+    public SplitPlaceholderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void init(SplitSelectStateController controller) {
+        this.mSplitController = controller;
+    }
+
+    public SplitSelectStateController getSplitController() {
+        return mSplitController;
+    }
+
+    public void setIcon(IconView icon) {
+        if (mIcon == null) {
+            mIcon = new IconView(getContext());
+            addView(mIcon);
+        }
+        mIcon.setDrawable(icon.getDrawable());
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(icon.getLayoutParams());
+        params.gravity = Gravity.CENTER;
+        mIcon.setLayoutParams(params);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
similarity index 64%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
rename to quickstep/src/com/android/quickstep/views/TaskMenuView.java
index fb799a8..906e854 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,80 +16,58 @@
 
 package com.android.quickstep.views;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.graphics.Outline;
+import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+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.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver.OnScrollChangedListener;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.FastBitmapDrawable;
 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.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskUtils;
-import com.android.quickstep.views.IconView.OnScaleUpdateListener;
+import com.android.quickstep.util.TaskCornerRadius;
 
 /**
  * Contains options for a recent task when long-pressing its icon.
  */
-public class TaskMenuView extends AbstractFloatingView {
+public class TaskMenuView extends AbstractFloatingView implements OnScrollChangedListener {
 
     private static final Rect sTempRect = new Rect();
 
-    private final OnScaleUpdateListener mTaskViewIconScaleListener = new OnScaleUpdateListener() {
-        @Override
-        public void onScaleUpdate(float scale) {
-            final Drawable drawable = mTaskIcon.getDrawable();
-            if (drawable instanceof FastBitmapDrawable) {
-                if (scale != ((FastBitmapDrawable) drawable).getScale()) {
-                    mMenuIconDrawable.setScale(scale);
-                }
-            }
-        }
-    };
-
-    private final OnScaleUpdateListener mMenuIconScaleListener = new OnScaleUpdateListener() {
-        @Override
-        public void onScaleUpdate(float scale) {
-            final Drawable taskViewDrawable = mTaskView.getIconView().getDrawable();
-            if (taskViewDrawable instanceof FastBitmapDrawable) {
-                final float currentScale = ((FastBitmapDrawable) taskViewDrawable).getScale();
-                if (currentScale != scale) {
-                    ((FastBitmapDrawable) taskViewDrawable).setScale(scale);
-                }
-            }
-        }
-    };
-
     private static final int REVEAL_OPEN_DURATION = 150;
     private static final int REVEAL_CLOSE_DURATION = 100;
+    private final float mTaskInsetMargin;
 
-    private final float mThumbnailTopMargin;
     private BaseDraggingActivity mActivity;
     private TextView mTaskName;
-    private IconView mTaskIcon;
     private AnimatorSet mOpenCloseAnimator;
     private TaskView mTaskView;
     private LinearLayout mOptionLayout;
-    private FastBitmapDrawable mMenuIconDrawable;
 
     public TaskMenuView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -99,14 +77,14 @@
         super(context, attrs, defStyleAttr);
 
         mActivity = BaseDraggingActivity.fromContext(context);
-        mThumbnailTopMargin = getResources().getDimension(R.dimen.task_thumbnail_top_margin);
+        setClipToOutline(true);
+        mTaskInsetMargin = getResources().getDimension(R.dimen.task_card_margin);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mTaskName = findViewById(R.id.task_name);
-        mTaskIcon = findViewById(R.id.task_icon);
         mOptionLayout = findViewById(R.id.menu_option_layout);
     }
 
@@ -133,34 +111,45 @@
     }
 
     @Override
-    public void logActionCommand(int command) {
-        // TODO
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        // Remove all scale listeners when menu is removed
-        mTaskView.getIconView().removeUpdateScaleListener(mTaskViewIconScaleListener);
-        mTaskIcon.removeUpdateScaleListener(mMenuIconScaleListener);
-    }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_TASK_MENU) != 0;
     }
 
-    public void setPosition(float x, float y, PagedOrientationHandler pagedOrientationHandler) {
-        float adjustedY = y + mThumbnailTopMargin;
+    @Override
+    public ViewOutlineProvider getOutlineProvider() {
+        return new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(),
+                        TaskCornerRadius.get(view.getContext()));
+            }
+        };
+    }
+
+    private void setPosition(float x, float y, int overscrollShift) {
+        PagedOrientationHandler pagedOrientationHandler = mTaskView.getPagedOrientationHandler();
+        // Inset due to margin
+        PointF additionalInset = pagedOrientationHandler
+                .getAdditionalInsetForTaskMenu(mTaskInsetMargin);
+        int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+
+        float adjustedY = y + taskTopMargin - additionalInset.y;
+        float adjustedX = x - additionalInset.x;
         // Changing pivot to make computations easier
         // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set,
         // which would render the X and Y position set here incorrect
         setPivotX(0);
-        setPivotY(0);
+        if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+            // In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
+            setPivotY(-taskTopMargin);
+        } else {
+            setPivotY(0);
+        }
         setRotation(pagedOrientationHandler.getDegreesRotated());
-        setX(pagedOrientationHandler.getTaskMenuX(x, mTaskView.getThumbnail()));
-        setY(pagedOrientationHandler.getTaskMenuY(adjustedY, mTaskView.getThumbnail()));
+        setX(pagedOrientationHandler.getTaskMenuX(adjustedX,
+                mTaskView.getThumbnail(), overscrollShift));
+        setY(pagedOrientationHandler.getTaskMenuY(
+                adjustedY, mTaskView.getThumbnail(), overscrollShift));
     }
 
     public void onRotationChanged() {
@@ -175,11 +164,11 @@
         }
     }
 
-    public static TaskMenuView showForTask(TaskView taskView) {
+    public static boolean showForTask(TaskView taskView) {
         BaseDraggingActivity activity = BaseDraggingActivity.fromContext(taskView.getContext());
         final TaskMenuView taskMenuView = (TaskMenuView) activity.getLayoutInflater().inflate(
                         R.layout.task_menu, activity.getDragLayer(), false);
-        return taskMenuView.populateAndShowForTask(taskView) ? taskMenuView : null;
+        return taskMenuView.populateAndShowForTask(taskView);
     }
 
     private boolean populateAndShowForTask(TaskView taskView) {
@@ -192,9 +181,17 @@
             return false;
         }
         post(this::animateOpen);
+        ((RecentsView) mActivity.getOverviewPanel()).addOnScrollChangedListener(this);
         return true;
     }
 
+    @Override
+    public void onScrollChanged() {
+        RecentsView rv = mTaskView.getRecentsView();
+        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) {
@@ -207,55 +204,64 @@
     }
 
     private void addMenuOptions(TaskView taskView) {
-        Drawable icon = taskView.getTask().icon.getConstantState().newDrawable();
-        mTaskIcon.setDrawable(icon);
-        mTaskIcon.setOnClickListener(v -> close(true));
         mTaskName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
         mTaskName.setOnClickListener(v -> close(true));
-
-        // Set the icons to match scale by listening to each other's changes
-        mMenuIconDrawable = icon instanceof FastBitmapDrawable ? (FastBitmapDrawable) icon : null;
-        taskView.getIconView().addUpdateScaleListener(mTaskViewIconScaleListener);
-        mTaskIcon.addUpdateScaleListener(mMenuIconScaleListener);
-
-        // Move the icon and text up half an icon size to lay over the TaskView
-        LinearLayout.LayoutParams params =
-                (LinearLayout.LayoutParams) mTaskIcon.getLayoutParams();
-        params.topMargin = (int) -mThumbnailTopMargin;
-        mTaskIcon.setLayoutParams(params);
-
-        TaskOverlayFactory.getEnabledShortcuts(taskView).forEach(this::addMenuOption);
+        
+        TaskOverlayFactory.getEnabledShortcuts(taskView, mActivity.getDeviceProfile())
+                .forEach(this::addMenuOption);
     }
 
     private void addMenuOption(SystemShortcut menuOption) {
-        ViewGroup menuOptionView = (ViewGroup) mActivity.getLayoutInflater().inflate(
+        LinearLayout menuOptionView = (LinearLayout) mActivity.getLayoutInflater().inflate(
                 R.layout.task_view_menu_option, this, false);
         menuOption.setIconAndLabelFor(
                 menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
         LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
-        mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp);
-        menuOptionView.setOnClickListener(menuOption);
+        mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp,
+                menuOptionView, mActivity.getDeviceProfile());
+        menuOptionView.setEnabled(menuOption.isEnabled());
+        menuOptionView.setAlpha(menuOption.isEnabled() ? 1 : 0.5f);
+        menuOptionView.setOnClickListener(view -> {
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && !menuOption.hasFinishRecentsInAction()) {
+                RecentsView recentsView = mTaskView.getRecentsView();
+                recentsView.switchToScreenshot(null,
+                        () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+                                false /* shouldPip */,
+                                () -> menuOption.onClick(view)));
+            } else {
+                menuOption.onClick(view);
+            }
+        });
         mOptionLayout.addView(menuOptionView);
     }
 
     private void orientAroundTaskView(TaskView taskView) {
         PagedOrientationHandler orientationHandler = taskView.getPagedOrientationHandler();
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        orientationHandler.setTaskMenuAroundTaskView(this, mTaskInsetMargin);
+
+        // Get Position
         mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
         Rect insets = mActivity.getDragLayer().getInsets();
         BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
-        params.width = orientationHandler.getTaskMenuWidth(taskView.getThumbnail());
+        int padding = getResources()
+                .getDimensionPixelSize(R.dimen.task_menu_vertical_padding);
+        params.width = orientationHandler.getTaskMenuWidth(taskView.getThumbnail()) - (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());
-        boolean canActivityRotate = taskView.getRecentsView()
-            .mOrientationState.isRecentsActivityRotationAllowed();
-        mOptionLayout.setOrientation(orientationHandler
-                .getTaskMenuLayoutOrientation(canActivityRotate, mOptionLayout));
-        setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top,
-            taskView.getPagedOrientationHandler());
+
+        // Set divider spacing
+        ShapeDrawable divider = new ShapeDrawable(new RectShape());
+        divider.getPaint().setColor(getResources().getColor(android.R.color.transparent));
+        int dividerSpacing = (int) getResources().getDimension(R.dimen.task_menu_spacing);
+        mOptionLayout.setShowDividers(SHOW_DIVIDER_MIDDLE);
+
+        orientationHandler.setTaskOptionsMenuLayoutOrientation(
+                mActivity.getDeviceProfile(), mOptionLayout, dividerSpacing, divider);
+        setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, 0);
     }
 
     private void animateOpen() {
@@ -276,9 +282,11 @@
         final Animator revealAnimator = createOpenCloseOutlineProvider()
                 .createRevealAnimator(this, closing);
         revealAnimator.setInterpolator(Interpolators.DEACCEL);
-        mOpenCloseAnimator.play(revealAnimator);
-        mOpenCloseAnimator.play(ObjectAnimator.ofFloat(mTaskView.getThumbnail(), DIM_ALPHA,
-                closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA));
+        mOpenCloseAnimator.playTogether(revealAnimator,
+                ObjectAnimator.ofFloat(
+                        mTaskView.getThumbnail(), DIM_ALPHA,
+                        closing ? 0 : TaskView.MAX_PAGE_SCRIM_ALPHA),
+                ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
         mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationStart(Animator animation) {
@@ -292,7 +300,6 @@
                 }
             }
         });
-        mOpenCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
         mOpenCloseAnimator.setDuration(closing ? REVEAL_CLOSE_DURATION: REVEAL_OPEN_DURATION);
         mOpenCloseAnimator.start();
     }
@@ -300,10 +307,11 @@
     private void closeComplete() {
         mIsOpen = false;
         mActivity.getDragLayer().removeView(this);
+        ((RecentsView) mActivity.getOverviewPanel()).removeOnScrollChangedListener(this);
     }
 
     private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
-        float radius = Themes.getDialogCornerRadius(getContext());
+        float radius = TaskCornerRadius.get(mContext);
         Rect fromRect = new Rect(0, 0, getWidth(), 0);
         Rect toRect = new Rect(0, 0, getWidth(), getHeight());
         return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
similarity index 71%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
rename to quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 9e97a69..c70596d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -16,6 +16,10 @@
 
 package com.android.quickstep.views;
 
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+
+import static com.android.launcher3.Utilities.comp;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
 
@@ -25,8 +29,6 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -43,15 +45,15 @@
 import android.view.View;
 
 import androidx.annotation.RequiresApi;
+import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SystemUiController;
-import com.android.launcher3.util.Themes;
 import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
 import com.android.systemui.plugins.OverviewScreenshotActions;
@@ -63,10 +65,6 @@
  * A task in the Recents view.
  */
 public class TaskThumbnailView extends View implements PluginListener<OverviewScreenshotActions> {
-
-    private static final ColorMatrix COLOR_MATRIX = new ColorMatrix();
-    private static final ColorMatrix SATURATION_COLOR_MATRIX = new ColorMatrix();
-
     private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
             new MainThreadInitializedObject<>(FullscreenDrawParams::new);
 
@@ -85,11 +83,11 @@
 
     private final BaseActivity mActivity;
     private TaskOverlay mOverlay;
-    private final boolean mIsDarkTextTheme;
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mClearPaint = new Paint();
     private final Paint mDimmingPaintAfterClearing = new Paint();
+    private final int mDimColor;
 
     // Contains the portion of the thumbnail that is clipped when fullscreen progress = 0.
     private final Rect mPreviewRect = new Rect();
@@ -100,9 +98,8 @@
     private ThumbnailData mThumbnailData;
     protected BitmapShader mBitmapShader;
 
-    private float mDimAlpha = 1f;
-    private float mDimAlphaMultiplier = 1f;
-    private float mSaturation = 1f;
+    /** How much this thumbnail is dimmed, 0 not dimmed at all, 1 totally dimmed. */
+    private float mDimAlpha = 0f;
 
     private boolean mOverlayEnabled;
     private OverviewScreenshotActions mOverviewScreenshotActionsPlugin;
@@ -120,11 +117,12 @@
         mPaint.setFilterBitmap(true);
         mBackgroundPaint.setColor(Color.WHITE);
         mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-        mDimmingPaintAfterClearing.setColor(Color.BLACK);
         mActivity = BaseActivity.fromContext(context);
-        mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
-        // Initialize with dummy value. It is overridden later by TaskView
+        // Initialize with placeholder value. It is overridden later by TaskView
         mFullscreenParams = TEMP_PARAMS.get(context);
+
+        mDimColor = RecentsView.getForegroundScrimDimColor(context);
+        mDimmingPaintAfterClearing.setColor(mDimColor);
     }
 
     /**
@@ -182,15 +180,12 @@
         updateThumbnailPaintFilter();
     }
 
-    public void setDimAlphaMultipler(float dimAlphaMultipler) {
-        mDimAlphaMultiplier = dimAlphaMultipler;
-        setDimAlpha(mDimAlpha);
-    }
-
     /**
      * Sets the alpha of the dim layer on top of this view.
      * <p>
-     * If dimAlpha is 0, no dimming is applied; if dimAlpha is 1, the thumbnail will be black.
+     * If dimAlpha is 0, no dimming is applied; if dimAlpha is 1, the thumbnail will be the
+     * extracted background color.
+     *
      */
     public void setDimAlpha(float dimAlpha) {
         mDimAlpha = dimAlpha;
@@ -208,13 +203,6 @@
         return mDimAlpha;
     }
 
-    public Rect getInsets(Rect fallback) {
-        if (mThumbnailData != null) {
-            return mThumbnailData.insets;
-        }
-        return fallback;
-    }
-
     /**
      * Get the scaled insets that are being used to draw the task view. This is a subsection of
      * the full snapshot.
@@ -226,6 +214,10 @@
             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());
@@ -250,10 +242,10 @@
     public int getSysUiStatusNavFlags() {
         if (mThumbnailData != null) {
             int flags = 0;
-            flags |= (mThumbnailData.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+            flags |= (mThumbnailData.appearance & APPEARANCE_LIGHT_STATUS_BARS) != 0
                     ? SystemUiController.FLAG_LIGHT_STATUS
                     : SystemUiController.FLAG_DARK_STATUS;
-            flags |= (mThumbnailData.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
+            flags |= (mThumbnailData.appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0
                     ? SystemUiController.FLAG_LIGHT_NAV
                     : SystemUiController.FLAG_DARK_NAV;
             return flags;
@@ -317,8 +309,17 @@
             float cornerRadius) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
-                canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
-                canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
+                // TODO(b/189265196): Temporary fix to align the surface with the cutout perfectly.
+                // Round up only when the live tile task is displayed in Overview.
+                float rounding = comp(mFullscreenParams.mFullscreenProgress);
+                float left = x + rounding / 2;
+                float top = y + rounding / 2;
+                float right = width - rounding;
+                float bottom = height - rounding;
+
+                canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius,
+                        mClearPaint);
+                canvas.drawRoundRect(left, top, right, bottom, cornerRadius, cornerRadius,
                         mDimmingPaintAfterClearing);
                 return;
             }
@@ -327,22 +328,14 @@
         // Draw the background in all cases, except when the thumbnail data is opaque
         final boolean drawBackgroundOnly = mTask == null || mTask.isLocked || mBitmapShader == null
                 || mThumbnailData == null;
-        if (drawBackgroundOnly || mPreviewPositionHelper.mClipBottom > 0
-                || mThumbnailData.isTranslucent) {
+        if (drawBackgroundOnly || mThumbnailData.isTranslucent) {
             canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
             if (drawBackgroundOnly) {
                 return;
             }
         }
 
-        if (mPreviewPositionHelper.mClipBottom > 0) {
-            canvas.save();
-            canvas.clipRect(x, y, width, mPreviewPositionHelper.mClipBottom);
-            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
-            canvas.restore();
-        } else {
-            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
-        }
+        canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
     }
 
     public TaskView getTaskView() {
@@ -352,11 +345,16 @@
     public void setOverlayEnabled(boolean overlayEnabled) {
         if (mOverlayEnabled != overlayEnabled) {
             mOverlayEnabled = overlayEnabled;
-            updateOverlay();
+
+            refreshOverlay();
         }
     }
 
-    private void updateOverlay() {
+    /**
+     * Potentially re-init the task overlay. Be cautious when calling this as the overlay may
+     * do processing on initialization.
+     */
+    private void refreshOverlay() {
         if (mOverlayEnabled) {
             getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
                     mPreviewPositionHelper.mIsOrientationChanged);
@@ -366,77 +364,48 @@
     }
 
     private void updateThumbnailPaintFilter() {
-        int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
-        ColorFilter filter = getColorFilter(mul, mIsDarkTextTheme, mSaturation);
+        ColorFilter filter = getColorFilter(mDimAlpha);
         mBackgroundPaint.setColorFilter(filter);
-        mDimmingPaintAfterClearing.setAlpha(255 - mul);
+        int alpha = (int) (mDimAlpha * 255);
+        mDimmingPaintAfterClearing.setAlpha(alpha);
         if (mBitmapShader != null) {
             mPaint.setColorFilter(filter);
         } else {
             mPaint.setColorFilter(null);
-            mPaint.setColor(Color.argb(255, mul, mul, mul));
+            mPaint.setColor(ColorUtils.blendARGB(Color.BLACK, mDimColor, alpha));
         }
         invalidate();
     }
 
     private void updateThumbnailMatrix() {
-        mPreviewPositionHelper.mClipBottom = -1;
         mPreviewPositionHelper.mIsOrientationChanged = false;
         if (mBitmapShader != null && mThumbnailData != null) {
             mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
                     mThumbnailData.thumbnail.getHeight());
             int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
                     .getRecentsActivityRotation();
+            boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
             mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
                     getMeasuredWidth(), getMeasuredHeight(), mActivity.getDeviceProfile(),
-                    currentRotation);
+                    currentRotation, isRtl);
 
             mBitmapShader.setLocalMatrix(mPreviewPositionHelper.mMatrix);
             mPaint.setShader(mBitmapShader);
         }
         getTaskView().updateCurrentFullscreenParams(mPreviewPositionHelper);
         invalidate();
-
-        // Update can be called from {@link #onSizeChanged} during layout, post handling of overlay
-        // as overlay could modify the views in the overlay as a side effect of its update.
-        post(this::updateOverlay);
     }
 
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
         updateThumbnailMatrix();
+
+        refreshOverlay();
     }
 
-    /**
-     * @param intensity multiplier for color values. 0 - make black (white if shouldLighten), 255 -
-     *                  leave unchanged.
-     */
-    private static ColorFilter getColorFilter(int intensity, boolean shouldLighten,
-            float saturation) {
-        intensity = Utilities.boundToRange(intensity, 0, 255);
-
-        if (intensity == 255 && saturation == 1) {
-            return null;
-        }
-
-        final float intensityScale = intensity / 255f;
-        COLOR_MATRIX.setScale(intensityScale, intensityScale, intensityScale, 1);
-
-        if (saturation != 1) {
-            SATURATION_COLOR_MATRIX.setSaturation(saturation);
-            COLOR_MATRIX.postConcat(SATURATION_COLOR_MATRIX);
-        }
-
-        if (shouldLighten) {
-            final float[] colorArray = COLOR_MATRIX.getArray();
-            final int colorAdd = 255 - intensity;
-            colorArray[4] = colorAdd;
-            colorArray[9] = colorAdd;
-            colorArray[14] = colorAdd;
-        }
-
-        return new ColorMatrixColorFilter(COLOR_MATRIX);
+    private ColorFilter getColorFilter(float dimAmount) {
+        return Utilities.makeColorTintingColorFilter(mDimColor, dimAmount);
     }
 
     public Bitmap getThumbnail() {
@@ -465,7 +434,6 @@
         // Contains the portion of the thumbnail that is clipped when fullscreen progress = 0.
         private final RectF mClippedInsets = new RectF();
         private final Matrix mMatrix = new Matrix();
-        private float mClipBottom = -1;
         private boolean mIsOrientationChanged;
 
         public Matrix getMatrix() {
@@ -475,41 +443,112 @@
         /**
          * Updates the matrix based on the provided parameters
          */
-        public void updateThumbnailMatrix(Rect thumbnailPosition, ThumbnailData thumbnailData,
-                int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation) {
+        public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
+                int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation,
+                boolean isRtl) {
             boolean isRotated = false;
             boolean isOrientationDifferent;
-            mClipBottom = -1;
 
             int thumbnailRotation = thumbnailData.rotation;
             int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
-            Rect thumbnailInsets = getBoundedInsets(
-                    dp.getInsets(), thumbnailData.insets, deltaRotate);
+            RectF thumbnailClipHint = TaskView.CLIP_STATUS_AND_NAV_BARS
+                    ? new RectF(thumbnailData.insets) : new RectF();
 
             float scale = thumbnailData.scale;
-            final float thumbnailWidth = thumbnailPosition.width()
-                    - (thumbnailInsets.left + thumbnailInsets.right) * scale;
-            final float thumbnailHeight = thumbnailPosition.height()
-                    - (thumbnailInsets.top + thumbnailInsets.bottom) * scale;
-
             final float thumbnailScale;
 
-            // Landscape vs portrait change
+            // Landscape vs portrait change.
+            // Note: Disable rotation in grid layout.
             boolean windowingModeSupportsRotation = !dp.isMultiWindowMode
-                    && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
+                    && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN
+                    && !(dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get());
             isOrientationDifferent = isOrientationChange(deltaRotate)
                     && windowingModeSupportsRotation;
-            if (canvasWidth == 0) {
+            if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
                 // If we haven't measured , skip the thumbnail drawing and only draw the background
                 // color
                 thumbnailScale = 0f;
             } else {
                 // Rotate the screenshot if not in multi-window mode
                 isRotated = deltaRotate > 0 && windowingModeSupportsRotation;
-                // Scale the screenshot to always fit the width of the card.
-                thumbnailScale = isOrientationDifferent
-                        ? canvasWidth / thumbnailHeight
-                        : canvasWidth / thumbnailWidth;
+
+                float surfaceWidth = thumbnailBounds.width() / scale;
+                float surfaceHeight = thumbnailBounds.height() / scale;
+                float availableWidth = surfaceWidth
+                        - (thumbnailClipHint.left + thumbnailClipHint.right);
+                float availableHeight = surfaceHeight
+                        - (thumbnailClipHint.top + thumbnailClipHint.bottom);
+
+                if (isRotated) {
+                    float canvasAspect = canvasWidth / (float) canvasHeight;
+                    float availableAspect = availableHeight / availableWidth;
+                    // Do not rotate thumbnail if it would not improve fit
+                    if (Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
+                            availableAspect, 0.1f)) {
+                        isRotated = false;
+                        isOrientationDifferent = false;
+                    }
+                }
+
+                final float targetW, targetH;
+                if (isOrientationDifferent) {
+                    targetW = canvasHeight;
+                    targetH = canvasWidth;
+                } else {
+                    targetW = canvasWidth;
+                    targetH = canvasHeight;
+                }
+                float canvasAspect = 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.
+                float croppedWidth = availableWidth;
+                if (croppedWidth < targetW) {
+                    croppedWidth = Math.min(targetW, surfaceWidth);
+                }
+
+                float croppedHeight = croppedWidth / canvasAspect;
+                if (croppedHeight > availableHeight) {
+                    croppedHeight = availableHeight;
+                    if (croppedHeight < targetH) {
+                        croppedHeight = Math.min(targetH, surfaceHeight);
+                    }
+                    croppedWidth = croppedHeight * canvasAspect;
+
+                    // One last check in case the task aspect radio messed up something
+                    if (croppedWidth > surfaceWidth) {
+                        croppedWidth = surfaceWidth;
+                        croppedHeight = croppedWidth / canvasAspect;
+                    }
+                }
+
+                // Update the clip hints. Align to 0,0, crop the remaining.
+                if (isRtl) {
+                    thumbnailClipHint.left += availableWidth - croppedWidth;
+                    if (thumbnailClipHint.right < 0) {
+                        thumbnailClipHint.left += thumbnailClipHint.right;
+                        thumbnailClipHint.right = 0;
+                    }
+                } else {
+                    thumbnailClipHint.right += availableWidth - croppedWidth;
+                    if (thumbnailClipHint.left < 0) {
+                        thumbnailClipHint.right += thumbnailClipHint.left;
+                        thumbnailClipHint.left = 0;
+                    }
+                }
+                thumbnailClipHint.bottom += availableHeight - croppedHeight;
+                if (thumbnailClipHint.top < 0) {
+                    thumbnailClipHint.bottom += thumbnailClipHint.top;
+                    thumbnailClipHint.top = 0;
+                } else if (thumbnailClipHint.bottom < 0) {
+                    thumbnailClipHint.top += thumbnailClipHint.bottom;
+                    thumbnailClipHint.bottom = 0;
+                }
+
+                thumbnailScale = targetW / (croppedWidth * scale);
             }
 
             Rect splitScreenInsets = dp.getInsets();
@@ -519,24 +558,24 @@
                     mClippedInsets.offsetTo(splitScreenInsets.left * scale,
                             splitScreenInsets.top * scale);
                 } else {
-                    mClippedInsets.offsetTo(thumbnailInsets.left * scale,
-                            thumbnailInsets.top * scale);
+                    mClippedInsets.offsetTo(thumbnailClipHint.left * scale,
+                            thumbnailClipHint.top * scale);
                 }
                 mMatrix.setTranslate(
-                        -thumbnailInsets.left * scale,
-                        -thumbnailInsets.top * scale);
+                        -thumbnailClipHint.left * scale,
+                        -thumbnailClipHint.top * scale);
             } else {
-                setThumbnailRotation(deltaRotate, thumbnailInsets, scale, thumbnailPosition);
+                setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds);
             }
 
             final float widthWithInsets;
             final float heightWithInsets;
             if (isOrientationDifferent) {
-                widthWithInsets = thumbnailPosition.height() * thumbnailScale;
-                heightWithInsets = thumbnailPosition.width() * thumbnailScale;
+                widthWithInsets = thumbnailBounds.height() * thumbnailScale;
+                heightWithInsets = thumbnailBounds.width() * thumbnailScale;
             } else {
-                widthWithInsets = thumbnailPosition.width() * thumbnailScale;
-                heightWithInsets = thumbnailPosition.height() * thumbnailScale;
+                widthWithInsets = thumbnailBounds.width() * thumbnailScale;
+                heightWithInsets = thumbnailBounds.height() * thumbnailScale;
             }
             mClippedInsets.left *= thumbnailScale;
             mClippedInsets.top *= thumbnailScale;
@@ -552,25 +591,9 @@
             }
 
             mMatrix.postScale(thumbnailScale, thumbnailScale);
-
-            float bitmapHeight = Math.max(0,
-                    (isOrientationDifferent ? thumbnailWidth : thumbnailHeight) * thumbnailScale);
-            if (Math.round(bitmapHeight) < canvasHeight) {
-                mClipBottom = bitmapHeight;
-            }
             mIsOrientationChanged = isOrientationDifferent;
         }
 
-        private Rect getBoundedInsets(Rect activityInsets, Rect insets, int deltaRotation) {
-            if (deltaRotation != 0) {
-                return insets;
-            }
-            return new Rect(Math.min(insets.left, activityInsets.left),
-                    Math.min(insets.top, activityInsets.top),
-                    Math.min(insets.right, activityInsets.right),
-                    Math.min(insets.bottom, activityInsets.bottom));
-        }
-
         private int getRotationDelta(int oldRotation, int newRotation) {
             int delta = newRotation - oldRotation;
             if (delta < 0) delta += 4;
@@ -586,12 +609,12 @@
             return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270;
         }
 
-        private void setThumbnailRotation(int deltaRotate, Rect thumbnailInsets, float scale,
+        private void setThumbnailRotation(int deltaRotate, RectF thumbnailInsets, float scale,
                 Rect thumbnailPosition) {
-            int newLeftInset = 0;
-            int newTopInset = 0;
-            int translateX = 0;
-            int translateY = 0;
+            float newLeftInset = 0;
+            float newTopInset = 0;
+            float translateX = 0;
+            float translateY = 0;
 
             mMatrix.setRotate(90 * deltaRotate);
             switch (deltaRotate) { /* Counter-clockwise */
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
new file mode 100644
index 0000000..2d322e9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -0,0 +1,1529 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static android.view.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;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
+import static com.android.launcher3.Utilities.comp;
+import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
+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.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.AttributeSet;
+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;
+import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherSettings;
+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;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.RunnableList;
+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.SystemUiProxy;
+import com.android.quickstep.TaskIconCache;
+import com.android.quickstep.TaskOverlayFactory;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.util.CancellableTask;
+import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.TaskCornerRadius;
+import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.QuickStepContract;
+
+import java.lang.annotation.Retention;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A task in the Recents view.
+ */
+public class TaskView extends FrameLayout implements Reusable {
+
+    private static final String TAG = TaskView.class.getSimpleName();
+
+    public static final int FLAG_UPDATE_ICON = 1;
+    public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
+
+    public static final int FLAG_UPDATE_ALL = FLAG_UPDATE_ICON | FLAG_UPDATE_THUMBNAIL;
+
+    /**
+     * Used in conjunction with {@link #onTaskListVisibilityChanged(boolean, int)}, providing more
+     * granularity on which components of this task require an update
+     */
+    @Retention(SOURCE)
+    @IntDef({FLAG_UPDATE_ALL, FLAG_UPDATE_ICON, FLAG_UPDATE_THUMBNAIL})
+    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.
+     */
+    public static final boolean CLIP_STATUS_AND_NAV_BARS = false;
+
+    private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f;
+    private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f;
+
+    public static final long SCALE_ICON_DURATION = 120;
+    private static final long DIM_ANIM_DURATION = 700;
+
+    private static final Interpolator FULLSCREEN_INTERPOLATOR = ACCEL_DEACCEL;
+
+    /**
+     * This technically can be a vanilla {@link TouchDelegate} class, however that class requires
+     * setting the touch bounds at construction, so we'd repeatedly be created many instances
+     * unnecessarily as scrolling occurs, whereas {@link TransformingTouchDelegate} allows touch
+     * delegated bounds only to be updated.
+     */
+    private TransformingTouchDelegate mIconTouchDelegate;
+    private TransformingTouchDelegate mChipTouchDelegate;
+
+    private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
+            Collections.singletonList(new Rect());
+
+    private static final FloatProperty<TaskView> FOCUS_TRANSITION =
+            new FloatProperty<TaskView>("focusTransition") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setIconAndDimTransitionProgress(v, false /* invert */);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mFocusTransitionProgress;
+                }
+            };
+
+    private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_X =
+            new FloatProperty<TaskView>("splitSelectTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setSplitSelectTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mSplitSelectTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_Y =
+            new FloatProperty<TaskView>("splitSelectTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setSplitSelectTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mSplitSelectTranslationY;
+                }
+            };
+
+    private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
+            new FloatProperty<TaskView>("dismissTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setDismissTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mDismissTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> DISMISS_TRANSLATION_Y =
+            new FloatProperty<TaskView>("dismissTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setDismissTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mDismissTranslationY;
+                }
+            };
+
+    private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_X =
+            new FloatProperty<TaskView>("taskOffsetTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setTaskOffsetTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mTaskOffsetTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_Y =
+            new FloatProperty<TaskView>("taskOffsetTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setTaskOffsetTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mTaskOffsetTranslationY;
+                }
+            };
+
+    private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_X =
+            new FloatProperty<TaskView>("taskResistanceTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setTaskResistanceTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mTaskResistanceTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_Y =
+            new FloatProperty<TaskView>("taskResistanceTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setTaskResistanceTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mTaskResistanceTranslationY;
+                }
+            };
+
+    private static final FloatProperty<TaskView> FULLSCREEN_TRANSLATION_X =
+            new FloatProperty<TaskView>("fullscreenTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setFullscreenTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mFullscreenTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> FULLSCREEN_TRANSLATION_Y =
+            new FloatProperty<TaskView>("fullscreenTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setFullscreenTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mFullscreenTranslationY;
+                }
+            };
+
+    private static final FloatProperty<TaskView> NON_FULLSCREEN_TRANSLATION_X =
+            new FloatProperty<TaskView>("nonFullscreenTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setNonFullscreenTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mNonFullscreenTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> NON_FULLSCREEN_TRANSLATION_Y =
+            new FloatProperty<TaskView>("nonFullscreenTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setNonFullscreenTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mNonFullscreenTranslationY;
+                }
+            };
+
+    private final TaskOutlineProvider mOutlineProvider;
+
+    private Task mTask;
+    private TaskThumbnailView mSnapshotView;
+    private IconView mIconView;
+    private final DigitalWellBeingToast mDigitalWellBeingToast;
+    private float mFullscreenProgress;
+    private float mGridProgress;
+    private float mFullscreenScale = 1;
+    private final FullscreenDrawParams mCurrentFullscreenParams;
+    private final StatefulActivity mActivity;
+
+    // Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
+    private float mDismissTranslationX;
+    private float mDismissTranslationY;
+    private float mTaskOffsetTranslationX;
+    private float mTaskOffsetTranslationY;
+    private float mTaskResistanceTranslationX;
+    private float mTaskResistanceTranslationY;
+    // The following translation variables should only be used in the same orientation as Launcher.
+    private float 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;
+    // Used when in SplitScreenSelectState
+    private float mSplitSelectTranslationY;
+    private float mSplitSelectTranslationX;
+
+    private ObjectAnimator mIconAndDimAnimator;
+    private float mIconScaleAnimStartProgress = 0;
+    private float mFocusTransitionProgress = 1;
+    private float mModalness = 0;
+    private float mStableAlpha = 1;
+
+    private boolean mShowScreenshot;
+
+    // The current background requests to load the task thumbnail and icon
+    private CancellableTask mThumbnailLoadRequest;
+    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;
+
+    public TaskView(Context context) {
+        this(context, null);
+    }
+
+    public TaskView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mActivity = StatefulActivity.fromContext(context);
+        setOnClickListener(this::onClick);
+
+        mCurrentFullscreenParams = new FullscreenDrawParams(context);
+        mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
+
+        mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams,
+                mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
+        setOutlineProvider(mOutlineProvider);
+    }
+
+    /**
+     * Builds proto for logging
+     */
+    public WorkspaceItemInfo getItemInfo() {
+        final Task task = getTask();
+        ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
+        WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
+        stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
+        stubInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
+        stubInfo.user = componentKey.user;
+        stubInfo.intent = new Intent().setComponent(componentKey.componentName);
+        stubInfo.title = task.title;
+        stubInfo.screenId = getRecentsView().indexOfChild(this);
+        return stubInfo;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mSnapshotView = findViewById(R.id.snapshot);
+        mIconView = findViewById(R.id.icon);
+        mIconTouchDelegate = new TransformingTouchDelegate(mIconView);
+    }
+
+    /**
+     * Whether the taskview should take the touch event from parent. Events passed to children
+     * that might require special handling.
+     */
+    public boolean offerTouchToChildren(MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            computeAndSetIconTouchDelegate();
+            computeAndSetChipTouchDelegate();
+        }
+        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,
+                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));
+        }
+    }
+
+    /**
+     * The modalness of this view is how it should be displayed when it is shown on its own in the
+     * modal state of overview.
+     *
+     * @param modalness [0, 1] 0 being in context with other tasks, 1 being shown on its own.
+     */
+    public void setModalness(float modalness) {
+        if (mModalness == modalness) {
+            return;
+        }
+        mModalness = modalness;
+        mIconView.setAlpha(comp(modalness));
+        if (mContextualChipWrapper != null) {
+            mContextualChipWrapper.setScaleX(comp(modalness));
+            mContextualChipWrapper.setScaleY(comp(modalness));
+        }
+        mDigitalWellBeingToast.updateBannerOffset(modalness,
+                mCurrentFullscreenParams.mCurrentDrawnInsets.top
+                        + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
+    }
+
+    public DigitalWellBeingToast getDigitalWellBeingToast() {
+        return mDigitalWellBeingToast;
+    }
+
+    /**
+     * Updates this task view to the given {@param task}.
+     *
+     * TODO(b/142282126) Re-evaluate if we need to pass in isMultiWindowMode after
+     *   that issue is fixed
+     */
+    public void bind(Task task, RecentsOrientedState orientedState) {
+        cancelPendingLoadTasks();
+        mTask = task;
+        mSnapshotView.bind(task);
+        setOrientationState(orientedState);
+    }
+
+    public Task getTask() {
+        return mTask;
+    }
+
+    public boolean hasTaskId(int taskId) {
+        return mTask != null && mTask.key != null && mTask.key.id == taskId;
+    }
+
+    public TaskThumbnailView getThumbnail() {
+        return mSnapshotView;
+    }
+
+    public IconView getIconView() {
+        return mIconView;
+    }
+
+    private void onClick(View view) {
+        if (getTask() == null) {
+            return;
+        }
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
+            if (!mIsClickableAsLiveTile) {
+                return;
+            }
+
+            // Reset the minimized state since we force-toggled the minimized state when entering
+            // overview, but never actually finished the recents animation
+            SystemUiProxy p = SystemUiProxy.INSTANCE.getNoCreate();
+            if (p != null) {
+                p.setSplitScreenMinimized(false);
+            }
+
+            mIsClickableAsLiveTile = false;
+            RecentsView recentsView = getRecentsView();
+            final RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
+            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();
+                return;
+            }
+
+            AnimatorSet anim = new AnimatorSet();
+            TaskViewUtils.composeRecentsLaunchAnimator(
+                    anim, this, targets.apps,
+                    targets.wallpapers, targets.nonApps, true /* launcherClosing */,
+                    mActivity.getStateManager(), recentsView,
+                    recentsView.getDepthController());
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animator) {
+                    recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animator) {
+                    recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
+                    mIsClickableAsLiveTile = true;
+                }
+            });
+            anim.start();
+        } else {
+            launcherNonLiveTileTask();
+        }
+        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
+            getRecentsView().confirmSplitSelect(this);
+        } else {
+            launchTaskAnimated();
+        }
+    }
+
+    /**
+     * Starts the task associated with this view and animates the startup.
+     * @return CompletionStage to indicate the animation completion or null if the launch failed.
+     */
+    public RunnableList launchTaskAnimated() {
+        if (mTask != null) {
+            TestLogging.recordEvent(
+                    TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
+            ActivityOptionsWrapper opts =  mActivity.getActivityLaunchOptions(this, null);
+            if (ActivityManagerWrapper.getInstance()
+                    .startActivityFromRecents(mTask.key, opts.options)) {
+                if (ENABLE_QUICKSTEP_LIVE_TILE.get() && getRecentsView().getRunningTaskId() != -1) {
+                    // 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);
+                    return callbackList;
+                }
+                return opts.onEndCallback;
+            } else {
+                notifyTaskLaunchFailed(TAG);
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Starts the task associated with this view without any animation
+     */
+    public void launchTask(@NonNull Consumer<Boolean> callback) {
+        launchTask(callback, false /* freezeTaskList */);
+    }
+
+    /**
+     * Starts the task associated with this view without any animation
+     */
+    public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
+        if (mTask != null) {
+            TestLogging.recordEvent(
+                    TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
+
+            // 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());
+            if (freezeTaskList) {
+                ActivityOptionsCompat.setFreezeRecentTasksList(opts);
+            }
+            Task.TaskKey key = mTask.key;
+            UI_HELPER_EXECUTOR.execute(() -> {
+                if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
+                    // If the call to start activity failed, then post the result immediately,
+                    // otherwise, wait for the animation start callback from the activity options
+                    // above
+                    MAIN_EXECUTOR.post(() -> {
+                        notifyTaskLaunchFailed(TAG);
+                        callback.accept(false);
+                    });
+                }
+            });
+        } else {
+            callback.accept(false);
+        }
+    }
+
+    /**
+     * See {@link TaskDataChanges}
+     * @param visible If this task view will be visible to the user in overview or hidden
+     */
+    public void onTaskListVisibilityChanged(boolean visible) {
+        onTaskListVisibilityChanged(visible, FLAG_UPDATE_ALL);
+    }
+
+    /**
+     * See {@link TaskDataChanges}
+     * @param visible If this task view will be visible to the user in overview or hidden
+     */
+    public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {
+        if (mTask == null) {
+            return;
+        }
+        cancelPendingLoadTasks();
+        if (visible) {
+            // These calls are no-ops if the data is already loaded, try and load the high
+            // resolution thumbnail if the state permits
+            RecentsModel model = RecentsModel.INSTANCE.get(getContext());
+            TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
+            TaskIconCache iconCache = model.getIconCache();
+
+            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+                mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
+                        mTask, thumbnail -> {
+                            mSnapshotView.setThumbnail(mTask, thumbnail);
+                        });
+            }
+            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+                mIconLoadRequest = iconCache.updateIconInBackground(mTask,
+                        (task) -> {
+                            setIcon(task.icon);
+                            mDigitalWellBeingToast.initialize(mTask);
+                        });
+            }
+        } else {
+            if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
+                mSnapshotView.setThumbnail(null, null);
+                // Reset the task thumbnail reference as well (it will be fetched from the cache or
+                // reloaded next time we need it)
+                mTask.thumbnail = null;
+            }
+            if (needsUpdate(changes, FLAG_UPDATE_ICON)) {
+                setIcon(null);
+            }
+        }
+    }
+
+    private boolean needsUpdate(@TaskDataChanges int dataChange, @TaskDataChanges int flag) {
+        return (dataChange & flag) == flag;
+    }
+
+    private void cancelPendingLoadTasks() {
+        if (mThumbnailLoadRequest != null) {
+            mThumbnailLoadRequest.cancel();
+            mThumbnailLoadRequest = null;
+        }
+        if (mIconLoadRequest != null) {
+            mIconLoadRequest.cancel();
+            mIconLoadRequest = null;
+        }
+    }
+
+    private boolean showTaskMenu() {
+        if (getRecentsView().mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
+            // Don't show menu when selecting second split screen app
+            return true;
+        }
+
+        if (!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);
+        }
+    }
+
+    private void setIcon(Drawable icon) {
+        if (icon != null) {
+            mIconView.setDrawable(icon);
+            mIconView.setOnClickListener(v -> showTaskMenu());
+            mIconView.setOnLongClickListener(v -> {
+                requestDisallowInterceptTouchEvent(true);
+                return showTaskMenu();
+            });
+        } else {
+            mIconView.setDrawable(null);
+            mIconView.setOnClickListener(null);
+            mIconView.setOnLongClickListener(null);
+        }
+    }
+
+    public void setOrientationState(RecentsOrientedState orientationState) {
+        PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
+        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+        snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+        int taskIconMargin = deviceProfile.overviewTaskMarginPx;
+        int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
+        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;
+        }
+        mSnapshotView.setLayoutParams(snapshotParams);
+        iconParams.width = iconParams.height = taskIconHeight;
+        mIconView.setLayoutParams(iconParams);
+        mIconView.setRotation(orientationHandler.getDegreesRotated());
+        snapshotParams.topMargin = deviceProfile.overviewTaskThumbnailTopMarginPx;
+        mSnapshotView.setLayoutParams(snapshotParams);
+        getThumbnail().getTaskOverlay().updateOrientationState(orientationState);
+    }
+
+    private void setIconAndDimTransitionProgress(float progress, boolean invert) {
+        if (invert) {
+            progress = 1 - progress;
+        }
+        mFocusTransitionProgress = progress;
+        float iconScalePercentage = (float) SCALE_ICON_DURATION / DIM_ANIM_DURATION;
+        float lowerClamp = invert ? 1f - iconScalePercentage : 0;
+        float upperClamp = invert ? 1 : iconScalePercentage;
+        float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
+                .getInterpolation(progress);
+        mIconView.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);
+    }
+
+    public void setIconScaleAnimStartProgress(float startProgress) {
+        mIconScaleAnimStartProgress = startProgress;
+    }
+
+    public void animateIconScaleAndDimIntoView() {
+        if (mIconAndDimAnimator != null) {
+            mIconAndDimAnimator.cancel();
+        }
+        mIconAndDimAnimator = ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1);
+        mIconAndDimAnimator.setCurrentFraction(mIconScaleAnimStartProgress);
+        mIconAndDimAnimator.setDuration(DIM_ANIM_DURATION).setInterpolator(LINEAR);
+        mIconAndDimAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mIconAndDimAnimator = null;
+            }
+        });
+        mIconAndDimAnimator.start();
+    }
+
+    protected void setIconScaleAndDim(float iconScale) {
+        setIconScaleAndDim(iconScale, false);
+    }
+
+    private void setIconScaleAndDim(float iconScale, boolean invert) {
+        if (mIconAndDimAnimator != null) {
+            mIconAndDimAnimator.cancel();
+        }
+        setIconAndDimTransitionProgress(iconScale, invert);
+    }
+
+    protected void resetViewTransforms() {
+        // fullscreenTranslation and accumulatedTranslation should not be reset, as
+        // resetViewTransforms is called during Quickswitch scrolling.
+        mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX =
+                mSplitSelectTranslationX = 0f;
+        mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY =
+                mSplitSelectTranslationY = 0f;
+        applyTranslationX();
+        applyTranslationY();
+        setTranslationZ(0);
+        setAlpha(mStableAlpha);
+        setIconScaleAndDim(1);
+        setColorTint(0, 0);
+    }
+
+    public void setStableAlpha(float parentAlpha) {
+        mStableAlpha = parentAlpha;
+        setAlpha(mStableAlpha);
+    }
+
+    @Override
+    public void onRecycle() {
+        mFullscreenTranslationX = mFullscreenTranslationY = mNonFullscreenTranslationX =
+                mNonFullscreenTranslationY = mGridTranslationX = mGridTranslationY =
+                        mBoxTranslationY = 0f;
+        resetViewTransforms();
+        // 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);
+        setOverlayEnabled(false);
+        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()) {
+            setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : right - left);
+            setPivotY(mSnapshotView.getTop());
+        } else {
+            setPivotX((right - left) * 0.5f);
+            setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
+        }
+        if (Utilities.ATLEAST_Q) {
+            SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
+            setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
+        }
+    }
+
+    /**
+     * How much to scale down pages near the edge of the screen.
+     */
+    public static float getEdgeScaleDownFactor(DeviceProfile deviceProfile) {
+        if (deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+            return EDGE_SCALE_DOWN_FACTOR_GRID;
+        } else {
+            return EDGE_SCALE_DOWN_FACTOR_CAROUSEL;
+        }
+    }
+
+    private void setFullscreenScale(float fullscreenScale) {
+        mFullscreenScale = fullscreenScale;
+        applyScale();
+    }
+
+    public float getFullscreenScale() {
+        return mFullscreenScale;
+    }
+
+    /**
+     * Moves TaskView between carousel and 2 row grid.
+     *
+     * @param gridProgress 0 = carousel; 1 = 2 row grid.
+     */
+    public void setGridProgress(float gridProgress) {
+        mGridProgress = gridProgress;
+        applyTranslationX();
+        applyTranslationY();
+        applyScale();
+    }
+
+    private void applyScale() {
+        float scale = 1;
+        float fullScreenProgress = FULLSCREEN_INTERPOLATOR.getInterpolation(mFullscreenProgress);
+        scale *= Utilities.mapRange(fullScreenProgress, 1f, mFullscreenScale);
+        setScaleX(scale);
+        setScaleY(scale);
+    }
+
+    private void setSplitSelectTranslationX(float x) {
+        mSplitSelectTranslationX = x;
+        applyTranslationX();
+    }
+
+    private void setSplitSelectTranslationY(float y) {
+        mSplitSelectTranslationY = y;
+        applyTranslationY();
+    }
+    private void setDismissTranslationX(float x) {
+        mDismissTranslationX = x;
+        applyTranslationX();
+    }
+
+    private void setDismissTranslationY(float y) {
+        mDismissTranslationY = y;
+        applyTranslationY();
+    }
+
+    private void setTaskOffsetTranslationX(float x) {
+        mTaskOffsetTranslationX = x;
+        applyTranslationX();
+    }
+
+    private void setTaskOffsetTranslationY(float y) {
+        mTaskOffsetTranslationY = y;
+        applyTranslationY();
+    }
+
+    private void setTaskResistanceTranslationX(float x) {
+        mTaskResistanceTranslationX = x;
+        applyTranslationX();
+    }
+
+    private void setTaskResistanceTranslationY(float y) {
+        mTaskResistanceTranslationY = y;
+        applyTranslationY();
+    }
+
+    private void setFullscreenTranslationX(float fullscreenTranslationX) {
+        mFullscreenTranslationX = fullscreenTranslationX;
+        applyTranslationX();
+    }
+
+    private void setFullscreenTranslationY(float fullscreenTranslationY) {
+        mFullscreenTranslationY = fullscreenTranslationY;
+        applyTranslationY();
+    }
+
+    private void setNonFullscreenTranslationX(float nonFullscreenTranslationX) {
+        mNonFullscreenTranslationX = nonFullscreenTranslationX;
+        applyTranslationX();
+    }
+
+    private void setNonFullscreenTranslationY(float nonFullscreenTranslationY) {
+        mNonFullscreenTranslationY = nonFullscreenTranslationY;
+        applyTranslationY();
+    }
+
+    public void setGridTranslationX(float gridTranslationX) {
+        mGridTranslationX = gridTranslationX;
+        applyTranslationX();
+    }
+
+    public float getGridTranslationX() {
+        return mGridTranslationX;
+    }
+
+    public void setGridTranslationY(float gridTranslationY) {
+        mGridTranslationY = gridTranslationY;
+        applyTranslationY();
+    }
+
+    public float getGridTranslationY() {
+        return mGridTranslationY;
+    }
+
+    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;
+        }
+        return scrollAdjustment;
+    }
+
+    public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+        return getScrollAdjustment(fullscreenEnabled, gridEnabled);
+    }
+
+    public float getSizeAdjustment(boolean fullscreenEnabled) {
+        float sizeAdjustment = 1;
+        if (fullscreenEnabled) {
+            sizeAdjustment *= mFullscreenScale;
+        }
+        return sizeAdjustment;
+    }
+
+    private void setBoxTranslationY(float boxTranslationY) {
+        mBoxTranslationY = boxTranslationY;
+        applyTranslationY();
+    }
+
+    private void applyTranslationX() {
+        setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
+                + mSplitSelectTranslationX + getPersistentTranslationX());
+    }
+
+    private void applyTranslationY() {
+        setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
+                + mSplitSelectTranslationY + getPersistentTranslationY());
+    }
+
+    /**
+     * Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does not
+     * change according to a temporary state (e.g. task offset).
+     */
+    public float getPersistentTranslationX() {
+        return getFullscreenTrans(mFullscreenTranslationX)
+                + getNonFullscreenTrans(mNonFullscreenTranslationX)
+                + getGridTrans(mGridTranslationX);
+    }
+
+    /**
+     * Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does not
+     * change according to a temporary state (e.g. task offset).
+     */
+    public float getPersistentTranslationY() {
+        return mBoxTranslationY
+                + getFullscreenTrans(mFullscreenTranslationY)
+                + getNonFullscreenTrans(mNonFullscreenTranslationY)
+                + getGridTrans(mGridTranslationY);
+    }
+
+    public FloatProperty<TaskView> getPrimarySplitTranslationProperty() {
+        return getPagedOrientationHandler().getPrimaryValue(
+                SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getSecondarySplitTranslationProperty() {
+        return getPagedOrientationHandler().getSecondaryValue(
+                SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
+        return getPagedOrientationHandler().getPrimaryValue(
+                DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getSecondaryDissmissTranslationProperty() {
+        return getPagedOrientationHandler().getSecondaryValue(
+                DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getPrimaryTaskOffsetTranslationProperty() {
+        return getPagedOrientationHandler().getPrimaryValue(
+                TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
+        return getPagedOrientationHandler().getSecondaryValue(
+                TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getPrimaryFullscreenTranslationProperty() {
+        return getPagedOrientationHandler().getPrimaryValue(
+                FULLSCREEN_TRANSLATION_X, FULLSCREEN_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getSecondaryFullscreenTranslationProperty() {
+        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);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
+        return false;
+    }
+
+    public boolean isEndQuickswitchCuj() {
+        return mEndQuickswitchCuj;
+    }
+
+    public void setEndQuickswitchCuj(boolean endQuickswitchCuj) {
+        mEndQuickswitchCuj = endQuickswitchCuj;
+    }
+
+    private static final class TaskOutlineProvider extends ViewOutlineProvider {
+
+        private int mMarginTop;
+        private FullscreenDrawParams mFullscreenParams;
+
+        TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams, int topMargin) {
+            mMarginTop = topMargin;
+            mFullscreenParams = fullscreenParams;
+        }
+
+        public void updateParams(FullscreenDrawParams params, int topMargin) {
+            mFullscreenParams = params;
+            mMarginTop = topMargin;
+        }
+
+        @Override
+        public void getOutline(View view, Outline outline) {
+            RectF insets = mFullscreenParams.mCurrentDrawnInsets;
+            float scale = mFullscreenParams.mScale;
+            outline.setRoundRect(0,
+                    (int) (mMarginTop * scale),
+                    (int) ((insets.left + view.getWidth() + insets.right) * scale),
+                    (int) ((insets.top + view.getHeight() + insets.bottom) * scale),
+                    mFullscreenParams.mCurrentDrawnCornerRadius);
+        }
+    }
+
+    private int getExpectedViewHeight(View view) {
+        int expectedHeight;
+        int h = view.getLayoutParams().height;
+        if (h > 0) {
+            expectedHeight = h;
+        } else {
+            int m = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY - 1, MeasureSpec.AT_MOST);
+            view.measure(m, m);
+            expectedHeight = view.getMeasuredHeight();
+        }
+        return expectedHeight;
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        info.addAction(
+                new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close,
+                        getContext().getText(R.string.accessibility_close)));
+
+        final Context context = getContext();
+        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
+                mActivity.getDeviceProfile())) {
+            info.addAction(s.createAccessibilityAction(context));
+        }
+
+        if (mDigitalWellBeingToast.hasLimit()) {
+            info.addAction(
+                    new AccessibilityNodeInfo.AccessibilityAction(
+                            R.string.accessibility_app_usage_settings,
+                            getContext().getText(R.string.accessibility_app_usage_settings)));
+        }
+
+        final RecentsView recentsView = getRecentsView();
+        final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
+                AccessibilityNodeInfo.CollectionItemInfo.obtain(
+                        0, 1, recentsView.getTaskViewCount() - recentsView.indexOfChild(this) - 1,
+                        1, false);
+        info.setCollectionItemInfo(itemInfo);
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (action == R.string.accessibility_close) {
+            getRecentsView().dismissTask(this, true /*animateTaskView*/,
+                    true /*removeTask*/);
+            return true;
+        }
+
+        if (action == R.string.accessibility_app_usage_settings) {
+            mDigitalWellBeingToast.openAppUsageSettings(this);
+            return true;
+        }
+
+        for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
+                mActivity.getDeviceProfile())) {
+            if (s.hasHandlerForAction(action)) {
+                s.onClick(this);
+                return true;
+            }
+        }
+
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    public RecentsView getRecentsView() {
+        return (RecentsView) getParent();
+    }
+
+    PagedOrientationHandler getPagedOrientationHandler() {
+        return getRecentsView().mOrientationState.getOrientationHandler();
+    }
+
+    private void notifyTaskLaunchFailed(String tag) {
+        String msg = "Failed to launch task";
+        if (mTask != null) {
+            msg += " (task=" + mTask.key.baseIntent + " userId=" + mTask.key.userId + ")";
+        }
+        Log.w(tag, msg);
+        Toast.makeText(getContext(), R.string.activity_not_available, LENGTH_SHORT).show();
+    }
+
+    /**
+     * Hides the icon and shows insets when this TaskView is about to be shown fullscreen.
+     *
+     * @param progress: 0 = show icon and no insets; 1 = don't show icon and show full insets.
+     */
+    public void setFullscreenProgress(float progress) {
+        progress = Utilities.boundToRange(progress, 0, 1);
+        mFullscreenProgress = progress;
+        mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
+        getThumbnail().getTaskOverlay().setFullscreenProgress(progress);
+
+        applyTranslationX();
+        applyTranslationY();
+        applyScale();
+
+        TaskThumbnailView thumbnail = getThumbnail();
+        updateCurrentFullscreenParams(thumbnail.getPreviewPositionHelper());
+
+        if (!getRecentsView().isTaskIconScaledDown(this)) {
+            // Some of the items in here are dependent on the current fullscreen params, but don't
+            // update them if the icon is supposed to be scaled down.
+            setIconScaleAndDim(progress, true /* invert */);
+        }
+
+        thumbnail.setFullscreenParams(mCurrentFullscreenParams);
+        mOutlineProvider.updateParams(
+                mCurrentFullscreenParams,
+                mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx);
+        invalidateOutline();
+    }
+
+    void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
+        if (getRecentsView() == null) {
+            return;
+        }
+        mCurrentFullscreenParams.setProgress(
+                mFullscreenProgress,
+                getRecentsView().getScaleX(),
+                getWidth(), mActivity.getDeviceProfile(),
+                previewPositionHelper);
+    }
+
+    /**
+     * Updates TaskView scaling and translation required to support variable width if enabled, while
+     * ensuring TaskView fits into screen in fullscreen.
+     */
+    void updateTaskSize() {
+        ViewGroup.LayoutParams params = getLayoutParams();
+        float fullscreenScale;
+        float boxTranslationY;
+        int expectedWidth;
+        int expectedHeight;
+        if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+            final int thumbnailPadding =
+                    mActivity.getDeviceProfile().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;
+            }
+
+            // 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);
+            }
+
+            // Align to top of task Rect.
+            boxTranslationY = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f;
+        } else {
+            fullscreenScale = 1f;
+            boxTranslationY = 0f;
+            expectedWidth = ViewGroup.LayoutParams.MATCH_PARENT;
+            expectedHeight = ViewGroup.LayoutParams.MATCH_PARENT;
+        }
+
+        setFullscreenScale(fullscreenScale);
+        setBoxTranslationY(boxTranslationY);
+        if (params.width != expectedWidth || params.height != expectedHeight) {
+            params.width = expectedWidth;
+            params.height = expectedHeight;
+            setLayoutParams(params);
+        }
+    }
+
+    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);
+        return Utilities.mapRange(progress, 0, endTranslation);
+    }
+
+    public boolean isRunningTask() {
+        if (getRecentsView() == null) {
+            return false;
+        }
+        return this == getRecentsView().getRunningTaskView();
+    }
+
+    public boolean isFocusedTask() {
+        if (getRecentsView() == null) {
+            return false;
+        }
+        return this == getRecentsView().getFocusedTaskView();
+    }
+
+    public void setShowScreenshot(boolean showScreenshot) {
+        mShowScreenshot = showScreenshot;
+    }
+
+    public boolean showScreenshot() {
+        if (!isRunningTask()) {
+            return true;
+        }
+        return mShowScreenshot;
+    }
+
+    public void setOverlayEnabled(boolean overlayEnabled) {
+        mSnapshotView.setOverlayEnabled(overlayEnabled);
+    }
+
+    public void initiateSplitSelect(SplitPositionOption splitPositionOption) {
+        AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_TASK_MENU);
+        getRecentsView().initiateSplitSelect(this, splitPositionOption);
+    }
+
+    /**
+     * Set a color tint on the snapshot and supporting views.
+     */
+    public void setColorTint(float amount, int tintColor) {
+        mSnapshotView.setDimAlpha(amount);
+        mIconView.setIconColorTint(tintColor, amount);
+        mDigitalWellBeingToast.setBannerColorTint(tintColor, amount);
+    }
+
+    /**
+     * We update and subsequently draw these in {@link #setFullscreenProgress(float)}.
+     */
+    public static class FullscreenDrawParams {
+
+        private final float mCornerRadius;
+        private final float mWindowCornerRadius;
+
+        public float mFullscreenProgress;
+        public RectF mCurrentDrawnInsets = new RectF();
+        public float mCurrentDrawnCornerRadius;
+        /** The current scale we apply to the thumbnail to adjust for new left/right insets. */
+        public float mScale = 1;
+
+        public FullscreenDrawParams(Context context) {
+            mCornerRadius = TaskCornerRadius.get(context);
+            mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
+
+            mCurrentDrawnCornerRadius = mCornerRadius;
+        }
+
+        /**
+         * Sets the progress in range [0, 1]
+         */
+        public void setProgress(float fullscreenProgress, float parentScale, int previewWidth,
+                DeviceProfile dp, PreviewPositionHelper pph) {
+            mFullscreenProgress = fullscreenProgress;
+            RectF insets = pph.getInsetsToDrawInFullscreen();
+
+            float currentInsetsLeft = insets.left * fullscreenProgress;
+            float currentInsetsRight = insets.right * fullscreenProgress;
+            mCurrentDrawnInsets.set(currentInsetsLeft, insets.top * fullscreenProgress,
+                    currentInsetsRight, insets.bottom * fullscreenProgress);
+            float fullscreenCornerRadius = dp.isMultiWindowMode ? 0 : mWindowCornerRadius;
+
+            mCurrentDrawnCornerRadius =
+                    Utilities.mapRange(fullscreenProgress, mCornerRadius, fullscreenCornerRadius)
+                            / parentScale;
+
+            // 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.
+            if (previewWidth > 0) {
+                mScale = previewWidth / (previewWidth + currentInsetsLeft + currentInsetsRight);
+            }
+        }
+
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index a412b39..dc73a9a 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -16,7 +16,15 @@
 
 package com.android.quickstep;
 
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.quickstep.views.RecentsView;
 
 import org.junit.rules.RuleChain;
 import org.junit.rules.TestRule;
@@ -31,4 +39,49 @@
                 outerRule(new NavigationModeSwitchRule(mLauncher)).
                 around(super.getRulesInsideActivityMonitor());
     }
+
+    @Override
+    protected void onLauncherActivityClose(Launcher launcher) {
+        RecentsView recentsView = launcher.getOverviewPanel();
+        if (recentsView != null) {
+            recentsView.finishRecentsAnimation(true, null);
+        }
+    }
+
+    @Override
+    protected void checkLauncherState(Launcher launcher, ContainerType expectedContainerType,
+            boolean isResumed, boolean isStarted) {
+        if (!isInLiveTileMode(launcher, expectedContainerType)) {
+            super.checkLauncherState(launcher, expectedContainerType, isResumed, isStarted);
+        } else {
+            assertTrue("[Live Tile] hasBeenResumed() == isStarted(), hasBeenResumed(): "
+                            + isResumed, isResumed != isStarted);
+        }
+    }
+
+    @Override
+    protected void checkLauncherStateInOverview(Launcher launcher,
+            ContainerType expectedContainerType, boolean isStarted, boolean isResumed) {
+        if (!isInLiveTileMode(launcher, expectedContainerType)) {
+            super.checkLauncherStateInOverview(launcher, expectedContainerType, isStarted,
+                    isResumed);
+        } else {
+            assertTrue(
+                    "[Live Tile] Launcher is not started or has been resumed in state: "
+                            + expectedContainerType,
+                    isStarted && !isResumed);
+        }
+    }
+
+    private boolean isInLiveTileMode(Launcher launcher,
+            LauncherInstrumentation.ContainerType expectedContainerType) {
+        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()
+                || expectedContainerType != LauncherInstrumentation.ContainerType.OVERVIEW) {
+            return false;
+        }
+
+        RecentsView recentsView = launcher.getOverviewPanel();
+        return recentsView.getSizeStrategy().isInLiveTileMode()
+                && recentsView.getRunningTaskId() != -1;
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
deleted file mode 100644
index 5904fcd..0000000
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ /dev/null
@@ -1,162 +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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetId;
-import android.content.ComponentName;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.os.Process;
-import android.view.View;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.appprediction.PredictionRowView;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.model.AppLaunchTracker;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class AppPredictionsUITests extends AbstractQuickStepTest {
-
-    private LauncherActivityInfo mSampleApp1;
-    private LauncherActivityInfo mSampleApp2;
-    private LauncherActivityInfo mSampleApp3;
-
-    private AppPredictor.Callback mCallback;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-
-        List<LauncherActivityInfo> activities = mTargetContext.getSystemService(LauncherApps.class)
-                .getActivityList(null, Process.myUserHandle());
-        mSampleApp1 = activities.get(0);
-        mSampleApp2 = activities.get(1);
-        mSampleApp3 = activities.get(2);
-
-        // Disable app tracker
-        AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
-        PredictionUiStateManager.INSTANCE.initializeForTesting(null);
-
-        mCallback = PredictionUiStateManager.INSTANCE.get(mTargetContext).appPredictorCallback(
-                Client.HOME);
-
-        mDevice.setOrientationNatural();
-    }
-
-    @After
-    public void tearDown() throws Throwable {
-        AppLaunchTracker.INSTANCE.initializeForTesting(null);
-        PredictionUiStateManager.INSTANCE.initializeForTesting(null);
-        mDevice.unfreezeRotation();
-    }
-
-    /**
-     * Test that prediction UI is updated as soon as we get predictions from the system
-     */
-    @Test
-    public void testPredictionExistsInAllApps() {
-        mLauncher.pressHome().switchToAllApps();
-
-        // Dispatch an update
-        sendPredictionUpdate(mSampleApp1, mSampleApp2);
-        // The first update should apply immediately.
-        waitForLauncherCondition("Predictions were not updated in loading state",
-                launcher -> getPredictedApp(launcher).size() == 2);
-    }
-
-    /**
-     * Test that prediction update is deferred if it is already visible
-     */
-    @Test
-    public void testPredictionsDeferredUntilHome() {
-        mDevice.pressHome();
-        sendPredictionUpdate(mSampleApp1, mSampleApp2);
-        mLauncher.pressHome().switchToAllApps();
-        waitForLauncherCondition("Predictions were not updated in loading state",
-                launcher -> getPredictedApp(launcher).size() == 2);
-
-        // Update predictions while all-apps is visible
-        sendPredictionUpdate(mSampleApp1, mSampleApp2, mSampleApp3);
-        assertEquals(2, getFromLauncher(this::getPredictedApp).size());
-
-        // Go home and go back to all-apps
-        mLauncher.pressHome().switchToAllApps();
-        assertEquals(3, getFromLauncher(this::getPredictedApp).size());
-    }
-
-    @Test
-    public void testPredictionsDisabled() {
-        mDevice.pressHome();
-        sendPredictionUpdate();
-        mLauncher.pressHome().switchToAllApps();
-
-        waitForLauncherCondition("Predictions were not updated in loading state",
-                launcher -> launcher.getAppsView().getFloatingHeaderView()
-                        .findFixedRowByType(PredictionRowView.class).getVisibility() == View.GONE);
-        assertFalse(PredictionUiStateManager.INSTANCE.get(mTargetContext)
-                .getCurrentState().isEnabled);
-    }
-
-    public ArrayList<BubbleTextView> getPredictedApp(Launcher launcher) {
-        PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
-                .findFixedRowByType(PredictionRowView.class);
-
-        ArrayList<BubbleTextView> predictedAppViews = new ArrayList<>();
-        for (int i = 0; i < container.getChildCount(); i++) {
-            View view = container.getChildAt(i);
-            if (view instanceof BubbleTextView && view.getVisibility() == View.VISIBLE) {
-                predictedAppViews.add((BubbleTextView) view);
-            }
-        }
-        return predictedAppViews;
-    }
-
-    private void sendPredictionUpdate(LauncherActivityInfo... activities) {
-        getOnUiThread(() -> {
-            List<AppTarget> targets = new ArrayList<>(activities.length);
-            for (LauncherActivityInfo info : activities) {
-                ComponentName cn = info.getComponentName();
-                AppTarget target = new AppTarget.Builder(
-                        new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser())
-                        .setClassName(cn.getClassName())
-                        .build();
-                targets.add(target);
-            }
-            mCallback.onTargetsAvailable(targets);
-            return null;
-        });
-    }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index ccfa3fc..3e84a76 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -11,7 +11,6 @@
 import android.app.PendingIntent;
 import android.app.usage.UsageStatsManager;
 import android.content.Intent;
-import android.os.Build;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -34,9 +33,6 @@
 
     @Test
     public void testToast() throws Exception {
-        // b/150303529
-        if (Build.MODEL.contains("Cuttlefish")) return;
-
         startAppFast(CALCULATOR_PACKAGE);
 
         final UsageStatsManager usageStatsManager =
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index b9e0f62..a683d01 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -23,6 +23,7 @@
 import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
 import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT;
+import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_BROADCAST_TIMEOUT_SECS;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp;
 import static com.android.launcher3.ui.AbstractLauncherUiTest.startAppFast;
@@ -41,6 +42,7 @@
 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;
@@ -54,11 +56,13 @@
 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.FailureRewriterRule;
 import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.quickstep.views.RecentsView;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
@@ -66,6 +70,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.model.Statement;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -100,9 +106,8 @@
         }
 
         mOrderSensitiveRules = RuleChain
-                .outerRule(new FailureRewriterRule())
-                .around(new NavigationModeSwitchRule(mLauncher))
-                .around(new FailureWatcher(mDevice));
+                .outerRule(new NavigationModeSwitchRule(mLauncher))
+                .around(new FailureWatcher(mDevice, mLauncher));
 
         mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
                 getHomeIntentInPackage(context),
@@ -112,11 +117,16 @@
             @Override
             public void evaluate() throws Throwable {
                 TestCommandReceiver.callCommand(TestCommandReceiver.ENABLE_TEST_LAUNCHER);
+                OverviewUpdateHandler updateHandler =
+                        MAIN_EXECUTOR.submit(OverviewUpdateHandler::new).get();
                 UiDevice.getInstance(getInstrumentation()).executeShellCommand(
                         getLauncherCommand(mOtherLauncherActivity));
+                updateHandler.mChangeCounter
+                        .await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
                 try {
                     base.evaluate();
                 } finally {
+                    MAIN_EXECUTOR.submit(updateHandler::destroy).get();
                     TestCommandReceiver.callCommand(TestCommandReceiver.DISABLE_TEST_LAUNCHER);
                     UiDevice.getInstance(getInstrumentation()).executeShellCommand(
                             getLauncherCommand(getLauncherInMyProcess()));
@@ -131,10 +141,21 @@
                     TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
                     getString("result"));
         }
-        // b/143488140
-        mDevice.pressHome();
-        mDevice.waitForIdle();
-        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+    }
+
+    @Before
+    public void setUp() {
+        mLauncher.onTestStart();
+    }
+
+    @After
+    public void tearDown() {
+        try {
+            // Limits UI tests affecting tests running after them.
+            AbstractQuickStepTest.checkDetectedLeaks(mLauncher);
+        } finally {
+            mLauncher.onTestFinish();
+        }
     }
 
     // b/143488140
@@ -166,9 +187,15 @@
 
     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;
             }
@@ -194,8 +221,9 @@
                 () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
 
         BaseOverview overview = mLauncher.getBackground().switchToOverview();
-        executeOnRecents(recents ->
-                assertTrue("Don't have at least 3 tasks", getTaskCount(recents) >= 3));
+        executeOnRecents(recents -> {
+            assertTrue("Don't have at least 3 tasks", getTaskCount(recents) >= 3);
+        });
 
         // Test flinging forward and backward.
         overview.flingForward();
@@ -213,7 +241,7 @@
         OverviewTask task = overview.getCurrentTask();
         assertNotNull("overview.getCurrentTask() returned null (1)", task);
         assertNotNull("OverviewTask.open returned null", task.open());
-        assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
+        assertTrue("Test activity didn't open from Overview", TestHelpers.wait(Until.hasObject(
                 By.pkg(getAppPackageName()).text("TestActivity2")),
                 DEFAULT_UI_TIMEOUT));
 
@@ -230,7 +258,7 @@
 
         // Test dismissing all tasks.
         pressHomeAndGoToOverview().dismissAllTasks();
-        assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
+        assertTrue("Fallback Launcher not visible", TestHelpers.wait(Until.hasObject(By.pkg(
                 mOtherLauncherActivity.packageName)), WAIT_TIME_MS));
     }
 
@@ -241,4 +269,30 @@
     private int getTaskCount(RecentsActivity recents) {
         return recents.<RecentsView>getOverviewPanel().getTaskViewCount();
     }
+
+    private class OverviewUpdateHandler {
+
+        final RecentsAnimationDeviceState mRads;
+        final OverviewComponentObserver mObserver;
+        final CountDownLatch mChangeCounter;
+
+        OverviewUpdateHandler() {
+            Context ctx = getInstrumentation().getTargetContext();
+            mRads = new RecentsAnimationDeviceState(ctx);
+            mObserver = new OverviewComponentObserver(ctx, mRads);
+            mChangeCounter = new CountDownLatch(1);
+            if (mObserver.getHomeIntent().getComponent()
+                    .getPackageName().equals(mOtherLauncherActivity.packageName)) {
+                // Home already same
+                mChangeCounter.countDown();
+            } else {
+                mObserver.setOverviewChangeListener(b -> mChangeCounter.countDown());
+            }
+        }
+
+        void destroy() {
+            mObserver.onDestroy();
+            mRads.destroy();
+        }
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 40265c4..67840d1 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -168,7 +168,7 @@
 
         Log.d(TAG, "setActiveOverlay: " + overlayPackage + "...");
         UiDevice.getInstance(getInstrumentation()).executeShellCommand(
-                "cmd overlay enable-exclusive " + overlayPackage);
+                "cmd overlay enable-exclusive --category " + overlayPackage);
 
         if (currentSysUiNavigationMode() != expectedMode) {
             final CountDownLatch latch = new CountDownLatch(1);
@@ -188,7 +188,7 @@
                     SYS_UI_NAVIGATION_MODE.removeModeChangeListener(listener));
 
             Wait.atMost(() -> "Navigation mode didn't change to " + expectedMode,
-                    () -> currentSysUiNavigationMode() == expectedMode, 60000 /* b/148422894 */,
+                    () -> currentSysUiNavigationMode() == expectedMode, WAIT_TIME_MS,
                     launcher);
             // b/139137636
 //            assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
@@ -200,9 +200,9 @@
                 () -> launcher.getNavigationModel() == expectedMode, WAIT_TIME_MS, launcher);
 
         Wait.atMost(() -> "Switching nav mode: "
-                        + launcher.getNavigationModeMismatchError(),
-                () -> launcher.getNavigationModeMismatchError() == null,
-                60000 /* b/148422894 */, launcher);
+                        + launcher.getNavigationModeMismatchError(false),
+                () -> launcher.getNavigationModeMismatchError(false) == null,
+                WAIT_TIME_MS, launcher);
         AbstractLauncherUiTest.checkDetectedLeaks(launcher);
         return true;
     }
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 9732cdc..6e19436 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -100,6 +100,7 @@
             // The test action.
             mLauncher.getBackground().switchToOverview();
         }
+        closeLauncherActivity();
         mLauncher.pressHome();
     }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index ecd4e2b..b856e0b 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
+import android.util.Log;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,20 +32,19 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.tapl.AllApps;
-import com.android.launcher3.tapl.AllAppsFromOverview;
 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.tapl.TestHelpers;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 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;
 
@@ -94,19 +94,6 @@
     }
 
     @Test
-    public void testAllAppsFromOverview() throws Exception {
-        if (!mLauncher.hasAllAppsInOverview()) {
-            return;
-        }
-
-        // Test opening all apps from Overview.
-        assertNotNull("switchToAllApps() returned null",
-                mLauncher.getWorkspace().switchToOverview().switchToAllApps());
-
-        TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllAppsFromOverview());
-    }
-
-    @Test
     @PortraitLandscape
     public void testOverview() throws Exception {
         startTestAppsWithCheck();
@@ -159,28 +146,6 @@
                 launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
                         numTasks - 1, getTaskCount(launcher)));
 
-        if (mLauncher.hasAllAppsInOverview() && (!TestHelpers.isInLauncherProcess()
-                || getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape))) {
-            // Test switching to all apps and back.
-            final AllAppsFromOverview allApps = overview.switchToAllApps();
-            assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
-            assertTrue("Launcher internal state is not All Apps (1)",
-                    isInState(() -> LauncherState.ALL_APPS));
-
-            overview = allApps.switchBackToOverview();
-            assertNotNull("allApps.switchBackToOverview() returned null", overview);
-            assertTrue("Launcher internal state didn't switch to Overview",
-                    isInState(() -> LauncherState.OVERVIEW));
-
-            // Test UIDevice.pressBack()
-            overview.switchToAllApps();
-            assertNotNull("overview.switchToAllApps() returned null (2)", allApps);
-            assertTrue("Launcher internal state is not All Apps (2)",
-                    isInState(() -> LauncherState.ALL_APPS));
-            mDevice.pressBack();
-            mLauncher.getOverview();
-        }
-
         // Test UIDevice.pressHome, once we are in AllApps.
         mDevice.pressHome();
         waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
@@ -200,13 +165,17 @@
     @NavigationModeSwitch
     @PortraitLandscape
     public void testOverviewActions() throws Exception {
-        if (mLauncher.getNavigationModel() != NavigationModel.TWO_BUTTON) {
-            startTestAppsWithCheck();
-            OverviewActions actionsView =
-                    mLauncher.pressHome().switchToOverview().getOverviewActions();
-            actionsView.clickAndDismissScreenshot();
-            actionsView.clickAndDismissShare();
-        }
+        // Experimenting for b/165029151:
+        final Overview overview = mLauncher.pressHome().switchToOverview();
+        if (overview.hasTasks()) overview.dismissAllTasks();
+        mLauncher.pressHome();
+        //
+
+        startTestAppsWithCheck();
+        OverviewActions actionsView =
+                mLauncher.pressHome().switchToOverview().getOverviewActions();
+        actionsView.clickAndDismissScreenshot();
+        actionsView.clickAndDismissShare();
     }
 
     private int getCurrentOverviewPage(Launcher launcher) {
@@ -218,20 +187,6 @@
     }
 
     @Test
-    public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
-        if (!mLauncher.hasAllAppsInOverview()) {
-            return;
-        }
-
-        final AllApps allApps =
-                mLauncher.getWorkspace().switchToOverview().switchToAllApps();
-        assertTrue("Launcher internal state is not All Apps",
-                isInState(() -> LauncherState.ALL_APPS));
-
-        TaplTestsLauncher3.runIconLaunchFromAllAppsTest(this, allApps);
-    }
-
-    @Test
     @NavigationModeSwitch
     @PortraitLandscape
     public void testSwitchToOverview() throws Exception {
@@ -264,6 +219,7 @@
     }
 
     @Test
+    @Ignore("b/197802324")
     @PortraitLandscape
     public void testAllAppsFromHome() throws Exception {
         // Test opening all apps
@@ -304,6 +260,10 @@
             assertTrue("The second app we should have quick switched to is not running",
                     isTestActivityRunning(2));
         }
+        background = getAndAssertBackground();
+        background.quickSwitchToPreviousAppSwipeLeft();
+        assertTrue("The 2nd app we should have quick switched to is not running",
+                isTestActivityRunning(3));
         getAndAssertBackground();
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index 115294a..f33abb0 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -49,7 +49,6 @@
 import androidx.test.uiautomator.UiDevice;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.tapl.Background;
@@ -58,6 +57,7 @@
 import com.android.launcher3.testcomponent.TestCommandReceiver;
 import com.android.launcher3.ui.TaplTestsLauncher3;
 import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 
 import org.junit.Before;
@@ -71,7 +71,7 @@
 
 /**
  * Test to verify view inflation does not happen during swipe up.
- * To verify view inflation, we setup a dummy ViewConfiguration and check if any call to that class
+ * To verify view inflation, we setup a stub ViewConfiguration and check if any call to that class
  * does from a View.init method or not.
  *
  * Alternative approaches considered:
@@ -79,10 +79,7 @@
  *        directly (ex: new LinearLayout)
  *    Using ExtendedMockito: Mocking static methods from platform classes (loaded in zygote) makes
  *        the main thread extremely slow and untestable
- *
- * Suppressed until b/141579810 is resolved
  */
-@Suppress
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest {
@@ -117,6 +114,7 @@
 
     @Test
     @NavigationModeSwitch(mode = ZERO_BUTTON)
+    @Suppress // until b/190618549 is fixed
     public void testSwipeUpFromApp() throws Exception {
         try {
             // Go to overview once so that all views are initialized and cached
@@ -137,18 +135,20 @@
 
     @Test
     @NavigationModeSwitch(mode = ZERO_BUTTON)
+    @Suppress // until b/190729479 is fixed
     public void testSwipeUpFromApp_widget_update() {
-        String dummyText = "Some random dummy text";
+        String stubText = "Some random stub text";
 
         executeSwipeUpTestWithWidget(
                 widgetId -> { },
                 widgetId -> AppWidgetManager.getInstance(getContext())
-                        .updateAppWidget(widgetId, createMainWidgetViews(dummyText)),
-                dummyText);
+                        .updateAppWidget(widgetId, createMainWidgetViews(stubText)),
+                stubText);
     }
 
     @Test
     @NavigationModeSwitch(mode = ZERO_BUTTON)
+    @Suppress // until b/190729479 is fixed
     public void testSwipeUp_with_list_widgets() {
         SimpleViewsFactory viewFactory = new SimpleViewsFactory();
         viewFactory.viewCount = 1;
@@ -188,6 +188,11 @@
             LauncherSettings.Settings.call(mResolver,
                     LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
             LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
+            // Make sure the widget is big enough to show a list of items
+            info.minSpanX = 2;
+            info.minSpanY = 2;
+            info.spanX = 2;
+            info.spanY = 2;
             LauncherAppWidgetInfo item = createWidgetInfo(info, getTargetContext(), true);
 
             addItemToScreen(item);
diff --git a/res/animator-v23/discovery_bounce.xml b/res/animator-v23/discovery_bounce.xml
deleted file mode 100644
index f554853..0000000
--- a/res/animator-v23/discovery_bounce.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="2166"
-    android:repeatCount="5">
-    <propertyValuesHolder
-        android:propertyName="progress"
-        android:valueType="floatType">
-        <keyframe
-            android:fraction="0"
-            android:value="1f" />
-        <keyframe
-            android:fraction="0.246"
-            android:value="1f" />
-        <keyframe
-            android:fraction=".423"
-            android:interpolator="@interpolator/disco_bounce"
-            android:value="0.9738f" />
-        <keyframe
-            android:fraction="0.754"
-            android:interpolator="@interpolator/disco_bounce"
-            android:value="1f" />
-        <keyframe
-            android:fraction="1"
-            android:value="1f" />
-    </propertyValuesHolder>
-</objectAnimator>
diff --git a/res/animator/discovery_bounce.xml b/res/animator/discovery_bounce.xml
index f02ebdb..f554853 100644
--- a/res/animator/discovery_bounce.xml
+++ b/res/animator/discovery_bounce.xml
@@ -16,20 +16,28 @@
 ** limitations under the License.
 */
 -->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:ordering="sequentially">
-    <objectAnimator
-        android:duration="166"
-        android:interpolator="@interpolator/disco_bounce"
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="2166"
+    android:repeatCount="5">
+    <propertyValuesHolder
         android:propertyName="progress"
-        android:startOffset="750"
-        android:valueFrom="1f"
-        android:valueTo="0.9438f"
-        android:valueType="floatType" />
-    <objectAnimator
-        android:duration="500"
-        android:interpolator="@interpolator/disco_bounce"
-        android:propertyName="progress"
-        android:valueTo="1f"
-        android:valueType="floatType" />
-</set>
+        android:valueType="floatType">
+        <keyframe
+            android:fraction="0"
+            android:value="1f" />
+        <keyframe
+            android:fraction="0.246"
+            android:value="1f" />
+        <keyframe
+            android:fraction=".423"
+            android:interpolator="@interpolator/disco_bounce"
+            android:value="0.9738f" />
+        <keyframe
+            android:fraction="0.754"
+            android:interpolator="@interpolator/disco_bounce"
+            android:value="1f" />
+        <keyframe
+            android:fraction="1"
+            android:value="1f" />
+    </propertyValuesHolder>
+</objectAnimator>
diff --git a/go/res/values/dimens.xml b/res/color-night-v31/all_apps_tab_background_selected.xml
similarity index 74%
copy from go/res/values/dimens.xml
copy to res/color-night-v31/all_apps_tab_background_selected.xml
index f1b1053..b7c9ff6 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-night-v31/all_apps_tab_background_selected.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_accent2_100"/>
+</selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color-night-v31/all_apps_tab_text.xml
similarity index 67%
copy from go/res/values/dimens.xml
copy to res/color-night-v31/all_apps_tab_text.xml
index f1b1053..83237b4 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-night-v31/all_apps_tab_text.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_50" android:state_selected="true"/>
+    <item android:color="@android:color/system_neutral2_700"/>
+</selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color-night-v31/folder_background_dark.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to res/color-night-v31/folder_background_dark.xml
index f1b1053..a5bd636 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-night-v31/folder_background_dark.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<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/go/res/values/dimens.xml b/res/color-night-v31/popup_shade_first.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to res/color-night-v31/popup_shade_first.xml
index f1b1053..ba74128 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-night-v31/popup_shade_first.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="20" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color-night-v31/popup_shade_second.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to res/color-night-v31/popup_shade_second.xml
index f1b1053..efc6205 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-night-v31/popup_shade_second.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="15" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color-night-v31/popup_shade_third.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to res/color-night-v31/popup_shade_third.xml
index f1b1053..591c7ed 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-night-v31/popup_shade_third.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="10" />
+</selector>
diff --git a/res/values-v26/bools.xml b/res/color-night-v31/surface.xml
similarity index 74%
copy from res/values-v26/bools.xml
copy to res/color-night-v31/surface.xml
index ad8c7a1..fbc9e43 100644
--- a/res/values-v26/bools.xml
+++ b/res/color-night-v31/surface.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_800" />
+</selector>
diff --git a/res/values-v26/bools.xml b/res/color-night-v31/widgets_picker_scrim.xml
similarity index 72%
copy from res/values-v26/bools.xml
copy to res/color-night-v31/widgets_picker_scrim.xml
index ad8c7a1..be7010b 100644
--- a/res/values-v26/bools.xml
+++ b/res/color-night-v31/widgets_picker_scrim.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,6 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="@android:color/system_neutral1_900" android:alpha="0.8" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color-night/all_apps_tab_background_selected.xml
similarity index 77%
copy from go/res/values/dimens.xml
copy to res/color-night/all_apps_tab_background_selected.xml
index f1b1053..b22bc8b 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-night/all_apps_tab_background_selected.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#BFEBE3"/>
+</selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color-night/all_apps_tab_text.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to res/color-night/all_apps_tab_text.xml
index f1b1053..183af01 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-night/all_apps_tab_text.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#F0F0F0" android:state_selected="true"/>
+    <item android:color="#464646"/>
+</selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color-v31/all_apps_tab_background_selected.xml
similarity index 74%
copy from go/res/values/dimens.xml
copy to res/color-v31/all_apps_tab_background_selected.xml
index f1b1053..dac8fa2 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/all_apps_tab_background_selected.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_accent1_100"/>
+</selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color-v31/all_apps_tab_text.xml
similarity index 67%
copy from go/res/values/dimens.xml
copy to res/color-v31/all_apps_tab_text.xml
index f1b1053..c3520a7 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/all_apps_tab_text.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_900" android:state_selected="true"/>
+    <item android:color="@android:color/system_neutral2_700"/>
+</selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color-v31/all_apps_tabs_background.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to res/color-v31/all_apps_tabs_background.xml
index f1b1053..30757b0 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/all_apps_tabs_background.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_500" android:lStar="97" />
+</selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color-v31/folder_background_light.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to res/color-v31/folder_background_light.xml
index f1b1053..e3c7e7d 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/folder_background_light.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_neutral1_50"
+        android:lStar="98" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color-v31/overview_scrim.xml
similarity index 73%
copy from go/res/values/dimens.xml
copy to res/color-v31/overview_scrim.xml
index f1b1053..8079995 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/overview_scrim.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="@android:color/system_neutral2_500" android:lStar="87" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color-v31/overview_scrim_dark.xml
similarity index 74%
copy from go/res/values/dimens.xml
copy to res/color-v31/overview_scrim_dark.xml
index f1b1053..b8ed774 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/overview_scrim_dark.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="@android:color/system_neutral1_800" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color-v31/popup_shade_first.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to res/color-v31/popup_shade_first.xml
index f1b1053..28d9155 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/popup_shade_first.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="98" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color-v31/popup_shade_second.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to res/color-v31/popup_shade_second.xml
index f1b1053..dec562c 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/popup_shade_second.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="95" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color-v31/popup_shade_third.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to res/color-v31/popup_shade_third.xml
index f1b1053..582232c 100644
--- a/go/res/values/dimens.xml
+++ b/res/color-v31/popup_shade_third.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="?attr/popupColorPrimary"
+        android:lStar="90" />
+</selector>
diff --git a/res/values-v26/bools.xml b/res/color-v31/surface.xml
similarity index 72%
copy from res/values-v26/bools.xml
copy to res/color-v31/surface.xml
index ad8c7a1..30f3032 100644
--- a/res/values-v26/bools.xml
+++ b/res/color-v31/surface.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_500" android:lStar="98"/>
+</selector>
diff --git a/res/values-v26/bools.xml b/res/color-v31/widgets_picker_scrim.xml
similarity index 72%
copy from res/values-v26/bools.xml
copy to res/color-v31/widgets_picker_scrim.xml
index ad8c7a1..648824a 100644
--- a/res/values-v26/bools.xml
+++ b/res/color-v31/widgets_picker_scrim.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,6 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="@android:color/system_neutral1_200" android:alpha="0.8" />
+</selector>
diff --git a/res/color-v24/all_apps_bg_hand_fill.xml b/res/color/all_apps_bg_hand_fill.xml
similarity index 100%
rename from res/color-v24/all_apps_bg_hand_fill.xml
rename to res/color/all_apps_bg_hand_fill.xml
diff --git a/res/color-v24/all_apps_bg_hand_fill_dark.xml b/res/color/all_apps_bg_hand_fill_dark.xml
similarity index 100%
rename from res/color-v24/all_apps_bg_hand_fill_dark.xml
rename to res/color/all_apps_bg_hand_fill_dark.xml
diff --git a/go/res/values/dimens.xml b/res/color/all_apps_tab_background_selected.xml
similarity index 84%
rename from go/res/values/dimens.xml
rename to res/color/all_apps_tab_background_selected.xml
index f1b1053..5cb9bd8 100644
--- a/go/res/values/dimens.xml
+++ b/res/color/all_apps_tab_background_selected.xml
@@ -13,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#8DF5E3"/>
+</selector>
\ No newline at end of file
diff --git a/res/color/all_apps_tab_text.xml b/res/color/all_apps_tab_text.xml
index f0c6310..dace380 100644
--- a/res/color/all_apps_tab_text.xml
+++ b/res/color/all_apps_tab_text.xml
@@ -14,6 +14,6 @@
      limitations under the License.
 -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="?android:attr/colorAccent" android:state_selected="true"/>
-    <item android:color="?android:attr/textColorTertiary"/>
+    <item android:color="#1B1B1B" android:state_selected="true"/>
+    <item android:color="#464646"/>
 </selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color/all_apps_tabs_background.xml
similarity index 77%
copy from go/res/values/dimens.xml
copy to res/color/all_apps_tabs_background.xml
index f1b1053..a4b7d1f 100644
--- a/go/res/values/dimens.xml
+++ b/res/color/all_apps_tabs_background.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#F6F6F6"/>
+</selector>
\ No newline at end of file
diff --git a/res/values-v26/bools.xml b/res/color/arrow_tip_view_bg.xml
similarity index 74%
copy from res/values-v26/bools.xml
copy to res/color/arrow_tip_view_bg.xml
index ad8c7a1..91eed50 100644
--- a/res/values-v26/bools.xml
+++ b/res/color/arrow_tip_view_bg.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/colorAccent" />
+</selector>
diff --git a/res/values-v26/bools.xml b/res/color/arrow_tip_view_content.xml
similarity index 73%
copy from res/values-v26/bools.xml
copy to res/color/arrow_tip_view_content.xml
index ad8c7a1..7d7f98b 100644
--- a/res/values-v26/bools.xml
+++ b/res/color/arrow_tip_view_content.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/textColorPrimaryInverse" />
+</selector>
diff --git a/res/values-v26/bools.xml b/res/color/button_bg.xml
similarity index 74%
copy from res/values-v26/bools.xml
copy to res/color/button_bg.xml
index ad8c7a1..91eed50 100644
--- a/res/values-v26/bools.xml
+++ b/res/color/button_bg.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/colorAccent" />
+</selector>
diff --git a/res/values-v26/bools.xml b/res/color/button_text.xml
similarity index 73%
copy from res/values-v26/bools.xml
copy to res/color/button_text.xml
index ad8c7a1..7d7f98b 100644
--- a/res/values-v26/bools.xml
+++ b/res/color/button_text.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/textColorPrimaryInverse" />
+</selector>
diff --git a/res/color/cell_layout_bg_color_active.xml b/res/color/cell_layout_bg_color_active.xml
new file mode 100644
index 0000000..d1a3d7c
--- /dev/null
+++ b/res/color/cell_layout_bg_color_active.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="0.3"
+      android:color="?android:attr/colorAccent"/>
+</selector>
diff --git a/res/color/cell_layout_bg_color_inactive.xml b/res/color/cell_layout_bg_color_inactive.xml
new file mode 100644
index 0000000..0632100
--- /dev/null
+++ b/res/color/cell_layout_bg_color_inactive.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:alpha="0.25"
+        android:color="?android:attr/colorAccent"/>
+</selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color/drop_target_text.xml
similarity index 66%
copy from go/res/values/dimens.xml
copy to res/color/drop_target_text.xml
index f1b1053..18d78e7 100644
--- a/go/res/values/dimens.xml
+++ b/res/color/drop_target_text.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?workspaceAccentColor" android:state_selected="false" />
+    <item android:color="?dropTargetHoverTextColor" android:state_selected="true" />
+</selector>
\ No newline at end of file
diff --git a/res/color/overview_button.xml b/res/color/overview_button.xml
index 6ac36bf..aa48b78 100644
--- a/res/color/overview_button.xml
+++ b/res/color/overview_button.xml
@@ -2,10 +2,10 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
         android:alpha="1"
-        android:color="?attr/workspaceTextColor"
+        android:color="?android:attr/textColorPrimary"
         android:state_enabled="true" />
     <item
         android:alpha="?android:disabledAlpha"
-        android:color="?attr/workspaceTextColor"
+        android:color="?android:attr/textColorPrimary"
         android:state_enabled="false" />
 </selector>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/color/overview_scrim.xml
similarity index 77%
copy from go/res/values/dimens.xml
copy to res/color/overview_scrim.xml
index f1b1053..48cf576 100644
--- a/go/res/values/dimens.xml
+++ b/res/color/overview_scrim.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="#30000000" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color/overview_scrim_dark.xml
similarity index 77%
copy from go/res/values/dimens.xml
copy to res/color/overview_scrim_dark.xml
index f1b1053..48cf576 100644
--- a/go/res/values/dimens.xml
+++ b/res/color/overview_scrim_dark.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="#30000000" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color/popup_shade_first.xml
similarity index 75%
copy from go/res/values/dimens.xml
copy to res/color/popup_shade_first.xml
index f1b1053..151190b 100644
--- a/go/res/values/dimens.xml
+++ b/res/color/popup_shade_first.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:color="?attr/popupShadeFirst" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color/popup_shade_second.xml
similarity index 75%
copy from go/res/values/dimens.xml
copy to res/color/popup_shade_second.xml
index f1b1053..8660850 100644
--- a/go/res/values/dimens.xml
+++ b/res/color/popup_shade_second.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:color="?attr/popupShadeSecond" />
+</selector>
diff --git a/go/res/values/dimens.xml b/res/color/popup_shade_third.xml
similarity index 75%
copy from go/res/values/dimens.xml
copy to res/color/popup_shade_third.xml
index f1b1053..9544728 100644
--- a/go/res/values/dimens.xml
+++ b/res/color/popup_shade_third.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:color="?attr/popupShadeThird" />
+</selector>
\ No newline at end of file
diff --git a/res/values-v26/bools.xml b/res/color/surface.xml
similarity index 73%
copy from res/values-v26/bools.xml
copy to res/color/surface.xml
index ad8c7a1..6150110 100644
--- a/res/values-v26/bools.xml
+++ b/res/color/surface.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="?android:attr/colorBackgroundFloating" />
+</selector>
diff --git a/res/values-v26/bools.xml b/res/color/widgets_picker_scrim.xml
similarity index 74%
rename from res/values-v26/bools.xml
rename to res/color/widgets_picker_scrim.xml
index ad8c7a1..1cf97f6 100644
--- a/res/values-v26/bools.xml
+++ b/res/color/widgets_picker_scrim.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,6 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="#000000" android:alpha="0.32" />
+</selector>
diff --git a/res/drawable-hdpi/widget_resize_frame.9.png b/res/drawable-hdpi/widget_resize_frame.9.png
deleted file mode 100644
index a710932..0000000
--- a/res/drawable-hdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_shadow.9.png b/res/drawable-hdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 7cb5214..0000000
--- a/res/drawable-hdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_frame.9.png b/res/drawable-mdpi/widget_resize_frame.9.png
deleted file mode 100644
index 252482f..0000000
--- a/res/drawable-mdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_shadow.9.png b/res/drawable-mdpi/widget_resize_shadow.9.png
deleted file mode 100644
index a2010e2..0000000
--- a/res/drawable-mdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-v24/drag_handle_indicator_shadow.xml b/res/drawable-v24/drag_handle_indicator_shadow.xml
deleted file mode 100644
index 774bc38..0000000
--- a/res/drawable-v24/drag_handle_indicator_shadow.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-<com.android.launcher3.graphics.ShadowDrawable
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/drag_handle_indicator_no_shadow"
-    android:elevation="@dimen/vertical_drag_handle_elevation" />
diff --git a/res/drawable-v26/ic_launcher_home.xml b/res/drawable-v26/ic_launcher_home.xml
deleted file mode 100644
index 7038775..0000000
--- a/res/drawable-v26/ic_launcher_home.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@color/icon_background" />
-    <foreground>
-        <bitmap android:src="@mipmap/ic_launcher_home_foreground"/>
-    </foreground>
-</adaptive-icon>
diff --git a/res/drawable-v28/bg_celllayout.xml b/res/drawable-v28/bg_celllayout.xml
new file mode 100644
index 0000000..c68bdec
--- /dev/null
+++ b/res/drawable-v28/bg_celllayout.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_active="true" >
+        <shape android:shape="rectangle" >
+            <stroke
+                android:width="@dimen/spring_loaded_panel_border"
+                android:color="?android:attr/colorAccent" />
+            <solid android:color="@color/cell_layout_bg_color_active"/>
+            <corners android:radius="?android:attr/dialogCornerRadius" />
+
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle" >
+            <solid android:color="@color/cell_layout_bg_color_inactive"/>
+            <corners android:radius="?android:attr/dialogCornerRadius" />
+        </shape>
+    </item>
+</selector>
diff --git a/res/drawable/top_round_rect_primary.xml b/res/drawable-v28/widgets_bottom_sheet_background.xml
similarity index 74%
copy from res/drawable/top_round_rect_primary.xml
copy to res/drawable-v28/widgets_bottom_sheet_background.xml
index 1caaa02..7fb8681 100644
--- a/res/drawable/top_round_rect_primary.xml
+++ b/res/drawable-v28/widgets_bottom_sheet_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2018 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.
@@ -15,12 +15,12 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorPrimary" />
+    android:shape="rectangle">
+    <solid android:color="@color/surface" />
     <corners
-        android:topLeftRadius="@dimen/bg_round_rect_radius"
-        android:topRightRadius="@dimen/bg_round_rect_radius"
+        android:topLeftRadius="?android:attr/dialogCornerRadius"
+        android:topRightRadius="?android:attr/dialogCornerRadius"
         android:bottomLeftRadius="0dp"
         android:bottomRightRadius="0dp"
         />
-</shape>
+</shape>
\ No newline at end of file
diff --git a/res/drawable-xhdpi/widget_resize_frame.9.png b/res/drawable-xhdpi/widget_resize_frame.9.png
deleted file mode 100644
index 563c75d..0000000
--- a/res/drawable-xhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_shadow.9.png b/res/drawable-xhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 2b1ac05..0000000
--- a/res/drawable-xhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_frame.9.png b/res/drawable-xxhdpi/widget_resize_frame.9.png
deleted file mode 100644
index ea527f4..0000000
--- a/res/drawable-xxhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_shadow.9.png b/res/drawable-xxhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 5412168..0000000
--- a/res/drawable-xxhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_frame.9.png b/res/drawable-xxxhdpi/widget_resize_frame.9.png
deleted file mode 100644
index 4644e9a..0000000
--- a/res/drawable-xxxhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_shadow.9.png b/res/drawable-xxxhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 63cea84..0000000
--- a/res/drawable-xxxhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/add_item_dialog_background.xml b/res/drawable/add_item_dialog_background.xml
new file mode 100644
index 0000000..c3a8269
--- /dev/null
+++ b/res/drawable/add_item_dialog_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/all_apps_divider.xml b/res/drawable/all_apps_divider.xml
index 4bd274d..53d3522 100644
--- a/res/drawable/all_apps_divider.xml
+++ b/res/drawable/all_apps_divider.xml
@@ -15,6 +15,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?android:attr/colorControlHighlight" />
-    <size android:height="1dp" />
+    <solid android:color="@android:color/transparent" />
+    <size android:height="2dp" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/all_apps_tabs_background.xml b/res/drawable/all_apps_tabs_background.xml
new file mode 100644
index 0000000..aea2e7a
--- /dev/null
+++ b/res/drawable/all_apps_tabs_background.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    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>
\ No newline at end of file
diff --git a/res/drawable/arrow_toast_rounded_background.xml b/res/drawable/arrow_toast_rounded_background.xml
index 52cc6fc..1206ddd 100644
--- a/res/drawable/arrow_toast_rounded_background.xml
+++ b/res/drawable/arrow_toast_rounded_background.xml
@@ -14,6 +14,6 @@
     limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
-    <solid android:color="?android:attr/colorAccent" />
-    <corners android:radius="8dp" />
+    <solid android:color="@color/arrow_tip_view_bg" />
+    <corners android:radius="@dimen/dialogCornerRadius" />
 </shape>
diff --git a/res/drawable/bg_celllayout.xml b/res/drawable/bg_celllayout.xml
index b81b37f..4e7e82f 100644
--- a/res/drawable/bg_celllayout.xml
+++ b/res/drawable/bg_celllayout.xml
@@ -23,13 +23,16 @@
         <shape android:shape="rectangle" >
             <stroke
                 android:width="@dimen/spring_loaded_panel_border"
-                android:color="@color/spring_loaded_highlighted_panel_border_color" />
-            <solid android:color="@color/spring_loaded_panel_color" />
+                android:color="?android:attr/colorAccent" />
+            <solid android:color="@color/cell_layout_bg_color_active"/>
+            <corners android:radius="@dimen/bg_round_rect_radius" />
+
         </shape>
     </item>
     <item>
         <shape android:shape="rectangle" >
-            <solid android:color="@color/spring_loaded_panel_color" />
+            <solid android:color="@color/cell_layout_bg_color_inactive"/>
+            <corners android:radius="@dimen/bg_round_rect_radius" />
         </shape>
     </item>
 </selector>
diff --git a/res/drawable/top_round_rect_primary.xml b/res/drawable/bg_rounded_corner_bottom_sheet.xml
similarity index 65%
copy from res/drawable/top_round_rect_primary.xml
copy to res/drawable/bg_rounded_corner_bottom_sheet.xml
index 1caaa02..aa49bce 100644
--- a/res/drawable/top_round_rect_primary.xml
+++ b/res/drawable/bg_rounded_corner_bottom_sheet.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2018 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.
@@ -14,13 +13,11 @@
      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="?android:attr/colorPrimary" />
+    android:shape="rectangle" >
+    <solid android:color="@color/surface" />
     <corners
-        android:topLeftRadius="@dimen/bg_round_rect_radius"
-        android:topRightRadius="@dimen/bg_round_rect_radius"
-        android:bottomLeftRadius="0dp"
-        android:bottomRightRadius="0dp"
-        />
-</shape>
+        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
new file mode 100644
index 0000000..68681a6
--- /dev/null
+++ b/res/drawable/bg_widgets_picker_handle.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.
+-->
+<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/go/res/values/dimens.xml b/res/drawable/bg_widgets_searchbox.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to res/drawable/bg_widgets_searchbox.xml
index f1b1053..dc6d868 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/bg_widgets_searchbox.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="@color/surface" />
+    <corners android:radius="24dp" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/button_bottom_rounded_colored_ripple.xml b/res/drawable/button_bottom_rounded_colored_ripple.xml
new file mode 100644
index 0000000..95f5234
--- /dev/null
+++ b/res/drawable/button_bottom_rounded_colored_ripple.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">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <corners
+                    android:topLeftRadius="4dp"
+                    android:topRightRadius="4dp"
+                    android:bottomLeftRadius="12dp"
+                    android:bottomRightRadius="12dp"  />
+                <solid android:color="@color/button_bg"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/res/drawable/button_rounded_colored_ripple.xml b/res/drawable/button_rounded_colored_ripple.xml
new file mode 100644
index 0000000..f6d689f
--- /dev/null
+++ b/res/drawable/button_rounded_colored_ripple.xml
@@ -0,0 +1,28 @@
+<?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">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <corners android:radius="12dp"/>
+                <solid android:color="@color/button_bg"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/res/drawable/button_top_rounded_bordered_ripple.xml b/res/drawable/button_top_rounded_bordered_ripple.xml
new file mode 100644
index 0000000..f15a4a0
--- /dev/null
+++ b/res/drawable/button_top_rounded_bordered_ripple.xml
@@ -0,0 +1,35 @@
+<?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">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <corners
+                    android:topLeftRadius="12dp"
+                    android:topRightRadius="12dp"
+                    android:bottomLeftRadius="4dp"
+                    android:bottomRightRadius="4dp"  />
+                <solid android:color="@color/surface"/>
+                <stroke
+                    android:width="2dp"
+                    android:color="@color/button_bg"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/res/drawable/deep_shortcuts_text_placeholder.xml b/res/drawable/deep_shortcuts_text_placeholder.xml
index 99da50f..5820d6b 100644
--- a/res/drawable/deep_shortcuts_text_placeholder.xml
+++ b/res/drawable/deep_shortcuts_text_placeholder.xml
@@ -15,7 +15,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?attr/popupColorSecondary" />
+    <solid android:color="?attr/popupColorPrimary" />
     <corners android:radius="8dp" />
     <size android:height="16dp" />
 </shape>
diff --git a/res/drawable/drag_handle_indicator_no_shadow.xml b/res/drawable/drag_handle_indicator_no_shadow.xml
deleted file mode 100644
index 341e60c..0000000
--- a/res/drawable/drag_handle_indicator_no_shadow.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="@dimen/vertical_drag_handle_width"
-    android:height="@dimen/vertical_drag_handle_height"
-    android:viewportWidth="18.0"
-    android:viewportHeight="6.0"
-    android:tint="?attr/workspaceTextColor" >
-
-    <path
-        android:pathData="M17,6c-0.15,0-0.3-0.03-0.45-0.11L9,2.12L1.45,5.89c-0.5,0.25-1.09,
-        0.05-1.34-0.45S0.06,4.35,0.55,4.11l8-4c0.28-0.14,0.61-0.14,0.89,0l8,4c0.49,0.25,0.69,
-        0.85,0.45,1.34C17.72,5.8,17.37,6,17,6z"
-        android:fillColor="@android:color/white" />
-</vector>
diff --git a/go/res/values/dimens.xml b/res/drawable/drop_target_background.xml
similarity index 63%
copy from go/res/values/dimens.xml
copy to res/drawable/drop_target_background.xml
index f1b1053..7e07bf5 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/drop_target_background.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true"
+        android:drawable="@drawable/drop_target_frame_hover" />
+    <item android:state_selected="false"
+        android:drawable="@drawable/drop_target_frame" />
+</selector>
\ No newline at end of file
diff --git a/res/drawable/top_round_rect_primary.xml b/res/drawable/drop_target_frame.xml
similarity index 64%
copy from res/drawable/top_round_rect_primary.xml
copy to res/drawable/drop_target_frame.xml
index 1caaa02..666a96e 100644
--- a/res/drawable/top_round_rect_primary.xml
+++ b/res/drawable/drop_target_frame.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2018 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.
@@ -15,12 +15,8 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorPrimary" />
-    <corners
-        android:topLeftRadius="@dimen/bg_round_rect_radius"
-        android:topRightRadius="@dimen/bg_round_rect_radius"
-        android:bottomLeftRadius="0dp"
-        android:bottomRightRadius="0dp"
-        />
-</shape>
+    android:shape="rectangle">
+    <solid android:color="@android:color/transparent" />
+    <corners android:radius="28dp" />
+    <stroke android:width="2dp" android:color="?attr/workspaceAccentColor" />
+</shape>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/drawable/drop_target_frame_hover.xml
similarity index 69%
copy from go/res/values/dimens.xml
copy to res/drawable/drop_target_frame_hover.xml
index f1b1053..ddf3a4d 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/drop_target_frame_hover.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +14,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="?attr/workspaceAccentColor" />
+    <corners android:radius="28dp" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/full_rounded_colored_ripple.xml b/res/drawable/full_rounded_colored_ripple.xml
new file mode 100644
index 0000000..d89537c
--- /dev/null
+++ b/res/drawable/full_rounded_colored_ripple.xml
@@ -0,0 +1,28 @@
+<?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">
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item>
+            <shape android:shape="rectangle">
+                <solid android:color="@color/button_bg"/>
+                <corners android:radius="28dp"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/quickstep/res/layout/scrim_view.xml b/res/drawable/full_rounded_transparent_ripple.xml
similarity index 64%
copy from quickstep/res/layout/scrim_view.xml
copy to res/drawable/full_rounded_transparent_ripple.xml
index 2cc37f9..a1e7943 100644
--- a/quickstep/res/layout/scrim_view.xml
+++ b/res/drawable/full_rounded_transparent_ripple.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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,8 +13,14 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.views.ShelfScrimView
+
+<ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:id="@+id/scrim_view" />
\ No newline at end of file
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape android:shape="oval">
+            <size android:height="24dp" android:width="24dp"/>
+            <solid android:color="@android:color/white"/>
+        </shape>
+    </item>
+</ripple>
\ 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
new file mode 100644
index 0000000..75887c9
--- /dev/null
+++ b/res/drawable/gesture_tutorial_motion_overview_light_mode.xml
@@ -0,0 +1,1587 @@
+<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/gm_edit_24.xml b/res/drawable/gm_edit_24.xml
new file mode 100644
index 0000000..59a0dc2
--- /dev/null
+++ b/res/drawable/gm_edit_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M20.41,4.94l-1.35,-1.35c-0.78,-0.78 -2.05,-0.78 -2.83,0L3,16.82L3,21h4.18L20.41,7.77c0.79,-0.78 0.79,-2.05 0,-2.83zM6.41,19.06L5,19v-1.36l9.82,-9.82 1.41,1.41 -9.82,9.83z"/>
+</vector>
diff --git a/res/drawable/ic_allapps_search.xml b/res/drawable/ic_allapps_search.xml
index 2aeb947..dbed824 100644
--- a/res/drawable/ic_allapps_search.xml
+++ b/res/drawable/ic_allapps_search.xml
@@ -17,8 +17,9 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
+    android:viewportWidth="24.0"
+    android:autoMirrored="true">
     <path
-        android:fillColor="?android:attr/colorAccent"
+        android:fillColor="?android:attr/textColorTertiary"
         android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
 </vector>
diff --git a/res/drawable/ic_block_no_shadow.xml b/res/drawable/ic_block_no_shadow.xml
index edeb4c6..6ac61f4 100644
--- a/res/drawable/ic_block_no_shadow.xml
+++ b/res/drawable/ic_block_no_shadow.xml
@@ -14,8 +14,8 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
+    android:width="20dp"
+    android:height="20dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
     android:tint="?android:attr/textColorPrimary">
diff --git a/res/drawable-v24/ic_block_shadow.xml b/res/drawable/ic_block_shadow.xml
similarity index 100%
rename from res/drawable-v24/ic_block_shadow.xml
rename to res/drawable/ic_block_shadow.xml
diff --git a/res/drawable/ic_conversations_widget_category.xml b/res/drawable/ic_conversations_widget_category.xml
new file mode 100644
index 0000000..7b13b23
--- /dev/null
+++ b/res/drawable/ic_conversations_widget_category.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+  <path
+      android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
+      android:fillColor="#81C995"/>
+  <path
+      android:pathData="M27,34C23.134,34 20,30.866 20,27C20,23.134 23.134,20 27,20C30.866,20 34,23.134 34,27C34,28.4872 33.5362,29.8662 32.7453,31"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#3C4043"/>
+  <path
+      android:pathData="M35,33l-8,0l-0,2l8,0z"
+      android:fillColor="#3C4043"/>
+  <path
+      android:pathData="M21,21m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
+      android:fillColor="#81C995"/>
+  <path
+      android:pathData="M16,25h5v2h-5z"
+      android:fillColor="#81C995"/>
+  <path
+      android:pathData="M21,28C24.866,28 28,24.866 28,21C28,17.134 24.866,14 21,14C17.134,14 14,17.134 14,21C14,22.4872 14.4638,23.8662 15.2547,25"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#ffffff"/>
+  <path
+      android:pathData="M13,27h8v2h-8z"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/res/drawable/ic_corp_off.xml b/res/drawable/ic_corp_off.xml
index 62a9787..117258e 100644
--- a/res/drawable/ic_corp_off.xml
+++ b/res/drawable/ic_corp_off.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
+<?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.
@@ -16,10 +15,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0"
-    android:tint="?android:attr/textColorHint" >
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorHint">
     <path
-        android:pathData="M22 7.95c.05-1.11-.84-2-1.95-1.95H16V3.95c0-1.11-.84-2-1.95-1.95h-4C8.94 1.95 8 2.84 8 3.95v.32l14 14V7.95zM14 6h-4V4h4v2zm7.54 14.28l-7.56-7.56v.01l-1.7-1.7h.01L7.21 5.95 3.25 1.99 1.99 3.27 4.69 6h-.64c-1.11 0-1.99.86-1.99 1.97l-.01 11.02c0 1.11.89 2.01 2 2.01h15.64l2.05 2.02L23 21.75l-1.46-1.47z"
-        android:fillColor="@android:color/white"/>
+        android:fillColor="@android:color/white"
+        android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v1.17L10.83,8L20,8v9.17l1.98,1.98c0,-0.05 0.02,-0.1 0.02,-0.16L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2zM19,19L8,8 6,6 2.81,2.81 1.39,4.22 3.3,6.13C2.54,6.41 2.01,7.14 2.01,8L2,19c0,1.11 0.89,2 2,2h14.17l1.61,1.61 1.41,-1.41 -0.37,-0.37L19,19zM4,19L4,8h1.17l11,11L4,19z" />
 </vector>
\ No newline at end of file
diff --git a/res/drawable/ic_deepshortcut_placeholder.xml b/res/drawable/ic_deepshortcut_placeholder.xml
index 85a9694..16e7cf3 100644
--- a/res/drawable/ic_deepshortcut_placeholder.xml
+++ b/res/drawable/ic_deepshortcut_placeholder.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+     Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,10 +14,7 @@
      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="oval">
-    <solid android:color="?attr/popupColorSecondary" />
-    <size
-        android:height="32dp"
-        android:width="32dp" />
-</shape>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="?attr/popupColorPrimary"/>
+    <foreground android:drawable="?attr/popupColorPrimary"/>
+</adaptive-icon>
diff --git a/res/drawable/ic_drag_handle.xml b/res/drawable/ic_drag_handle.xml
index 0181ff1..9db75f4 100644
--- a/res/drawable/ic_drag_handle.xml
+++ b/res/drawable/ic_drag_handle.xml
@@ -19,7 +19,7 @@
         android:height="@dimen/deep_shortcut_drag_handle_size"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0"
-        android:tint="?android:attr/textColorHint" >
+        android:tint="?android:attr/textColorPrimary" >
 
     <path
         android:pathData="M20,9H4v2h16V9z M4,15h16v-2H4V15z"
diff --git a/res/drawable/ic_expand_less.xml b/res/drawable/ic_expand_less.xml
new file mode 100644
index 0000000..cc16083
--- /dev/null
+++ b/res/drawable/ic_expand_less.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorSecondary">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M18.59,16.41L20,15l-8,-8 -8,8 1.41,1.41L12,9.83"/>
+</vector>
diff --git a/res/drawable/ic_expand_more.xml b/res/drawable/ic_expand_more.xml
new file mode 100644
index 0000000..ecbce7f
--- /dev/null
+++ b/res/drawable/ic_expand_more.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorSecondary">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17"/>
+</vector>
diff --git a/res/drawable/ic_gm_close_24.xml b/res/drawable/ic_gm_close_24.xml
new file mode 100644
index 0000000..2c9c932
--- /dev/null
+++ b/res/drawable/ic_gm_close_24.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:fillColor="?android:attr/textColorTertiary"
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12 19,6.41z"/>
+</vector>
diff --git a/res/drawable/ic_launcher_home.xml b/res/drawable/ic_launcher_home.xml
index a6f2519..7038775 100644
--- a/res/drawable/ic_launcher_home.xml
+++ b/res/drawable/ic_launcher_home.xml
@@ -13,6 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<bitmap
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/ic_launcher_home" />
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/icon_background" />
+    <foreground>
+        <bitmap android:src="@mipmap/ic_launcher_home_foreground"/>
+    </foreground>
+</adaptive-icon>
diff --git a/res/drawable/ic_remove_no_shadow.xml b/res/drawable/ic_remove_no_shadow.xml
index 2c706db..10f1e43 100644
--- a/res/drawable/ic_remove_no_shadow.xml
+++ b/res/drawable/ic_remove_no_shadow.xml
@@ -14,8 +14,8 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
+    android:width="20dp"
+    android:height="20dp"
     android:viewportHeight="24.0"
     android:viewportWidth="24.0"
     android:tint="?android:attr/textColorPrimary">
diff --git a/res/drawable-v24/ic_remove_shadow.xml b/res/drawable/ic_remove_shadow.xml
similarity index 100%
rename from res/drawable-v24/ic_remove_shadow.xml
rename to res/drawable/ic_remove_shadow.xml
diff --git a/res/drawable-v24/ic_setup_shadow.xml b/res/drawable/ic_setup_shadow.xml
similarity index 100%
rename from res/drawable-v24/ic_setup_shadow.xml
rename to res/drawable/ic_setup_shadow.xml
diff --git a/quickstep/res/drawable/ic_split_screen.xml b/res/drawable/ic_split_screen.xml
similarity index 100%
rename from quickstep/res/drawable/ic_split_screen.xml
rename to res/drawable/ic_split_screen.xml
diff --git a/res/drawable/ic_uninstall_no_shadow.xml b/res/drawable/ic_uninstall_no_shadow.xml
index 6aff102..fbabdd2 100644
--- a/res/drawable/ic_uninstall_no_shadow.xml
+++ b/res/drawable/ic_uninstall_no_shadow.xml
@@ -14,8 +14,8 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
+        android:width="20dp"
+        android:height="20dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0"
         android:tint="?android:attr/textColorPrimary" >
diff --git a/res/drawable-v24/ic_uninstall_shadow.xml b/res/drawable/ic_uninstall_shadow.xml
similarity index 100%
rename from res/drawable-v24/ic_uninstall_shadow.xml
rename to res/drawable/ic_uninstall_shadow.xml
diff --git a/res/drawable/ic_widget_height_decrease.xml b/res/drawable/ic_widget_height_decrease.xml
new file mode 100644
index 0000000..df704ba
--- /dev/null
+++ b/res/drawable/ic_widget_height_decrease.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorPrimary">
+  <path
+      android:fillColor="#FFFFFF"
+      android:pathData="M8,19h3v3h2v-3h3l-4,-4 -4,4zM16,4h-3L13,1h-2v3L8,4l4,4 4,-4zM4,9v2h16L20,9L4,9zM4,12h16v2H4z"/>
+</vector>
diff --git a/res/drawable/ic_widget_height_increase.xml b/res/drawable/ic_widget_height_increase.xml
new file mode 100644
index 0000000..c263a4b
--- /dev/null
+++ b/res/drawable/ic_widget_height_increase.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorPrimary">
+  <path
+      android:fillColor="#FFFFFF"
+      android:pathData="M4,20h16v2L4,22zM4,2h16v2L4,4zM13,9h3l-4,-4 -4,4h3v6L8,15l4,4 4,-4h-3z"/>
+</vector>
diff --git a/res/drawable/ic_widget_width_decrease.xml b/res/drawable/ic_widget_width_decrease.xml
new file mode 100644
index 0000000..2a2fad7
--- /dev/null
+++ b/res/drawable/ic_widget_width_decrease.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<rotate
+  xmlns:android="http://schemas.android.com/apk/res/android"
+  android:drawable="@drawable/ic_widget_height_decrease"
+  android:pivotX="50%"
+  android:pivotY="50%"
+  android:fromDegrees="90"
+  android:toDegrees="90" />
diff --git a/res/drawable/ic_widget_width_increase.xml b/res/drawable/ic_widget_width_increase.xml
new file mode 100644
index 0000000..89b9f40
--- /dev/null
+++ b/res/drawable/ic_widget_width_increase.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<rotate
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ic_widget_height_increase"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:fromDegrees="90"
+    android:toDegrees="90" />
diff --git a/go/res/values/dimens.xml b/res/drawable/middle_item_primary.xml
similarity index 68%
copy from go/res/values/dimens.xml
copy to res/drawable/middle_item_primary.xml
index f1b1053..0c04ea1 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/middle_item_primary.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="?attr/popupColorPrimary"/>
+    <corners android:radius="@dimen/popup_smaller_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/notification_circle.xml b/res/drawable/notification_circle.xml
new file mode 100644
index 0000000..65fbaea
--- /dev/null
+++ b/res/drawable/notification_circle.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+
+    <solid android:color="?attr/popupNotificationDotColor"/>
+
+    <size
+        android:width="@dimen/notification_circle_icon_size"
+        android:height="@dimen/notification_circle_icon_size"/>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/padded_rounded_action_button.xml b/res/drawable/padded_rounded_action_button.xml
new file mode 100644
index 0000000..6432efd
--- /dev/null
+++ b/res/drawable/padded_rounded_action_button.xml
@@ -0,0 +1,31 @@
+<?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"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item
+        android:left="@dimen/padded_rounded_button_padding"
+        android:top="@dimen/padded_rounded_button_padding"
+        android:right="@dimen/padded_rounded_button_padding"
+        android:bottom="@dimen/padded_rounded_button_padding">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/rounded_button_radius" />
+            <stroke android:width="1dp"
+                android:color="?androidprv:attr/colorAccentPrimaryVariant" />
+        </shape>
+    </item>
+</layer-list>
+
diff --git a/go/res/values/dimens.xml b/res/drawable/personal_work_tabs_ripple.xml
similarity index 61%
copy from go/res/values/dimens.xml
copy to res/drawable/personal_work_tabs_ripple.xml
index f1b1053..2e57b80 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/personal_work_tabs_ripple.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<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
diff --git a/res/drawable/rounded_action_button.xml b/res/drawable/rounded_action_button.xml
new file mode 100644
index 0000000..f043893
--- /dev/null
+++ b/res/drawable/rounded_action_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.
+  -->
+
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/rounded_button_radius" />
+    <stroke android:width="1dp" android:color="@color/all_apps_tab_background_selected" />
+    <padding
+        android:left="@dimen/rounded_button_padding"
+        android:right="@dimen/rounded_button_padding" />
+</shape>
+
diff --git a/go/res/values/dimens.xml b/res/drawable/single_item_primary.xml
similarity index 68%
copy from go/res/values/dimens.xml
copy to res/drawable/single_item_primary.xml
index f1b1053..1c0889b 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/single_item_primary.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="?attr/popupColorPrimary"/>
+    <corners android:radius="@dimen/popup_single_item_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/widget_reconfigure_button_frame.xml b/res/drawable/widget_reconfigure_button_frame.xml
new file mode 100644
index 0000000..37d93ad
--- /dev/null
+++ b/res/drawable/widget_reconfigure_button_frame.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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:width="@dimen/widget_reconfigure_button_size"
+        android:height="@dimen/widget_reconfigure_button_size">
+        <shape
+            android:shape="rectangle">
+            <solid android:color="?android:attr/colorAccent" />
+            <corners android:radius="@dimen/widget_reconfigure_button_corner_radius" />
+        </shape>
+    </item>
+    <item
+        android:gravity="center"
+        android:padding="@dimen/widget_reconfigure_button_padding"
+        android:drawable="@drawable/gm_edit_24"
+        android:color="?android:attr/colorPrimary" />
+</layer-list>
diff --git a/res/drawable/top_round_rect_primary.xml b/res/drawable/widget_resize_frame.xml
similarity index 64%
copy from res/drawable/top_round_rect_primary.xml
copy to res/drawable/widget_resize_frame.xml
index 1caaa02..9426de4 100644
--- a/res/drawable/top_round_rect_primary.xml
+++ b/res/drawable/widget_resize_frame.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2018 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.
@@ -15,12 +15,8 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorPrimary" />
-    <corners
-        android:topLeftRadius="@dimen/bg_round_rect_radius"
-        android:topRightRadius="@dimen/bg_round_rect_radius"
-        android:bottomLeftRadius="0dp"
-        android:bottomRightRadius="0dp"
-        />
-</shape>
+    android:shape="rectangle">
+    <solid android:color="@android:color/transparent" />
+    <corners android:radius="@android:dimen/system_app_widget_background_radius" />
+    <stroke android:width="2dp" android:color="?attr/workspaceAccentColor" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/top_round_rect_primary.xml b/res/drawable/widgets_bottom_sheet_background.xml
similarity index 74%
rename from res/drawable/top_round_rect_primary.xml
rename to res/drawable/widgets_bottom_sheet_background.xml
index 1caaa02..b877546 100644
--- a/res/drawable/top_round_rect_primary.xml
+++ b/res/drawable/widgets_bottom_sheet_background.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2018 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.
@@ -15,12 +15,12 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorPrimary" />
+    android:shape="rectangle">
+    <solid android:color="@color/surface" />
     <corners
-        android:topLeftRadius="@dimen/bg_round_rect_radius"
-        android:topRightRadius="@dimen/bg_round_rect_radius"
+        android:topLeftRadius="@dimen/default_dialog_corner_radius"
+        android:topRightRadius="@dimen/default_dialog_corner_radius"
         android:bottomLeftRadius="0dp"
         android:bottomRightRadius="0dp"
         />
-</shape>
+</shape>
\ No newline at end of file
diff --git a/res/values-v26/bools.xml b/res/drawable/widgets_recommendation_background.xml
similarity index 67%
copy from res/values-v26/bools.xml
copy to res/drawable/widgets_recommendation_background.xml
index ad8c7a1..0550a34 100644
--- a/res/values-v26/bools.xml
+++ b/res/drawable/widgets_recommendation_background.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* Copyright 2017, The Android Open Source Project
+/*
+**
+** 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.
@@ -15,9 +17,8 @@
 ** limitations under the License.
 */
 -->
-
-<resources>
-    <bool name="notification_dots_enabled">true</bool>
-
-    <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
+<shape android:shape="rectangle"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/surface" />
+    <corners android:radius="@dimen/widget_list_top_bottom_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/drawable/widgets_tray_expand_button.xml
similarity index 64%
copy from go/res/values/dimens.xml
copy to res/drawable/widgets_tray_expand_button.xml
index f1b1053..8316e0f 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/widgets_tray_expand_button.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true"
+        android:drawable="@drawable/ic_expand_less" />
+    <item android:state_checked="false"
+        android:drawable="@drawable/ic_expand_more" />
+</selector>
diff --git a/res/drawable/work_apps_toggle_background.xml b/res/drawable/work_apps_toggle_background.xml
new file mode 100644
index 0000000..a47c8fe
--- /dev/null
+++ b/res/drawable/work_apps_toggle_background.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/work_fab_radius" />
+            <solid android:color="?android:attr/colorControlHighlight" />
+            <padding
+                android:left="@dimen/work_profile_footer_padding"
+                android:right="@dimen/work_profile_footer_padding" />
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/work_fab_radius" />
+            <solid android:color="@color/all_apps_tab_background_selected" />
+            <padding
+                android:left="@dimen/work_profile_footer_padding"
+                android:right="@dimen/work_profile_footer_padding" />
+        </shape>
+    </item>
+</selector>
diff --git a/res/drawable/work_card.xml b/res/drawable/work_card.xml
new file mode 100644
index 0000000..4a66cac
--- /dev/null
+++ b/res/drawable/work_card.xml
@@ -0,0 +1,24 @@
+<?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"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <solid android:color="?androidprv:attr/colorSurface" />
+    <corners android:radius="@dimen/work_edu_card_radius" />
+</shape>
+
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index 830255b..0e06690 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -16,74 +16,94 @@
 ** limitations under the License.
 */
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.dragndrop.AddItemDragLayer
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/add_item_drag_layer"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
-    <ScrollView
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:importantForAccessibility="no">
+
+    <com.android.launcher3.widget.AddItemWidgetsBottomSheet
+        android:id="@+id/add_item_bottom_sheet"
         android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:clipToPadding="false">
+        android:layout_height="wrap_content"
+        android:theme="?attr/widgetsTheme"
+        android:layout_gravity="bottom"
+        android:orientation="vertical">
 
         <LinearLayout
+            android:id="@+id/add_item_bottom_sheet_content"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical">
+            android:padding="24dp"
+            android:background="@drawable/add_item_dialog_background"
+            android:orientation="vertical" >
+
+            <TextView
+                style="@style/TextHeadline"
+                android:id="@+id/widget_appName"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center_horizontal"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="24sp"
+                android:ellipsize="end"
+                android:fadingEdge="horizontal"
+                android:singleLine="true"
+                android:maxLines="1" />
 
             <TextView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:paddingBottom="20dp"
-                android:paddingLeft="24dp"
-                android:paddingRight="24dp"
-                android:paddingTop="4dp"
-                android:text="@string/add_item_request_drag_hint" />
+                android:gravity="center_horizontal"
+                android:paddingTop="8dp"
+                android:text="@string/add_item_request_drag_hint"
+                android:textSize="14sp"
+                android:textColor="?android:attr/textColorSecondary"
+                android:alpha="0.7"/>
 
-            <FrameLayout
+            <include layout="@layout/widget_cell"
+                android:id="@+id/widget_cell"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:layout_marginVertical="16dp" />
+
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:background="?android:attr/colorPrimaryDark"
-                android:theme="?attr/widgetsTheme">
-
-                <com.android.launcher3.dragndrop.LivePreviewWidgetCell
-                    android:id="@+id/widget_cell"
+                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="wrap_content"
-                    android:layout_gravity="center_horizontal"
-                    android:layout_weight="1"
-                    android:background="?android:attr/colorPrimaryDark"
-                    android:focusable="true"
-                    android:gravity="center_horizontal"
-                    android:orientation="vertical" >
+                    android:layout_height="36dp"
+                    android:paddingHorizontal="16dp"
+                    android:textSize="14sp"
+                    android:textColor="@color/button_text"
+                    android:text="@android:string/cancel"
+                    android:onClick="onCancelClick"/>
 
-                    <include layout="@layout/widget_cell_content"  />
+                <Space
+                    android:layout_width="8dp"
+                    android:layout_height="wrap_content" />
 
-                </com.android.launcher3.dragndrop.LivePreviewWidgetCell>
-            </FrameLayout>
+                <Button
+                    style="@style/Button.FullRounded.Colored"
+                    android:layout_width="wrap_content"
+                    android:layout_height="36dp"
+                    android:paddingHorizontal="16dp"
+                    android:textSize="14sp"
+                    android:textColor="@color/button_text"
+                    android:text="@string/add_to_home_screen"
+                    android:onClick="onPlaceAutomaticallyClick"/>
+            </LinearLayout>
         </LinearLayout>
-    </ScrollView>
+    </com.android.launcher3.widget.AddItemWidgetsBottomSheet>
 
-    <LinearLayout
-        style="?android:attr/buttonBarStyle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="end"
-        android:paddingBottom="4dp"
-        android:paddingEnd="12dp"
-        android:paddingStart="12dp"
-        android:paddingTop="4dp" >
-        <Button
-            style="?android:attr/buttonBarButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:onClick="onCancelClick"
-            android:text="@android:string/cancel" />
-        <Button
-            style="?android:attr/buttonBarButtonStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:onClick="onPlaceAutomaticallyClick"
-            android:text="@string/place_automatically" />
-    </LinearLayout>
-</LinearLayout>
+</com.android.launcher3.dragndrop.AddItemDragLayer>
+
+
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 67ec664..9ac6ed0 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- 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.
@@ -16,8 +15,7 @@
 <!-- The top and bottom paddings are defined in this container, but since we want
      the list view to span the full width (for touch interception purposes), we
      will bake the left/right padding into that view's background itself. -->
-<com.android.launcher3.allapps.LauncherAllAppsContainerView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.allapps.LauncherAllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/apps_view"
     android:theme="?attr/allAppsTheme"
     android:layout_width="match_parent"
@@ -38,43 +36,15 @@
         android:layout_below="@id/search_container_all_apps"
         android:clipToPadding="false"
         android:paddingTop="@dimen/all_apps_header_top_padding"
+        android:paddingBottom="@dimen/all_apps_header_bottom_padding"
         android:orientation="vertical" >
 
         <include layout="@layout/floating_header_content" />
 
-        <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
-            android:id="@+id/tabs"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/all_apps_header_tab_height"
-            android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
-            android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
-            android:orientation="horizontal"
-            style="@style/TextHeadline">
-
-            <Button
-                android:id="@+id/tab_personal"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:background="?android:attr/selectableItemBackground"
-                android:text="@string/all_apps_personal_tab"
-                android:textColor="@color/all_apps_tab_text"
-                android:textSize="14sp" />
-
-            <Button
-                android:id="@+id/tab_work"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:background="?android:attr/selectableItemBackground"
-                android:text="@string/all_apps_work_tab"
-                android:textColor="@color/all_apps_tab_text"
-                android:textSize="14sp" />
-        </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
+        <include layout="@layout/all_apps_personal_work_tabs" />
     </com.android.launcher3.allapps.FloatingHeaderView>
 
     <include
-        android:id="@id/search_container_all_apps"
         layout="@layout/search_container_all_apps"/>
 
     <include layout="@layout/all_apps_fast_scroller" />
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index 79fb612..069954c 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- 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.
@@ -13,16 +12,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.BubbleTextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<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"
+    style="@style/BaseIcon.AllApps"
     android:id="@+id/icon"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
     launcher:iconDisplay="all_apps"
-    launcher:centerVertically="true"
-    android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
-    android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />
+    launcher:centerVertically="true" />
 
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
new file mode 100644
index 0000000..11143fb
--- /dev/null
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -0,0 +1,51 @@
+<?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:layout_gravity="center_horizontal"
+    android:orientation="horizontal"
+    style="@style/TextHeadline">
+
+    <Button
+        android:id="@+id/tab_personal"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_marginEnd="@dimen/all_apps_tabs_button_horizontal_padding"
+        android:layout_marginVertical="@dimen/all_apps_tabs_vertical_padding"
+        android:layout_weight="1"
+        android:background="@drawable/all_apps_tabs_background"
+        android:text="@string/all_apps_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_marginStart="@dimen/all_apps_tabs_button_horizontal_padding"
+        android:layout_marginVertical="@dimen/all_apps_tabs_vertical_padding"
+        android:layout_weight="1"
+        android:background="@drawable/all_apps_tabs_background"
+        android:text="@string/all_apps_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/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index 2accd2d..de4a69d 100644
--- a/res/layout/all_apps_tabs.xml
+++ b/res/layout/all_apps_tabs.xml
@@ -22,7 +22,7 @@
     android:layout_height="match_parent"
     android:layout_below="@id/search_container_all_apps"
     android:layout_gravity="center_horizontal|top"
-    android:layout_marginTop="@dimen/all_apps_header_tab_height"
+    android:layout_marginTop="@dimen/all_apps_header_pill_height"
     android:clipChildren="true"
     android:clipToPadding="false"
     android:descendantFocusability="afterDescendants"
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index 12561b6..ff07a91 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -18,50 +18,73 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/widget_resize_shadow"
-    android:foreground="@drawable/widget_resize_frame"
-    android:foregroundTint="?attr/workspaceTextColor"
     android:padding="0dp">
 
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
 
+        <!-- Frame -->
+        <ImageView
+            android:id="@+id/widget_resize_frame"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:layout_margin="@dimen/resize_frame_margin"
+            android:src="@drawable/widget_resize_frame" />
+
         <!-- Left -->
         <ImageView
+            android:id="@+id/widget_resize_left_handle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="left|center_vertical"
             android:layout_marginLeft="@dimen/widget_handle_margin"
             android:src="@drawable/ic_widget_resize_handle"
-            android:tint="?attr/workspaceTextColor" />
+            android:tint="?attr/workspaceAccentColor" />
 
         <!-- Top -->
         <ImageView
+            android:id="@+id/widget_resize_top_handle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="top|center_horizontal"
             android:layout_marginTop="@dimen/widget_handle_margin"
             android:src="@drawable/ic_widget_resize_handle"
-            android:tint="?attr/workspaceTextColor" />
+            android:tint="?attr/workspaceAccentColor" />
 
         <!-- Right -->
         <ImageView
+            android:id="@+id/widget_resize_right_handle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="right|center_vertical"
             android:layout_marginRight="@dimen/widget_handle_margin"
             android:src="@drawable/ic_widget_resize_handle"
-            android:tint="?attr/workspaceTextColor" />
+            android:tint="?attr/workspaceAccentColor" />
 
         <!-- Bottom -->
         <ImageView
+            android:id="@+id/widget_resize_bottom_handle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="bottom|center_horizontal"
             android:layout_marginBottom="@dimen/widget_handle_margin"
             android:src="@drawable/ic_widget_resize_handle"
-            android:tint="?attr/workspaceTextColor" />
+            android:tint="?attr/workspaceAccentColor" />
+
+        <ImageButton
+            android:id="@+id/widget_reconfigure_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="@dimen/widget_reconfigure_button_padding"
+            android:layout_gravity="bottom|end"
+            android:layout_marginBottom="@dimen/widget_reconfigure_button_margin"
+            android:layout_marginEnd="@dimen/widget_reconfigure_button_margin"
+            android:src="@drawable/widget_reconfigure_button_frame"
+            android:background="?android:attr/selectableItemBackground"
+            android:visibility="gone"
+            android:contentDescription="@string/widget_reconfigure_button_content_description" />
 
     </FrameLayout>
 </com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/res/layout/arrow_toast.xml b/res/layout/arrow_toast.xml
index 0ec9981..9a6f8c3 100644
--- a/res/layout/arrow_toast.xml
+++ b/res/layout/arrow_toast.xml
@@ -19,46 +19,23 @@
     android:layout_height="wrap_content"
     android:layout_width="wrap_content">
 
-    <LinearLayout
+    <TextView
+        android:id="@+id/text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:paddingStart="24dp"
-        android:paddingEnd="4dp"
-        android:background="@drawable/arrow_toast_rounded_background"
         android:layout_gravity="center_horizontal"
+        android:gravity="center"
+        android:padding="16dp"
+        android:background="@drawable/arrow_toast_rounded_background"
         android:elevation="2dp"
-        android:orientation="horizontal">
-
-        <TextView
-            android:id="@+id/text"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:paddingTop="5dp"
-            android:paddingBottom="5dp"
-            android:gravity="center"
-            android:layout_gravity="center_vertical"
-            android:textColor="@android:color/white"
-            android:textSize="16sp"/>
-        <ImageView
-            android:id="@+id/dismiss"
-            android:layout_width="40dp"
-            android:layout_height="40dp"
-            android:layout_gravity="center_vertical"
-            android:padding="10dp"
-            android:layout_marginStart="2dp"
-            android:layout_marginEnd="2dp"
-            android:alpha="0.7"
-            android:src="@drawable/ic_remove_no_shadow"
-            android:tint="@android:color/white"
-            android:background="?android:attr/selectableItemBackgroundBorderless"
-            android:contentDescription="@string/accessibility_close"/>
-    </LinearLayout>
+        android:outlineProvider="none"
+        android:textColor="@color/arrow_tip_view_content"
+        android:textSize="14sp"/>
 
     <View
         android:id="@+id/arrow"
         android:elevation="2dp"
-        android:layout_width="10dp"
-        android:layout_height="8dp"
-        android:layout_marginTop="-2dp"/>
+        android:outlineProvider="none"
+        android:layout_width="@dimen/arrow_toast_arrow_width"
+        android:layout_height="10dp"/>
 </merge>
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 840a8b7..b175d17 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -19,6 +19,8 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="@dimen/bg_popup_item_height"
+    android:elevation="@dimen/deep_shortcuts_elevation"
+    android:background="@drawable/middle_item_primary"
     android:theme="@style/PopupItem" >
 
     <com.android.launcher3.shortcuts.DeepShortcutTextView
@@ -31,6 +33,8 @@
         android:paddingEnd="@dimen/popup_padding_end"
         android:drawableEnd="@drawable/ic_drag_handle"
         android:drawablePadding="@dimen/deep_shortcut_drawable_padding"
+        android:singleLine="true"
+        android:ellipsize="end"
         android:textSize="14sp"
         android:textColor="?android:attr/textColorPrimary"
         launcher:layoutHorizontal="true"
@@ -45,12 +49,4 @@
         android:layout_gravity="start|center_vertical"
         android:background="@drawable/ic_deepshortcut_placeholder"/>
 
-    <View
-        android:id="@+id/divider"
-        android:layout_width="@dimen/deep_shortcuts_divider_width"
-        android:layout_height="@dimen/popup_item_divider_height"
-        android:layout_gravity="end|bottom"
-        android:visibility="gone"
-        android:background="?attr/popupColorTertiary" />
-
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/scrim_view.xml b/res/layout/floating_widget_view.xml
similarity index 84%
rename from res/layout/scrim_view.xml
rename to res/layout/floating_widget_view.xml
index a604d56..eea7a92 100644
--- a/res/layout/scrim_view.xml
+++ b/res/layout/floating_widget_view.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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,8 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.views.ScrimView
+<com.android.quickstep.views.FloatingWidgetView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:id="@+id/scrim_view" />
\ No newline at end of file
+    android:layoutDirection="ltr" />
diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml
index 32a5419..1cdee08 100644
--- a/res/layout/folder_application.xml
+++ b/res/layout/folder_application.xml
@@ -21,4 +21,5 @@
     android:textColor="?attr/folderTextColor"
     android:includeFontPadding="false"
     android:hapticFeedbackEnabled="false"
-    launcher:iconDisplay="folder" />
+    launcher:iconDisplay="folder"
+    launcher:centerVertically="true" />
diff --git a/res/layout/home_settings.xml b/res/layout/home_settings.xml
new file mode 100644
index 0000000..0f2461a
--- /dev/null
+++ b/res/layout/home_settings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <EditText
+        android:id="@+id/filter_box"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="@dimen/developer_options_filter_margins"
+        android:hint="@string/developer_options_filter_hint"
+        android:visibility="gone"
+        />
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@android:id/list_container"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/keyboard_drag_and_drop.xml b/res/layout/keyboard_drag_and_drop.xml
new file mode 100644
index 0000000..e9463c4
--- /dev/null
+++ b/res/layout/keyboard_drag_and_drop.xml
@@ -0,0 +1,34 @@
+<?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.keyboard.KeyboardDragAndDropView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusable="true"
+    android:orientation="vertical"
+    android:elevation="6dp">
+
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:background="?attr/folderFillColor"
+        android:padding="8dp"
+        android:textColor="?attr/folderTextColor"
+        />
+
+</com.android.launcher3.keyboard.KeyboardDragAndDropView>
\ No newline at end of file
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index a137908..039d8d3 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -28,6 +28,12 @@
         android:clipToPadding="false"
         android:importantForAccessibility="no">
 
+        <com.android.launcher3.views.AccessibilityActionsView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:contentDescription="@string/home_screen"
+            />
+
         <!-- The workspace contains 5 screens of cells -->
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.Workspace
@@ -43,11 +49,6 @@
             android:id="@+id/hotseat"
             layout="@layout/hotseat" />
 
-        <include
-            android:id="@+id/overview_panel"
-            layout="@layout/overview_panel" />
-
-
         <!-- Keep these behind the workspace so that they are not visible when
          we go into AllApps -->
         <com.android.launcher3.pageindicators.WorkspacePageIndicator
@@ -61,9 +62,11 @@
             android:id="@+id/drop_target_bar"
             layout="@layout/drop_target_bar" />
 
-        <include
+        <com.android.launcher3.views.ScrimView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
             android:id="@+id/scrim_view"
-            layout="@layout/scrim_view" />
+            android:background="@android:color/transparent" />
 
         <include
             android:id="@+id/apps_view"
@@ -71,6 +74,10 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
 
+        <include
+            android:id="@+id/overview_panel"
+            layout="@layout/overview_panel" />
+
     </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout/launcher_preview_layout.xml b/res/layout/launcher_preview_layout.xml
index 3fd02e3..cf2f2c7 100644
--- a/res/layout/launcher_preview_layout.xml
+++ b/res/layout/launcher_preview_layout.xml
@@ -13,11 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.InsettableFrameLayout
+<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:layout_height="match_parent"
+    android:focusable="false">
 
     <com.android.launcher3.CellLayout
         android:id="@+id/workspace"
@@ -28,23 +29,8 @@
         launcher:containerType="workspace"
         launcher:pageIndicator="@+id/page_indicator"/>
 
-    <com.android.launcher3.Hotseat
+    <include
         android:id="@+id/hotseat"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:importantForAccessibility="no"
-        android:theme="@style/HomeScreenElementTheme"
-        launcher:containerType="hotseat" />
+        layout="@layout/hotseat" />
 
-    <com.android.launcher3.InsettableFrameLayout
-        android:id="@+id/apps_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" >
-
-        <include
-            android:id="@id/search_container_all_apps"
-            layout="@layout/search_container_all_apps"/>
-
-    </com.android.launcher3.InsettableFrameLayout>
-
-</com.android.launcher3.InsettableFrameLayout>
\ No newline at end of file
+</view>
\ No newline at end of file
diff --git a/res/layout/longpress_options_menu.xml b/res/layout/longpress_options_menu.xml
index 20bb5b8..fbe28d8 100644
--- a/res/layout/longpress_options_menu.xml
+++ b/res/layout/longpress_options_menu.xml
@@ -15,12 +15,10 @@
 -->
 <com.android.launcher3.views.OptionsPopupView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/deep_shortcuts_container"
+    android:id="@+id/popup_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="?attr/popupColorPrimary"
     android:clipToPadding="false"
     android:clipChildren="false"
-    android:elevation="@dimen/deep_shortcuts_elevation"
     android:importantForAccessibility="yes"
     android:orientation="vertical" />
diff --git a/res/layout/notification_content.xml b/res/layout/notification_content.xml
index d01be01..84822a6 100644
--- a/res/layout/notification_content.xml
+++ b/res/layout/notification_content.xml
@@ -23,35 +23,36 @@
     <FrameLayout
         android:id="@+id/header"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_header_height"
-        android:paddingEnd="@dimen/notification_padding_end"
-        android:paddingStart="@dimen/notification_padding_start">
+        android:layout_height="wrap_content"
+        android:paddingEnd="@dimen/notification_padding"
+        android:paddingStart="@dimen/notification_padding">
         <TextView
             android:id="@+id/notification_text"
+            android:paddingTop="@dimen/notification_padding"
             android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_gravity="start"
-            android:gravity="center_vertical"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|start"
             android:text="@string/notifications_header"
             android:textColor="?android:attr/textColorPrimary"
-            android:textSize="@dimen/notification_header_text_size" />
+            android:textSize="@dimen/notification_header_text_size"
+            style="@style/TextHeadline"/>
         <TextView
             android:id="@+id/notification_count"
-            android:layout_width="@dimen/notification_icon_size"
-            android:layout_height="match_parent"
-            android:layout_gravity="end"
-            android:fontFamily="sans-serif-medium"
+            android:layout_width="@dimen/notification_circle_icon_size"
+            android:layout_height="@dimen/notification_circle_icon_size"
+            android:background="@drawable/notification_circle"
+            android:layout_gravity="bottom|end"
             android:gravity="center"
             android:textColor="?android:attr/textColorPrimary"
-            android:textSize="@dimen/notification_header_count_text_size" />
+            android:textSize="@dimen/notification_header_count_text_size"
+            style="@style/TextHeadline"/>
     </FrameLayout>
 
     <!-- Main view -->
     <com.android.launcher3.notification.NotificationMainView
         android:id="@+id/main_view"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_main_height"
-        android:background="@drawable/bg_notification_content"
+        android:layout_height="wrap_content"
         android:focusable="true" >
 
         <LinearLayout
@@ -61,28 +62,28 @@
             android:background="?attr/popupColorPrimary"
             android:gravity="center_vertical"
             android:orientation="vertical"
-            android:paddingBottom="14dp"
-            android:paddingEnd="@dimen/notification_main_text_padding_end"
-            android:paddingStart="@dimen/notification_padding_start">
+            android:paddingTop="@dimen/notification_padding"
+            android:paddingBottom="@dimen/notification_padding"
+            android:paddingEnd="@dimen/notification_padding"
+            android:paddingStart="@dimen/notification_main_text_padding_start">
             <TextView
                 android:id="@+id/title"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:ellipsize="end"
-                android:fontFamily="sans-serif"
                 android:lines="1"
                 android:textAlignment="viewStart"
                 android:textColor="?android:attr/textColorPrimary"
-                android:textSize="@dimen/notification_main_title_size" />
+                android:textSize="@dimen/notification_main_title_size"
+                style="@style/TextHeadline" />
 
             <TextView
                 android:id="@+id/text"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:ellipsize="end"
-                android:fontFamily="sans-serif"
                 android:lines="1"
-                android:textColor="?android:attr/textColorSecondary"
+                android:textColor="?android:attr/textColorPrimary"
                 android:textSize="@dimen/notification_main_text_size" />
         </LinearLayout>
 
@@ -90,45 +91,9 @@
             android:id="@+id/popup_item_icon"
             android:layout_width="@dimen/notification_icon_size"
             android:layout_height="@dimen/notification_icon_size"
-            android:layout_gravity="center_vertical|end"
-            android:layout_marginBottom="7dp"
-            android:layout_marginEnd="@dimen/notification_padding_end" />
+            android:layout_gravity="start"
+            android:layout_marginTop="@dimen/notification_padding"
+            android:layout_marginStart="@dimen/notification_icon_padding" />
 
     </com.android.launcher3.notification.NotificationMainView>
-
-    <!-- Divider -->
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/popup_item_divider_height"
-        android:layout_below="@id/main_view"
-        android:background="?attr/popupColorTertiary" />
-
-    <!-- Footer -->
-    <com.android.launcher3.notification.NotificationFooterLayout
-        android:id="@+id/footer"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_footer_height"
-        android:layout_gravity="center_vertical"
-        android:clipChildren="false">
-
-        <LinearLayout
-            android:id="@+id/icon_row"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:gravity="end|center_vertical"
-            android:orientation="horizontal"
-            android:padding="@dimen/notification_footer_icon_row_padding"/>
-
-        <View
-            android:id="@+id/overflow"
-            android:layout_width="@dimen/horizontal_ellipsis_size"
-            android:layout_height="@dimen/horizontal_ellipsis_size"
-            android:layout_gravity="start|center_vertical"
-            android:layout_marginStart="@dimen/horizontal_ellipsis_offset"
-            android:background="@drawable/horizontal_ellipsis" />
-
-    </com.android.launcher3.notification.NotificationFooterLayout>
 </merge>
\ No newline at end of file
diff --git a/res/layout/notification_gutter.xml b/res/layout/notification_gutter.xml
index 10e7f7d..9a3e55a 100644
--- a/res/layout/notification_gutter.xml
+++ b/res/layout/notification_gutter.xml
@@ -16,6 +16,5 @@
 <View
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="4dp"
-    android:layout_marginTop="4dp"
-    android:background="@drawable/bg_notification_content" />
\ No newline at end of file
+    android:layout_height="0dp"
+    android:layout_marginTop="@dimen/popup_margin" />
\ No newline at end of file
diff --git a/res/layout/overview_actions_container.xml b/res/layout/overview_actions_container.xml
index 5946bf6..f152c7c 100644
--- a/res/layout/overview_actions_container.xml
+++ b/res/layout/overview_actions_container.xml
@@ -14,6 +14,8 @@
      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. -->
 <Space
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="0dp"
diff --git a/res/layout/popup_container.xml b/res/layout/popup_container.xml
index c737407..18014bb 100644
--- a/res/layout/popup_container.xml
+++ b/res/layout/popup_container.xml
@@ -16,11 +16,27 @@
 
 <com.android.launcher3.popup.PopupContainerWithArrow
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/deep_shortcuts_container"
+    android:id="@+id/popup_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="?attr/popupColorPrimary"
     android:clipToPadding="false"
     android:clipChildren="false"
-    android:elevation="@dimen/deep_shortcuts_elevation"
-    android:orientation="vertical" />
\ No newline at end of file
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/deep_shortcuts_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:tag="@string/popup_container_iterate_children"
+        android:elevation="@dimen/deep_shortcuts_elevation"
+        android:orientation="vertical"/>
+
+    <LinearLayout
+        android:id="@+id/notification_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:background="?attr/popupColorPrimary"
+        android:elevation="@dimen/deep_shortcuts_elevation"
+        android:orientation="vertical"/>
+</com.android.launcher3.popup.PopupContainerWithArrow>
\ No newline at end of file
diff --git a/res/layout/scrim_view.xml b/res/layout/search_container_hotseat.xml
similarity index 80%
copy from res/layout/scrim_view.xml
copy to res/layout/search_container_hotseat.xml
index a604d56..8f12ca0 100644
--- a/res/layout/scrim_view.xml
+++ b/res/layout/search_container_hotseat.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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,8 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.views.ScrimView
+<View
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:id="@+id/scrim_view" />
\ No newline at end of file
+    android:layout_height="0dp" />
\ No newline at end of file
diff --git a/res/layout/secondary_launcher.xml b/res/layout/secondary_launcher.xml
index fdf4446..b15a320 100644
--- a/res/layout/secondary_launcher.xml
+++ b/res/layout/secondary_launcher.xml
@@ -67,12 +67,10 @@
             android:paddingTop="@dimen/all_apps_header_top_padding"
             android:orientation="vertical" >
 
-            <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
+            <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
                 android:id="@+id/tabs"
                 android:layout_width="match_parent"
-                android:layout_height="@dimen/all_apps_header_tab_height"
-                android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
-                android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
+                android:layout_height="@dimen/all_apps_header_pill_height"
                 android:orientation="horizontal"
                 style="@style/TextHeadline">
 
@@ -97,7 +95,7 @@
                     android:textAllCaps="true"
                     android:textColor="@color/all_apps_tab_text"
                     android:textSize="14sp" />
-            </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
+            </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
         </com.android.launcher3.allapps.FloatingHeaderView>
 
         <com.android.launcher3.allapps.search.AppsSearchContainerLayout
diff --git a/res/layout/settings_activity.xml b/res/layout/settings_activity.xml
new file mode 100644
index 0000000..5edd2df
--- /dev/null
+++ b/res/layout/settings_activity.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content_parent"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:fitsSystemWindows="true">
+
+    <Toolbar
+        android:id="@+id/action_bar"
+        style="?android:attr/actionBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="?android:attr/actionBarTheme" />
+
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/switch_preference_with_settings.xml b/res/layout/switch_preference_with_settings.xml
index d319561..cd51833 100644
--- a/res/layout/switch_preference_with_settings.xml
+++ b/res/layout/switch_preference_with_settings.xml
@@ -26,7 +26,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:src="@drawable/ic_setting"
-        android:tint="@android:color/black"
+        android:forceDarkAllowed="true"
         android:padding="12dp"
         android:background="?android:attr/selectableItemBackgroundBorderless" />
 
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index 881df1b..de091c5 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -19,6 +19,8 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="@dimen/bg_popup_item_height"
+    android:elevation="@dimen/deep_shortcuts_elevation"
+    android:background="@drawable/middle_item_primary"
     android:theme="@style/PopupItem" >
 
     <com.android.launcher3.BubbleTextView
@@ -30,7 +32,9 @@
         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"
@@ -42,14 +46,6 @@
         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/textColorTertiary"/>
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="@dimen/deep_shortcuts_divider_width"
-        android:layout_height="@dimen/popup_item_divider_height"
-        android:layout_gravity="end|bottom"
-        android:visibility="gone"
-        android:background="?attr/popupColorTertiary" />
+        android:backgroundTint="?android:attr/textColorPrimary"/>
 
 </com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/system_shortcut_icons.xml b/res/layout/system_shortcut_icons.xml
index a340f4f..775a45f 100644
--- a/res/layout/system_shortcut_icons.xml
+++ b/res/layout/system_shortcut_icons.xml
@@ -21,7 +21,8 @@
     android:layout_height="@dimen/system_shortcut_header_height"
     android:orientation="horizontal"
     android:gravity="end|center_vertical"
-    android:background="?attr/popupColorSecondary"
+    android:background="@drawable/single_item_primary"
+    android:elevation="@dimen/deep_shortcuts_elevation"
     android:clipToPadding="true">
 
     <Space android:layout_width="0dp"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 923352e..11eea60 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -18,8 +18,6 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="@drawable/round_rect_folder"
-    android:elevation="5dp"
     android:orientation="vertical" >
 
     <com.android.launcher3.folder.FolderPagedView
@@ -27,15 +25,12 @@
         android:clipToPadding="false"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingLeft="8dp"
-        android:paddingRight="8dp"
-        android:paddingTop="16dp"
         launcher:pageIndicator="@+id/folder_page_indicator" />
 
     <LinearLayout
         android:id="@+id/folder_footer"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="48dp"
         android:clipChildren="false"
         android:orientation="horizontal"
         android:paddingLeft="12dp"
@@ -49,17 +44,13 @@
             style="@style/TextHeadline"
             android:layout_weight="1"
             android:background="@android:color/transparent"
-            android:textStyle="bold"
             android:gravity="center_horizontal"
             android:hint="@string/folder_hint_text"
             android:imeOptions="flagNoExtractUi"
-            android:paddingBottom="@dimen/folder_label_padding_bottom"
-            android:paddingTop="@dimen/folder_label_padding_top"
             android:singleLine="true"
             android:textColor="?attr/folderTextColor"
             android:textColorHighlight="?android:attr/colorControlHighlight"
-            android:textColorHint="?attr/folderHintColor"
-            android:textSize="@dimen/folder_label_text_size" />
+            android:textColorHint="?attr/folderHintColor"/>
 
         <com.android.launcher3.pageindicators.PageIndicatorDots
             android:id="@+id/folder_page_indicator"
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index 148a99b..55dd1de 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -15,12 +15,13 @@
 -->
 <com.android.launcher3.widget.WidgetCell
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
+    android:layout_width="0dp"
     android:layout_height="wrap_content"
+    android:paddingHorizontal="@dimen/widget_cell_horizontal_padding"
+    android:paddingVertical="@dimen/widget_cell_vertical_padding"
     android:layout_weight="1"
     android:orientation="vertical"
     android:focusable="true"
-    android:background="?android:attr/colorPrimaryDark"
     android:gravity="center_horizontal">
 
     <include layout="@layout/widget_cell_content"  />
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index 64f2362..b27b505 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -17,47 +17,57 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content">
 
-    <LinearLayout
+    <com.android.launcher3.widget.WidgetCellPreview
+        android:id="@+id/widget_preview_container"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:importantForAccessibility="no"
+        android:layout_marginVertical="8dp">
+        <!-- The image of the widget. This view does not support padding. Any placement adjustment
+             should be done using margins. Width & height are set at runtime after scaling the
+             preview image. -->
+        <com.android.launcher3.widget.WidgetImageView
+            android:id="@+id/widget_preview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:importantForAccessibility="no"
+            android:layout_gravity="fill"/>
+    </com.android.launcher3.widget.WidgetCellPreview>
+
+    <!-- The name of the widget. -->
+    <TextView
+        android:id="@+id/widget_name"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingTop="@dimen/widget_preview_label_vertical_padding"
-        android:paddingBottom="@dimen/widget_preview_label_vertical_padding"
-        android:paddingLeft="@dimen/widget_preview_label_horizontal_padding"
-        android:paddingRight="@dimen/widget_preview_label_horizontal_padding"
-        android:orientation="horizontal">
+        android:ellipsize="end"
+        android:fadingEdge="horizontal"
+        android:gravity="center_horizontal"
+        android:singleLine="true"
+        android:maxLines="1"
+        android:textColor="?android:attr/textColorPrimary"
+        android:textSize="@dimen/widget_cell_font_size" />
 
-        <!-- The name of the widget. -->
-        <TextView
-            android:id="@+id/widget_name"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:ellipsize="end"
-            android:fadingEdge="horizontal"
-            android:gravity="start"
-            android:singleLine="true"
-            android:maxLines="1"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textSize="14sp" />
-
-        <!-- The original dimensions of the widget (can't be the same text as above due to different
-             style. -->
-        <TextView
-            android:id="@+id/widget_dims"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="5dp"
-            android:layout_marginLeft="5dp"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textSize="14sp"
-            android:alpha="0.8" />
-    </LinearLayout>
-
-    <!-- The image of the widget. This view does not support padding. Any placement adjustment
-         should be done using margins. -->
-    <com.android.launcher3.widget.WidgetImageView
-        android:id="@+id/widget_preview"
+    <!-- The original dimensions of the widget -->
+    <TextView
+        android:id="@+id/widget_dims"
         android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:textColor="?android:attr/textColorSecondary"
+        android:textSize="@dimen/widget_cell_font_size"
+        android:alpha="0.7" />
+
+    <TextView
+        android:id="@+id/widget_description"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:textSize="@dimen/widget_cell_font_size"
+        android:textColor="?android:attr/textColorSecondary"
+        android:maxLines="2"
+        android:ellipsize="end"
+        android:fadingEdge="horizontal"
+        android:alpha="0.7" />
+
 </merge>
\ No newline at end of file
diff --git a/quickstep/res/layout/scrim_view.xml b/res/layout/widget_shortcut_container.xml
similarity index 62%
copy from quickstep/res/layout/scrim_view.xml
copy to res/layout/widget_shortcut_container.xml
index 2cc37f9..a4d8eb4 100644
--- a/quickstep/res/layout/scrim_view.xml
+++ b/res/layout/widget_shortcut_container.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,8 +13,15 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.views.ShelfScrimView
+
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/system_shortcut_full"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:id="@+id/scrim_view" />
\ No newline at end of file
+    android:layout_height="@dimen/system_shortcut_header_height"
+    android:orientation="horizontal"
+    android:gravity="end|center_vertical"
+    android:elevation="@dimen/deep_shortcuts_elevation"
+    android:tag="@string/popup_container_iterate_children"
+    android:clipToPadding="true">
+</LinearLayout>
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index 3fdfc96..bbb08fa 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -19,36 +19,9 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingTop="28dp"
-    android:background="@drawable/top_round_rect_primary"
-    android:elevation="@dimen/deep_shortcuts_elevation"
     android:layout_gravity="bottom"
     android:theme="?attr/widgetsTheme">
 
-    <TextView
-        style="@style/TextHeadline"
-        android:id="@+id/title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_horizontal"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="24sp"/>
-
-    <TextView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_horizontal"
-        android:paddingTop="4dp"
-        android:fontFamily="sans-serif"
-        android:textColor="?android:attr/textColorTertiary"
-        android:textSize="14sp"
-        android:text="@string/long_press_widget_to_add"/>
-
-    <include layout="@layout/widgets_scroll_container"
-         android:id="@+id/widgets"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
-         android:layout_marginTop="45dp"
-         android:layout_marginBottom="40dp"/>
+    <include layout="@layout/widgets_bottom_sheet_content" />
 
 </com.android.launcher3.widget.WidgetsBottomSheet>
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
new file mode 100644
index 0000000..3b3ff8b
--- /dev/null
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -0,0 +1,53 @@
+<?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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout
+        android:id="@+id/widgets_bottom_sheet"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/widgets_bottom_sheet_background"
+        android:paddingTop="16dp"
+        android:orientation="vertical">
+        <View
+            android:id="@+id/collapse_handle"
+            android:layout_width="48dp"
+            android:layout_height="2dp"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginBottom="16dp"
+            android:visibility="gone"
+            android:background="?android:attr/textColorSecondary"/>
+        <TextView
+            style="@style/TextHeadline"
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="24sp"/>
+
+        <ScrollView
+            android:id="@+id/widgets_table_scroll_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:fadeScrollbars="false"
+            android:layout_marginVertical="16dp">
+            <include layout="@layout/widgets_table_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal" />
+        </ScrollView>
+    </LinearLayout>
+</merge>
diff --git a/res/layout/widgets_edu.xml b/res/layout/widgets_edu.xml
new file mode 100644
index 0000000..280c095
--- /dev/null
+++ b/res/layout/widgets_edu.xml
@@ -0,0 +1,66 @@
+<?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.views.WidgetsEduView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="bottom"
+    android:gravity="bottom"
+    android:orientation="vertical">
+
+    <LinearLayout
+        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:orientation="vertical"
+        android:paddingHorizontal="@dimen/bottom_sheet_edu_padding"
+        android:paddingTop="@dimen/bottom_sheet_edu_padding">
+
+        <TextView
+            style="@style/TextHeadline"
+            android:id="@+id/edu_header"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:text="@string/widget_education_header"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="24sp"
+            android:layout_marginBottom="16dp"/>
+
+        <TextView
+            android:id="@+id/edu_content"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:text="@string/widget_education_content"
+            android:textSize="14sp"
+            android:textColor="?android:attr/textColorSecondary"
+            android:layout_marginBottom="24dp"/>
+
+        <Button
+            android:id="@+id/edu_close_button"
+            style="@style/Button.Rounded.Colored"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            android:text="@string/widget_education_close_button"
+            android:textSize="16sp"
+            android:textColor="@color/button_text"
+            android:layout_marginBottom="8dp"/>
+    </LinearLayout>
+</com.android.launcher3.views.WidgetsEduView>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index f507a88..1b4f3b9 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -13,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.widget.WidgetsFullSheet
+<com.android.launcher3.widget.picker.WidgetsFullSheet
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
@@ -24,14 +25,17 @@
         android:id="@+id/container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="?android:attr/colorPrimary"
-        android:elevation="4dp">
+        android:background="?android:attr/colorBackground">
 
-        <com.android.launcher3.widget.WidgetsRecyclerView
-            android:id="@+id/widgets_list_view"
+        <TextView
+            android:id="@+id/no_widgets_text"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:clipToPadding="false" />
+            android:gravity="center"
+            android:visibility="gone"
+            android:fontFamily="sans-serif-medium"
+            android:textSize="20sp"
+            tools:text="No widgets available" />
 
         <!-- Fast scroller popup -->
         <TextView
@@ -48,5 +52,13 @@
             android:layout_alignParentEnd="true"
             android:layout_alignParentTop="true"
             android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+
+        <com.android.launcher3.widget.picker.WidgetsRecyclerView
+            android:id="@+id/search_widgets_list_view"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone"
+            android:clipToPadding="false" />
+
     </com.android.launcher3.views.TopRoundedCornerView>
-</com.android.launcher3.widget.WidgetsFullSheet>
\ No newline at end of file
+</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
new file mode 100644
index 0000000..f0ddc2b
--- /dev/null
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto">
+
+    <com.android.launcher3.workprofile.PersonalWorkPagedView
+        android:id="@+id/widgets_view_pager"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipToPadding="false"
+        android:paddingTop="@dimen/widget_picker_view_pager_top_padding"
+        android:descendantFocusability="afterDescendants"
+        launcher:pageIndicator="@+id/tabs">
+
+        <com.android.launcher3.widget.picker.WidgetsRecyclerView
+            android:id="@+id/primary_widgets_list_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            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:clipToPadding="false" />
+
+    </com.android.launcher3.workprofile.PersonalWorkPagedView>
+
+    <include layout="@layout/widgets_personal_work_tabs"/>
+</merge>
\ No newline at end of file
diff --git a/res/layout/scrim_view.xml b/res/layout/widgets_full_sheet_recyclerview.xml
similarity index 79%
copy from res/layout/scrim_view.xml
copy to res/layout/widgets_full_sheet_recyclerview.xml
index a604d56..fbe559c 100644
--- a/res/layout/scrim_view.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.views.ScrimView
+<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:id="@+id/scrim_view" />
\ No newline at end of file
+    android:clipToPadding="false" />
\ 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
new file mode 100644
index 0000000..4a3e20d
--- /dev/null
+++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml
@@ -0,0 +1,61 @@
+<?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
new file mode 100644
index 0000000..7f84050
--- /dev/null
+++ b/res/layout/widgets_list_row_header.xml
@@ -0,0 +1,82 @@
+<?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.WidgetsListHeader xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    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">
+
+    <ImageView
+        android:id="@+id/app_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="16dp"
+        android:importantForAccessibility="no"
+        tools:src="@drawable/ic_corp"/>
+
+    <LinearLayout
+        android:id="@+id/app_container"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:focusable="true"
+        android:descendantFocusability="afterDescendants">
+
+        <TextView
+            android:id="@+id/app_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start|center_vertical"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="16sp"
+            tools:text="App name" />
+
+        <TextView
+            android:id="@+id/app_subtitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="?android:attr/textColorSecondary"
+            android:alpha="0.7"
+            tools:text="m widgets, n shortcuts" />
+
+    </LinearLayout>
+
+    <!-- This checkbox is not clickable. The outermost LinearLayout is responsible to handle all
+         click event and update the checkbox state. -->
+    <CheckBox
+        android:id="@+id/toggle"
+        android:layout_marginHorizontal="16dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_alignParentEnd="true"
+        android:enabled="false"
+        android:clickable="false"
+        android:importantForAccessibility="no"
+        android:button="@drawable/widgets_tray_expand_button"/>
+
+</com.android.launcher3.widget.picker.WidgetsListHeader>
\ No newline at end of file
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index eec57a5..5942ba6 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -45,5 +45,5 @@
         launcher:iconSizeOverride="@dimen/widget_section_icon_size"
         launcher:layoutHorizontal="true" />
 
-    <include layout="@layout/widgets_scroll_container" />
+    <include layout="@layout/widgets_table_container" />
 </LinearLayout>
diff --git a/res/layout/widgets_personal_work_tabs.xml b/res/layout/widgets_personal_work_tabs.xml
new file mode 100644
index 0000000..532c422
--- /dev/null
+++ b/res/layout/widgets_personal_work_tabs.xml
@@ -0,0 +1,53 @@
+<?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_scroll_container.xml b/res/layout/widgets_scroll_container.xml
deleted file mode 100644
index fc509d1..0000000
--- a/res/layout/widgets_scroll_container.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<HorizontalScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/widgets_scroll_container"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="?android:attr/colorPrimaryDark"
-    android:scrollbars="none">
-    <LinearLayout
-        android:id="@+id/widgets_cell_list"
-        style="@style/TextTitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingStart="0dp"
-        android:paddingEnd="0dp"
-        android:orientation="horizontal"
-        android:showDividers="none"/>
-</HorizontalScrollView>
\ No newline at end of file
diff --git a/res/layout/widgets_search_bar.xml b/res/layout/widgets_search_bar.xml
new file mode 100644
index 0000000..cb27f4f
--- /dev/null
+++ b/res/layout/widgets_search_bar.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.launcher3.widget.picker.search.LauncherWidgetsSearchBar
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widgets_search_bar"
+    android:layout_width="match_parent"
+    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
+        android:id="@+id/widgets_search_bar_edit_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingVertical="12dp"
+        android:paddingStart="12dp"
+        android:paddingEnd="0dp"
+        android:drawablePadding="8dp"
+        android:drawableStart="@drawable/ic_allapps_search"
+        android:background="@null"
+        android:hint="@string/widgets_full_sheet_search_bar_hint"
+        android:maxLines="1"
+        android:layout_weight="1"
+        android:inputType="text"
+        android:imeOptions="actionSearch"
+        android:textColor="?android:attr/textColorPrimary"
+        android:textColorHint="?android:attr/textColorSecondary"/>
+
+    <ImageButton
+        android:id="@+id/widgets_search_cancel_button"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:padding="12dp"
+        android:src="@drawable/ic_gm_close_24"
+        android:background="@drawable/full_rounded_transparent_ripple"
+        android:layout_gravity="center"
+        android:contentDescription="@string/widgets_full_sheet_cancel_button_description"
+        android:visibility="gone"/>
+</com.android.launcher3.widget.picker.search.LauncherWidgetsSearchBar>
\ No newline at end of file
diff --git a/quickstep/res/layout/scrim_view.xml b/res/layout/widgets_table_container.xml
similarity index 72%
rename from quickstep/res/layout/scrim_view.xml
rename to res/layout/widgets_table_container.xml
index 2cc37f9..ab470d8 100644
--- a/quickstep/res/layout/scrim_view.xml
+++ b/res/layout/widgets_table_container.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.views.ShelfScrimView
+<com.android.launcher3.widget.picker.WidgetsListTableView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widgets_table"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:id="@+id/scrim_view" />
\ No newline at end of file
+    android:layout_height="wrap_content"
+    android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin" />
diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml
new file mode 100644
index 0000000..1517087
--- /dev/null
+++ b/res/layout/work_apps_edu.xml
@@ -0,0 +1,55 @@
+<?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.
+-->
+
+<com.android.launcher3.allapps.WorkEduCard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="8dp"
+    android:gravity="center">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingHorizontal="@dimen/work_card_padding_horizontal"
+        android:paddingVertical="@dimen/work_card_padding_horizontal"
+        android:background="@drawable/work_card"
+        android:layout_gravity="center_horizontal"
+        android:gravity="center"
+        android:id="@+id/wrapper">
+
+        <TextView
+            style="@style/PrimaryHeadline"
+            android:textColor="?android:attr/textColorPrimary"
+            android:id="@+id/work_apps_paused_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/work_card_padding_horizontal"
+            android:text="@string/work_profile_edu_work_apps"
+            android:textAlignment="center"
+            android:textSize="20sp" />
+
+        <Button
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/work_card_button_height"
+            android:id="@+id/action_btn"
+            android:textColor="?attr/workProfileOverlayTextColor"
+            android:text="@string/work_profile_edu_accept"
+            android:textAlignment="center"
+            android:background="@drawable/rounded_action_button"
+
+            android:textSize="14sp" />
+    </LinearLayout>
+</com.android.launcher3.allapps.WorkEduCard>
\ No newline at end of file
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 7f1107f..ec34b47 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -12,13 +12,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.allapps.WorkPausedCard xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="?attr/allAppsScrimColor"
-    android:padding="48dp"
+    android:layout_height="wrap_content"
+    android:padding="@dimen/work_edu_card_margin"
     android:orientation="vertical"
-    android:gravity="center">
+    android:gravity="center_horizontal">
 
     <TextView
         style="@style/PrimaryHeadline"
@@ -26,18 +25,31 @@
         android:id="@+id/work_apps_paused_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="8dp"
-        android:layout_marginBottom="8dp"
+        android:layout_marginTop="40dp"
         android:text="@string/work_apps_paused_title"
         android:textAlignment="center"
-        android:textSize="20sp" />
+        android:textSize="22sp" />
 
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:id="@+id/work_apps_paused_content"
-        android:textColor="?attr/workProfileOverlayTextColor"
+        android:textColor="?android:attr/textColorSecondary"
         android:text="@string/work_apps_paused_body"
         android:textAlignment="center"
-        android:textSize="16sp" />
-</LinearLayout>
\ No newline at end of file
+        android:layout_marginTop="16dp"
+        android:layout_marginBottom="24dp"
+        android:textSize="14sp" />
+
+    <Button
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/rounded_button_height"
+        android:id="@+id/enable_work_apps"
+        android:textColor="?attr/workProfileOverlayTextColor"
+        android:text="@string/work_apps_enable_btn_text"
+        android:textAlignment="center"
+        android:background="@drawable/rounded_action_button"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:textSize="14sp" />
+</com.android.launcher3.allapps.WorkPausedCard>
\ No newline at end of file
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
new file mode 100644
index 0000000..04faa15
--- /dev/null
+++ b/res/layout/work_mode_fab.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.allapps.WorkModeSwitch xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/TextHeadline"
+    android:id="@+id/work_mode_toggle"
+    android:layout_alignParentBottom="true"
+    android:layout_alignParentEnd="true"
+    android:layout_height="@dimen/work_fab_height"
+    android:layout_width="wrap_content"
+    android:gravity="center"
+    android:includeFontPadding="false"
+    android:drawableTint="@color/all_apps_tab_text"
+    android:textColor="@color/all_apps_tab_text"
+    android:textSize="14sp"
+    android:background="@drawable/work_apps_toggle_background"
+    android:drawablePadding="8dp"
+    android:drawableStart="@drawable/ic_corp_off"
+    android:layout_marginBottom="@dimen/work_fab_margin"
+    android:layout_marginEnd="@dimen/work_fab_margin"
+    android:text="@string/work_apps_pause_btn_text" />
\ No newline at end of file
diff --git a/res/layout/work_mode_switch.xml b/res/layout/work_mode_switch.xml
deleted file mode 100644
index 31953c7..0000000
--- a/res/layout/work_mode_switch.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.allapps.WorkModeSwitch
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    style="@style/PrimaryHeadline"
-    android:id="@+id/work_mode_toggle"
-    android:drawableStart="@drawable/ic_corp"
-    android:drawablePadding="16dp"
-    android:drawableTint="?attr/workProfileOverlayTextColor"
-    android:textColor="?attr/workProfileOverlayTextColor"
-    android:layout_alignParentBottom="true"
-    android:ellipsize="end"
-    android:elevation="10dp"
-    android:gravity="start"
-    android:lines="1"
-    android:showText="false"
-    android:textSize="@dimen/work_profile_footer_text_size"
-    android:background="?attr/allAppsScrimColor"
-    android:text="@string/work_profile_toggle_label"
-    android:paddingBottom="@dimen/work_profile_footer_padding"
-    android:paddingLeft="@dimen/work_profile_footer_padding"
-    android:paddingRight="@dimen/work_profile_footer_padding"
-    android:paddingTop="@dimen/work_profile_footer_padding"
-/>
diff --git a/res/layout/work_profile_edu.xml b/res/layout/work_profile_edu.xml
deleted file mode 100644
index c3c7010..0000000
--- a/res/layout/work_profile_edu.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?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.
--->
-<com.android.launcher3.views.WorkEduView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="bottom"
-    android:gravity="bottom"
-    android:orientation="vertical">
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="32dp"
-        android:background="@drawable/bottom_sheet_top_border"
-        android:backgroundTint="?attr/eduHalfSheetBGColor" />
-
-    <LinearLayout
-        android:id="@+id/view_wrapper"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="?attr/eduHalfSheetBGColor"
-        android:orientation="vertical"
-        android:paddingLeft="@dimen/bottom_sheet_edu_padding"
-        android:paddingRight="@dimen/bottom_sheet_edu_padding">
-
-        <TextView
-            android:id="@+id/content_text"
-            style="@style/TextHeadline"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="48dp"
-            android:layout_marginBottom="48dp"
-            android:gravity="center"
-            android:text="@string/work_profile_edu_personal_apps"
-            android:textAlignment="center"
-            android:textColor="@android:color/white"
-            android:textSize="20sp" />
-
-        <Button
-            android:id="@+id/proceed"
-            android:layout_width="wrap_content"
-            android:layout_height="48dp"
-            android:layout_gravity="end"
-            android:background="?android:attr/selectableItemBackground"
-            android:gravity="center"
-            android:text="@string/work_profile_edu_next"
-            android:textAlignment="center"
-            android:textColor="@android:color/white" />
-    </LinearLayout>
-</com.android.launcher3.views.WorkEduView>
\ No newline at end of file
diff --git a/res/mipmap-hdpi/ic_launcher_home.png b/res/mipmap-hdpi/ic_launcher_home.png
deleted file mode 100644
index d068d92..0000000
--- a/res/mipmap-hdpi/ic_launcher_home.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_home.png b/res/mipmap-mdpi/ic_launcher_home.png
deleted file mode 100644
index 16c8ec2..0000000
--- a/res/mipmap-mdpi/ic_launcher_home.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_home.png b/res/mipmap-xhdpi/ic_launcher_home.png
deleted file mode 100644
index 8b2671b..0000000
--- a/res/mipmap-xhdpi/ic_launcher_home.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_home.png b/res/mipmap-xxhdpi/ic_launcher_home.png
deleted file mode 100644
index 43d8b7d..0000000
--- a/res/mipmap-xxhdpi/ic_launcher_home.png
+++ /dev/null
Binary files differ
diff --git a/res/raw/downgrade_schema.json b/res/raw/downgrade_schema.json
index 8f1780e..bc25cec 100644
--- a/res/raw/downgrade_schema.json
+++ b/res/raw/downgrade_schema.json
@@ -2,8 +2,14 @@
   // Note: Comments are not supported in JSON schema, but android parser is lenient.
 
   // Maximum DB version supported by this schema
-  "version" : 28,
+  "version" : 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);",
+    "INSERT INTO favorites SELECT _id, title, intent, container, screen, cellX, cellY, spanX, spanY, itemType, appWidgetId, iconPackage, iconResource, icon, appWidgetProvider, modified, restored, profileId, rank, options FROM temp_favorites;",
+    "DROP TABLE temp_favorites;"
+  ],
   "downgrade_to_27" : [
     "CREATE TABLE workspaceScreens (_id INTEGER PRIMARY KEY,screenRank INTEGER,modified INTEGER NOT NULL DEFAULT 0)",
     "insert into workspaceScreens (_id, screenRank) select screen as _id, screen as screenRank from favorites where container = -100 group by screen order by screen"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index c39a9dc..6894e3c 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Afgelaaide program in veiligmodus gedeaktiveer"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Legstukke gedeaktiveer in Veiligmodus"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Kortpad is nie beskikbaar nie"</string>
-    <string name="home_screen" msgid="806512411299847073">"Tuisskerm"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Gepasmaakte handelinge"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Raak en hou om \'n legstuk op te tel."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dubbeltik en hou om \'n legstuk op te tel of gebruik gepasmaakte handelinge."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Tuis"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Verdeelde skerm"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed by %2$d hoog"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Raak en hou om self te plaas"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Voeg outomaties by"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Vee teks in hierdie soekkasie uit"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Geen legstukke beskikbaar nie"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Geen soekresultate nie"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Persoonlik"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Werk"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Gesprekke"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Deursoek programme"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Laai tans programme …"</string>
     <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="notifications_header" msgid="1404149926117359025">"Kennisgewings"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Raak en hou om \'n kortpad op te tel."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dubbeltik en hou om \'n kortpad op te tel of gebruik gepasmaakte handelinge."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Niks meer spasie op die tuisskerm nie."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen plek meer in die Gunstelinge-laai nie"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Programmelys"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lys persoonlike programme"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lys werkprogramme"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Tuis"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Verwyder"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleer"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Programinligting"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Vouer hernoem na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Style en muurpapiere"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Tuis-instellings"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Soek"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Hierdie program is nie geïnstalleer nie"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Die program vir hierdie ikoon is nie geïnstalleer nie. Jy kan dit verwyder of die program soek en dit self installeer."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeer tans; <xliff:g id="PROGRESS">%2$s</xliff:g> voltooi"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> laai tans af, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wag tans om te installeer"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-legstukke"</string>
     <string name="widgets_list" msgid="796804551140113767">"Legstukkelys"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Legstukkelys is toegemaak"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Voeg by tuisskerm"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Volgende"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Het dit"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Werkprofiel is onderbreek"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Werkprogramme kan nie vir jou kennisgewings stuur, jou battery gebruik of toegang tot jou ligging kry nie"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Werkprofiel is onderbreek. Werkprogramme kan nie vir jou kennisgewings stuur, jou battery gebruik of toegang tot jou ligging kry nie"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Werkprogramme kan nie vir jou kennisgewings stuur, jou battery gebruik of toegang tot jou ligging kry nie"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Werkprofiel is onderbreek. Werkprogramme kan nie vir jou kennisgewings stuur, jou battery gebruik of toegang tot jou ligging kry nie"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Werkprogramme het \'n kenteken en is sigbaar vir jou IT-administrateur"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Het dit"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Onderbreek werkprogramme"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Skakel aan"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Onderbreek werkprogramme en kennisgewings"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Misluk: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 90a54c0..8097ba1 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"የወረደው መተግበሪያ ደህንነቱ በተጠበቀ ሁኔታ ውስጥ ተሰናክሏል"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"ምግብሮች በደህንነቱ የተጠበቀ ሁኔታ ተሰናክለዋል"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"አቋራጭ አይገኝም"</string>
-    <string name="home_screen" msgid="806512411299847073">"መነሻ ገጽ"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"ብጁ እርምጃዎች"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ፍርግም ለማንሳት ይንኩ እና ይያዙት"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"አንድ ንዑስ ፕሮግራም ለመምረጥ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ አድርገው ይያዙ።"</string>
+    <string name="home_screen" msgid="5629429142036709174">"መነሻ"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"የተከፈለ ማያ ገጽ"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ስፋት በ%2$d ከፍታ"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ራስዎ ለማስቀመጥ ነክተው ይያዙት"</string>
     <string name="place_automatically" msgid="8064208734425456485">"በራስ-ሰር አክል"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ጽሁፍን ከፍለጋ ሳጥን አጽዳ"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ምግብሮች አይገኙም"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"ምንም የፍለጋ ውጤቶች የሉም"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"የግል"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ስራ"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"ውይይቶች"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"መተግበሪያዎችን ፈልግ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"መተግበሪያዎችን በመጫን ላይ…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"ከ«<xliff:g id="QUERY">%1$s</xliff:g>» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ተጨማሪ መተግበሪያዎች ይፈልጉ"</string>
     <string name="label_application" msgid="8531721983832654978">"መተግበሪያ"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ማሳወቂያዎች"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"አንድ አቋራጭ ለመውሰድ ነክተው ይያዙ።"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"አንድ አቋራጭ ለመውሰድ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ አድርገው ይያዙ።"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"አቋራጭን ለማንቀሳቀስ ይንኩ እና ይያዙ"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"አቋራጭን ለማንቀሳቀስ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ ያድርጉ እና ይያዙ።"</string>
     <string name="out_of_space" msgid="4691004494942118364">"በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"የመተግበሪያዎች ዝርዝር"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"የግል መተግበሪያዎች ዝርዝር"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"የሥራ መተግበሪያዎች ዝርዝር"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"መነሻ"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"አስወግድ"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"አራግፍ"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"የመተግበሪያ መረጃ"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"አቃፊ <xliff:g id="NAME">%1$s</xliff:g> ተብሎ ዳግም ተሰይሟል"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ንጥሎች"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ወይም ተጨማሪ ንጥሎች"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ፍርግሞች"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ቅጦች እና ልጣፎች"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"የመነሻ ቅንብሮች"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"ፈልግ"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ይህ መተግበሪያ አልተጫነም"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"የዚህ አዶ መተግበሪያ አልተጫነም። ማስወገድ ወይም መተግበሪያውን መፈለግና ራስዎ መጫን ይችላሉ።"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> በመጫን ላይ፣ <xliff:g id="PROGRESS">%2$s</xliff:g> ተጠናቅቋል"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> በመውረድ ላይ፣ <xliff:g id="PROGRESS">%2$s</xliff:g> ተጠናቋል"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ለመጫን በመጠበቅ ላይ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ንዑስ ፕሮግራሞች"</string>
     <string name="widgets_list" msgid="796804551140113767">"የመግብሮች ዝርዝር"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"የመግብሮች ዝርዝር ተዘግቷል"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ወደ መነሻ ማያ ገጽ ያክሉ"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ቀጣይ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ገባኝ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"የሥራ መገለጫ ባለበት ቆሟል"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"የስራ መተግበሪያዎች ማሳወቂያዎችን ወደ እርስዎ መላክ፣ ባትሪዎን መጠቀም ወይም አካባቢዎን መድረስ አይችሉም"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"የሥራ መገለጫ ባለበት ቆሟል። የሥራ መተግበሪያዎች ማሳወቂያዎችን ወደ እርስዎ መላክ፣ ባትሪዎን መጠቀም ወይም አካባቢዎን መድረስ አይችሉም"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"የስራ መተግበሪያዎች ማሳወቂያዎችን ወደ እርስዎ መላክ፣ ባትሪዎን መጠቀም ወይም አካባቢዎ ዘንድ መድረስ አይችሉም"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"የሥራ መገለጫ ባለበት ቆሟል። የስራ መተግበሪያዎች ማሳወቂያዎችን ወደ እርስዎ መላክ፣ ባትሪዎን መጠቀም ወይም አካባቢዎ ዘንድ መድረስ አይችሉም"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"የሥራ መተግበሪያዎች ባጅ የተደረገባቸው ሲሆን ለእርስዎ IT ቡድን ታይ ናቸው"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ገባኝ"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"የሥራ መተግበሪያዎችን ባሉበት አቁም"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"አብራ"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"አጣራ"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"የስራ መተግበሪያዎችን እና ማሳወቂያዎችን ባሉበት ያቁሙ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"አልተሳካም፦ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index a9e5cb4..b5115e2 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -26,28 +26,52 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"تم إيقاف التطبيق الذي تم تنزيله في الوضع الآمن"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"الأدوات غير مفعّلة في الوضع الآمن"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"الاختصار غير متاح"</string>
-    <string name="home_screen" msgid="806512411299847073">"الشاشة الرئيسية"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"الإجراءات المخصّصة"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"المس مع الاستمرار لاختيار إحدى الأدوات."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"انقر مرّتين مع الاستمرار لاختيار أداة أو استخدم الإجراءات المخصصة."</string>
+    <string name="home_screen" msgid="5629429142036709174">"الشاشة الرئيسية"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"تقسيم الشاشة"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏العرض %1$d الطول %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"انقر مع الاستمرار لإضافة العنصر يدويًا"</string>
     <string name="place_automatically" msgid="8064208734425456485">"الإضافة تلقائيًا"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"محو النص من مربّع البحث"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ما مِن أدوات متاحة."</string>
+    <string name="no_search_results" msgid="6518732304311458580">"ما مِن نتائج بحث."</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"الأدوات الشخصية"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"أدوات العمل"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"المحادثات"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"بحث في التطبيقات"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"جارٍ تحميل التطبيقات…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"لم يتم العثور على أي تطبيقات تتطابق مع \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"البحث عن مزيد من التطبيقات"</string>
     <string name="label_application" msgid="8531721983832654978">"تطبيق"</string>
     <string name="notifications_header" msgid="1404149926117359025">"الإشعارات"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"انقر مع الاستمرار لاختيار اختصار."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"يمكنك النقر مرّتين مع الاستمرار لاختيار اختصار أو استخدام الإجراءات المخصصة."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"انقر مرتين مع تثبيت إصبعك لنقل اختصار."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"انقر مرتين مع تثبيت إصبعك لنقل اختصار أو استخدام الإجراءات المخصّصة."</string>
     <string name="out_of_space" msgid="4691004494942118364">"ليس هناك مساحة أخرى في هذه الشاشة الرئيسية."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"لا يوجد المزيد من الحقول في علبة المفضلة"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"قائمة التطبيقات"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"قائمة التطبيقات الشخصية"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"قائمة تطبيقات العمل"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"الرئيسية"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"إزالة"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"إلغاء التثبيت"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"معلومات عن التطبيق"</string>
@@ -84,7 +108,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"تمت إعادة تسمية المجلد إلى <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر أو أكثر"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"الأنماط والخلفيات"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"إعدادات الشاشة الرئيسية"</string>
@@ -105,15 +128,15 @@
     <string name="abandoned_search" msgid="891119232568284442">"بحث"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"لم يتم تثبيت هذا التطبيق"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"لم يتم تثبيت تطبيق لهذا الرمز. يمكنك إزالته أو البحث عن التطبيق وتثبيته يدويًا."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"جارٍ تثبيت <xliff:g id="NAME">%1$s</xliff:g>، مستوى التقدم: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"جارٍ تنزيل <xliff:g id="NAME">%1$s</xliff:g>، اكتمل <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> في انتظار التثبيت"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"أدوات <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"قائمة الأدوات"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"تم إغلاق قائمة الأدوات."</string>
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"الإضافة إلى الشاشة الرئيسية"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"الإضافة إلى شاشة الصفحة الرئيسية"</string>
     <string name="action_move_here" msgid="2170188780612570250">"نقل العنصر إلى هنا"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"تمت إضافة العنصر إلى الشاشة الرئيسية"</string>
-    <string name="item_removed" msgid="851119963877842327">"تم حذف العنصر"</string>
+    <string name="item_removed" msgid="851119963877842327">"تمّت إزالة العنصر."</string>
     <string name="undo" msgid="4151576204245173321">"تراجع"</string>
     <string name="action_move" msgid="4339390619886385032">"نقل العنصر"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"نقل إلى الصف <xliff:g id="NUMBER_0">%1$s</xliff:g> العمود <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
@@ -145,8 +168,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"التالي"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"حسنًا"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"تم إيقاف الملف الشخصي للعمل مؤقتًا"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"لا يمكن لتطبيقات العمل إرسال إشعارات إليك أو استخدام بطاريتك أو الوصول إلى موقعك الجغرافي."</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"تم إيقاف الملف الشخصي للعمل مؤقتًا. لا يمكن لتطبيقات العمل إرسال إشعارات إليك أو استخدام بطاريتك أو الوصول إلى موقعك الجغرافي."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"لا يمكن لتطبيقات العمل إرسال إشعارات إليك أو استخدام بطاريتك أو الوصول إلى موقعك الجغرافي."</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"تم إيقاف الملف الشخصي للعمل مؤقتًا. لا يمكن لتطبيقات العمل إرسال إشعارات إليك أو استخدام بطاريتك أو الوصول إلى موقعك الجغرافي."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"تحمل تطبيقات العمل شارة وتكون مرئية لمشرف تكنولوجيا المعلومات."</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"حسنًا"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"إيقاف تطبيقات العمل مؤقتًا"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"تفعيل"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"فلتر"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"إيقاف تطبيقات العمل وإشعاراتها مؤقتًا"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"تعذَّر <xliff:g id="WHAT">%1$s</xliff:g>."</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 8e9aafc..31ac27f 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনল’ড কৰা এপটোক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"ৱিজেটবোৰক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"শ্বৰ্টকাট নাই"</string>
-    <string name="home_screen" msgid="806512411299847073">"গৃহ স্ক্ৰীণ"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"উপযোগিতা অনুসৰি কৰা কাৰ্যবিলাক"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"কোনো ৱিজেট বাছনি কৰিবলৈ স্পৰ্শ কৰি থাকক।"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"কোনো ৱিজেট বাছনি কৰিবলৈ অথবা উপযোগিতা অনুসৰি কাৰ্যবিলাক ব্য়ৱহাৰ কৰিবলৈ দুবাৰ টিপি থাকক।"</string>
+    <string name="home_screen" msgid="5629429142036709174">"গৃহ স্ক্ৰীন"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"বিভাজিত স্ক্ৰীন"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d বহল x %2$d ওখ"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"মেনুৱেলভাৱে ৰাখিবলৈ স্পৰ্শ কৰি থাকক"</string>
     <string name="place_automatically" msgid="8064208734425456485">"স্বয়ংক্ৰিয়ভাবে যোগ কৰক"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"সন্ধান বাকচত থকা পাঠ মচক"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"কোনো ৱিজেট নাই"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"সন্ধানৰ কোনো ফলাফল নাই"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ব্যক্তিগত"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"কৰ্মস্থান"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"বাৰ্তালাপ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"এপসমূহ সন্ধান কৰক"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"এপসমূহ ল’ড কৰি থকা হৈছে…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"ৰ সৈতে মিলা কোনো এপ্ বিচাৰি পোৱা নগ\'ল"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"আৰু অধিক এপবোৰ সন্ধান কৰক"</string>
     <string name="label_application" msgid="8531721983832654978">"এপ্"</string>
     <string name="notifications_header" msgid="1404149926117359025">"জাননীসমূহ"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"কোনো শ্বৰ্টকাট বাছনি কৰিবলৈ স্পৰ্শ কৰি হেঁচি ধৰক।"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"কোনো শ্বৰ্টকাট বাছনি কৰিবলৈ দুবাৰ টিপি হেঁচি ধৰক, বা নিজৰ উপযোগিতা অনুসৰি সৃষ্টি কৰা কাৰ্যসমূহ ব্যৱহাৰ কৰক।"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"শ্বৰ্টকাট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক।"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"কোনো শ্বৰ্টকাট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক অথবা কাষ্টম কাৰ্য ব্যৱহাৰ কৰক।"</string>
     <string name="out_of_space" msgid="4691004494942118364">"এই গৃহ স্ক্ৰীণত আৰু বেছি ঠাই নাই।"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দৰ ট্ৰে\'ত আৰু বেছি ঠাই নাই"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"এপৰ সূচী"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ব্যক্তিগত এপৰ তালিকা"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"কৰ্মস্থানৰ এপৰ তালিকা"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"গৃহপৃষ্ঠা"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"আঁতৰাওক"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনষ্টল কৰক"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"এপ সম্পৰ্কীয় তথ্য"</string>
@@ -80,10 +96,9 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ফ\'ল্ডাৰৰ নাম সলনি কৰি <xliff:g id="NAME">%1$s</xliff:g> কৰা হৈছে"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> টা বস্তু"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> টা অথবা তাতকৈ অধিক বস্তু"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ৱিজেটসমূহ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ৱালপেপাৰসমূহ"</string>
-    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ষ্টাইল &amp; ৱালপেপাৰ"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"গৃহ ছেটিংসমূহ"</string>
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"শৈলী আৰু ৱালপেপাৰ"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"গৃহ ছেটিং"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপোনাৰ প্ৰশাসকে অক্ষম কৰি ৰাখিছে"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"গৃহ স্ক্ৰীণ ঘূৰোৱাৰ অনুমতি দিয়ক"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ফ\'নটো যেতিয়া ঘূৰোৱা হয়"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"এই এপটো ইনষ্টল কৰা হোৱা নাই"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনৰ এপটো ইনষ্টল কৰা হোৱা নাই। আপুনি এইটো আঁতৰাব পাৰে অথবা এপটো বিচাৰি মেনুৱেলভাৱে ইনষ্টল কৰিব পাৰে।"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হৈছে"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনল’ড কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হ’ল"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল হোৱালৈ অপেক্ষা কৰি থকা হৈছে"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ৱিজেট"</string>
     <string name="widgets_list" msgid="796804551140113767">"ৱিজেটৰ তালিকা"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ৱিজেটৰ তালিকা বন্ধ কৰা হ’ল"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"গৃহ স্ক্ৰীণত যোগ কৰক"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"পৰৱৰ্তী"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"বুজি পালোঁ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"কৰ্মস্থানৰ প্ৰ\'ফাইলটো পজ কৰা আছে"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"কৰ্মস্থানৰ এপ্‌সমূহে আপোনালৈ জাননীসমূহ পঠিয়াব, আপোনাৰ বেটাৰী ব্যৱহাৰ কৰিব অথবা আপোনাৰ অৱস্থান এক্সেছ কৰিব নোৱাৰে"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"কৰ্মস্থানৰ প্ৰ\'ফাইলটো পজ কৰা আছে। কৰ্মস্থানৰ এপ্‌সমূহে আপোনালৈ জাননীসমূহ পঠিয়াব, আপোনাৰ বেটাৰী ব্যৱহাৰ কৰিব অথবা আপোনাৰ অৱস্থান এক্সেছ কৰিব নোৱাৰে"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"কৰ্মস্থানৰ এপ্‌সমূহে আপোনালৈ জাননী পঠিয়াব, আপোনাৰ বেটাৰী ব্যৱহাৰ কৰিব অথবা আপোনাৰ অৱস্থান এক্সেছ কৰিব নোৱাৰে"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"কৰ্মস্থানৰ প্ৰ’ফাইলটো পজ কৰা আছে। কৰ্মস্থানৰ এপ্‌সমূহে আপোনালৈ জাননী পঠিয়াব, আপোনাৰ বেটাৰী ব্যৱহাৰ কৰিব অথবা আপোনাৰ অৱস্থান এক্সেছ কৰিব নোৱাৰে"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"কর্মস্থানৰ এপ্‌সমূহ প্ৰতীকেৰে চিহ্নিত কৰা হয় আৰু সেইবোৰ আপোনাৰ আইটি প্ৰশাসকৰ বাবে দৃশ্যমান হয়"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"বুজি পালোঁ"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"কৰ্মস্থানৰ এপ্‌ পজ কৰক"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"অন কৰক"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ফিল্টাৰ"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"কর্মস্থানৰ এপ্‌সমূহ আৰু জাননীসমূহ পজ কৰক"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"বিফল: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index aed975a..bc0717d 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Güvənli rejimdə icazə verilməyən tətbiq endirildi"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Vidcetlər Güvənli rejimdə deaktiv edilib"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Qısayol əlçatan deyil"</string>
-    <string name="home_screen" msgid="806512411299847073">"Əsas ekran"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Fərdi əməliyyatlar"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidceti götürmək üçün toxunub saxlayın."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Vidceti götürmək üçün &amp; iki dəfə toxunub saxlayın və ya fərdi fəaliyyətləri istifadə edin."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Əsas səhifə"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Bölünmüş ekran"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d hündürlük %1$d enində"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Manual olaraq yerləşdirmək üçün toxunaraq basıb saxlayın"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Avtomatik əlavə edin"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Axtarış qutusundan mətni silin"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Əlçatan vidcet yoxdur"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Axtarış nəticəsi yoxdur"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Şəxsi"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"İş"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Söhbətlər"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tətbiqləri axtarın"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Tətbiqlər yüklənir…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Bildirişlər"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Qısayolu seçmək üçün basıb saxlayın."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Qısayolu seçmək üçün iki dəfə basıb saxlayın və ya fərdi əməliyyatlardan istifadə edin."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Bu Əsas ekranda boş yer yoxdur."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritlər-də yer yoxdur"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Tətbiq siyahısı"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Şəxsi tətbiqlərin siyahısı"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"İş tətbiqlərinin siyahısı"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Əsas səhifə"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Silin"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sistemdən sil"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Tətbiq infosu"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Qovluq adı <xliff:g id="NAME">%1$s</xliff:g> ilə dəyişdirildi"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> element"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> və ya daha çox element"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Üslub və divar kağızları"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home ayarları"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Axtarış"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Bu tətbiq quraşdırılmayıb"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Bu ikona üçün tətbiq quraşdırılmayıb. Onu silə bilərsiniz, və ya tətbiqi taparaq manual yol ilə quraşdıra bilərsiniz."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> quraşdırır, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlanıb"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> endirilir, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> yüklənmək üçün gözləyir"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> vidcetləri"</string>
     <string name="widgets_list" msgid="796804551140113767">"Vidcet siyahısı"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Vidcet siyahısı bağlandı"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Əsas ekrana əlavə edin"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Növbəti"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Anladım"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"İş profilinə fasilə verilib"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"İş tətbiqləri sizə bildirişlər göndərə, batareyanızdan istifadə edə və ya məkanınıza daxil ola bilməz"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"İş profilinə fasilə verilib. İş tətbiqləri sizə bildirişlər göndərə, batareyanızdan istifadə edə və ya məkanınıza daxil ola bilməz"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"İş tətbiqləri sizə bildirişlər göndərə, batareyanızdan istifadə edə və ya məkanınıza daxil ola bilməz"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"İş profili durdurulub. İş tətbiqləri sizə bildirişlər göndərə, batareyanızdan istifadə edə və ya məkanınıza daxil ola bilməz"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"İş tətbiqləri nişanlanıb və İT administratorunuza görünür"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Anladım"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"İş tətbiqlərini durdurun"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aktiv edin"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"İş tətbiqlərinə və bildirişlərə fasilə verin"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Alınmadı: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index e812368..ed08ca2 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -26,28 +26,46 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Preuzeta aplikacija je onemogućena u Bezbednom režimu"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Vidžeti su onemogućeni u Bezbednom režimu"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Prečica nije dostupna"</string>
-    <string name="home_screen" msgid="806512411299847073">"Početni ekran"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Prilagođene radnje"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite i zadržite da biste izabrali vidžet."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite i zadržite da biste izabrali vidžet ili koristite prilagođene radnje."</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="long_press_widget_to_add" msgid="3587712543577675817">"Dvaput 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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"širina od %1$d i visina od %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dodirnite i zadržite da biste postavili ručno"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Automatski dodaj"</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_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">"Pretraga"</string>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Obrišite tekst iz okvira za pretragu"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nije dostupan nijedan vidžet"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nema rezultata pretrage"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Lično"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Konverzacije"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretražite aplikacije"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Aplikacije se učitavaju…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Obaveštenja"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Dodirnite i zadržite da biste izabrali prečicu."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvaput dodirnite i zadržite da biste izabrali prečicu ili koristite prilagođene radnje."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Dvaput 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>
     <string name="out_of_space" msgid="4691004494942118364">"Nema više prostora na ovom početnom ekranu."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Omiljeno"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista aplikacija"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista ličnih aplikacija"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista poslovnih aplikacija"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Infor. o aplikaciji"</string>
@@ -74,14 +92,13 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. stranica od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. početni ekran od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Direktorijum je otvoren, <xliff:g id="WIDTH">%1$d</xliff:g> puta <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da biste zatvorili direktorijum"</string>
+    <string name="folder_opened" msgid="94695026776264709">"Folder je otvoren, <xliff:g id="WIDTH">%1$d</xliff:g> puta <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da biste zatvorili folder"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste sačuvali preimenovanje"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Direktorijum je zatvoren"</string>
-    <string name="folder_renamed" msgid="1794088362165669656">"Direktorijum je preimenovan u <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_name_format_exact" msgid="8626242716117004803">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
-    <string name="folder_name_format_overflow" msgid="4270108890534995199">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</string>
+    <string name="folder_closed" msgid="4100806530910930934">"Folder je zatvoren"</string>
+    <string name="folder_renamed" msgid="1794088362165669656">"Folder je preimenovan u <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
+    <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadine"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Podešavanja početnog ekrana"</string>
@@ -102,9 +119,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Pretraži"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ova aplikacija nije instalirana"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija za ovu ikonu nije instalirana. Možete da je uklonite ili da potražite aplikaciju i instalirate je ručno."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se instalira, <xliff:g id="PROGRESS">%2$s</xliff:g> gotovo"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se preuzima, završeno je <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> čeka na instaliranje"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Vidžeti za <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista vidžeta"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lista vidžeta je zatvorena"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodajte na početni ekran"</string>
@@ -117,11 +134,11 @@
     <string name="move_to_position" msgid="6750008980455459790">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju u omiljenim"</string>
     <string name="item_moved" msgid="4606538322571412879">"Stavka je premeštena"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Dodaj u direktorijum: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Dodaj u direktorijum u kome je <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Stavka je dodata u direktorijum"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Napravite direktorijum sa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Direktorijum je napravljen"</string>
+    <string name="add_to_folder" msgid="9040534766770853243">"Dodaj u folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Dodaj u folder u kome je <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="added_to_folder" msgid="4793259502305558003">"Stavka je dodata u folder"</string>
+    <string name="create_folder_with" msgid="4050141361160214248">"Napravite folder sa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="folder_created" msgid="6409794597405184510">"Folder je napravljen"</string>
     <string name="action_move_to_workspace" msgid="1603837886334246317">"Premesti na početni ekran"</string>
     <string name="action_resize" msgid="1802976324781771067">"Promeni veličinu"</string>
     <string name="action_increase_width" msgid="8773715375078513326">"Povećaj širinu"</string>
@@ -142,8 +159,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Dalje"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Važi"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Poslovni profil je pauziran"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Poslovne aplikacije ne mogu da vam šalju obaveštenja, koriste bateriju niti pristupaju lokaciji"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Poslovni profil je pauziran. Poslovne aplikacije ne mogu da vam šalju obaveštenja, koriste bateriju niti pristupaju lokaciji"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Poslovne aplikacije ne mogu da vam šalju obaveštenja, koriste bateriju niti pristupaju lokaciji"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Poslovni profil je pauziran. Poslovne aplikacije ne mogu da vam šalju obaveštenja, koriste bateriju niti pristupaju lokaciji"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Poslovne aplikacije su označene značkom i IT administrator može da ih vidi"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Važi"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pauziraj poslovne aplikacije"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Uključi"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte poslovne aplikacije i obaveštenja"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 0b2b6f1..c2d9946 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -26,28 +26,48 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Спампаваная праграма адключана ў Бяспечным рэжыме"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Віджэты адключаны ў Бяспечным рэжыме"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Ярлык недаступны"</string>
-    <string name="home_screen" msgid="806512411299847073">"Галоўны экран"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Спецыяльныя дзеянні"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Дакраніцеся і ўтрымлiвайце віджэт, каб выбр. яго."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць віджэт або выкарыстоўваць карыстальніцкія дзеянні."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Галоўны экран"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Падзелены экран"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Шырына: %1$d, вышыня: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Каб размясціць уручную, дакраніцеся і ўтрымлівайце"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Дадаць аўтаматычна"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Ачысціць тэкст у полі пошуку"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Віджэты недаступныя"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Няма вынікаў пошуку"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Асабістыя"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Працоўныя"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Размовы"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Пошук праграм"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Праграмы загружаюцца…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Праграм, якія адпавядаюць запыту \"<xliff:g id="QUERY">%1$s</xliff:g>\", не знойдзена"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Шукаць іншыя праграмы"</string>
     <string name="label_application" msgid="8531721983832654978">"Праграма"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Апавяшчэнні"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Дакраніцеся і ўтрымлiвайце ярлык, каб дадаць яго."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць ярлык або выкарыстоўваць спецыяльныя дзеянні."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Націсніце і ўтрымлівайце ярлык для перамяшчэння."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Дакраніцеся двойчы і ўтрымлівайце, каб перамясціць ярлык або выкарыстоўваць спецыяльныя дзеянні."</string>
     <string name="out_of_space" msgid="4691004494942118364">"На гэтым Галоўным экране больш няма месца."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"У латку \"Абранае\" больш няма месца"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Спіс праграм"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Спіс персанальных праграм"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Спіс працоўных праграм"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Галоўная"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Выдаліць"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Дэінсталяваць"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Пра праграму"</string>
@@ -82,7 +102,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Папка перайменавана ў <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g> ці больш"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Віджэты"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стылі і шпалеры"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Налады галоўнага экрана"</string>
@@ -96,16 +115,16 @@
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Каб паказваліся значкі апавяшчэнняў, уключыце апавяшчэнні праграм для <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Змяніць налады"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"Паказваць значкі апавяшчэнняў"</string>
-    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Дадаць значкі праграм на Галоўны экран"</string>
+    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Дадаваць значкі праграм на Галоўны экран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для новых праграм"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Невядома"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Выдаліць"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Шукаць"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Гэта праграма не ўсталявана"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Праграма для гэтага значка не ўсталявана. Вы можаце выдаліць яе або выканаць пошук і ўсталяваць яе ўручную."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Усталёўваецца праграма \"<xliff:g id="NAME">%1$s</xliff:g>\", завершана <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Ідзе спампоўка <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> завершана"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чакае ўсталёўкі"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Віджэты <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Спіс віджэтаў"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Спіс віджэтаў закрыты"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Дадаць на Галоўны экран"</string>
@@ -143,8 +162,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Далей"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Зразумела"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Працоўны профіль прыпынены"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Працоўныя праграмы не могуць адпраўляць вам апавяшчэнні, выкарыстоўваць акумулятар або атрымліваць доступ да вашага месцазнаходжання"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Працоўны профіль прыпынены. Працоўныя праграмы не могуць адпраўляць вам апавяшчэнні, выкарыстоўваць акумулятар або атрымліваць доступ да вашага месцазнаходжання"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Працоўныя праграмы не могуць адпраўляць вам апавяшчэнні, выкарыстоўваць акумулятар або атрымліваць доступ да даных пра ваша месцазнаходжанне"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Працоўны профіль прыпынены. Працоўныя праграмы не могуць адпраўляць вам апавяшчэнні, выкарыстоўваць акумулятар або атрымліваць доступ да даных пра ваша месцазнаходжанне"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Працоўныя праграмы маюць значкі і бачныя IT-адміністратару"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Зразумела"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Прыпыніць працоўныя праграмы"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Уключыць"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Фільтр"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Прыпыніць працоўныя праграмы і апавяшчэнні"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не ўдалося: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 512a7fc..390a8f8 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Изтегленото приложение е деактивирано в безопасния режим"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Приспособленията са деактивирани в безопасния режим"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Няма достъп до прекия път"</string>
-    <string name="home_screen" msgid="806512411299847073">"Начален екран"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Персонализирани действия"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Докоснете и задръжте за избор на приспособление."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Докоснете двукратно и задръжте за избор на приспособление или използвайте персонализирани действия."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Начален екран"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделен екран"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d и височина %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Докоснете и задръжте, за да поставите ръчно"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Автоматично добавяне"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Изчистване на текста от полето за търсене"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Няма налични приспособления"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Няма резултати от търсенето"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Лични"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Служебни"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Разговори"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Търсене в приложенията"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Приложенията се зареждат…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Няма намерени приложения, съответстващи на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Търсене на още приложения"</string>
     <string name="label_application" msgid="8531721983832654978">"Приложение"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Известия"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Докоснете и задръжте за избор на пряк път."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Докоснете двукратно и задръжте за избор на пряк път или използвайте персонализирани действия."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Докоснете и задръжте за преместване на пряк път."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Докоснете двукратно и задръжте за преместване на пряк път или използвайте персонализирани действия."</string>
     <string name="out_of_space" msgid="4691004494942118364">"На този начален екран няма повече място."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Няма повече място в областта с любимите"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Списък с приложения"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Списък с лични приложения"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Списък със служебни приложения"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Начало"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Премахване"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталиране"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Информация за прилож."</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана на „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“ – <xliff:g id="SIZE">%2$d</xliff:g> елемента"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“ – <xliff:g id="SIZE">%2$d</xliff:g> или повече елементи"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилове и тапети"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Настройки за началния екран"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Търсене"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Това приложение не е инсталирано"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Приложението за тази икона не е инсталирано. Можете да я премахнете или да потърсите приложението и да го инсталирате ръчно."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> завършено"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> се изтегля. Завършено: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> изчаква инсталиране"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Приспособления за <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Списък с приспособления"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Списъкът с приспособления е затворен"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Добавяне към началния екран"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Напред"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Разбрах"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Служебният потребителски профил е поставен на пауза"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Служебните приложения не могат да ви изпращат известия, да използват батерията или да осъществяват достъп до местоположението ви"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Служебният потребителски профил е поставен на пауза. Служебните приложения не могат да ви изпращат известия, да използват батерията или да осъществяват достъп до местоположението ви"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Служебните приложения не могат да ви изпращат известия, да използват батерията или да осъществяват достъп до местоположението ви"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Служебният потребителски профил е поставен на пауза. Служебните приложения не могат да ви изпращат известия, да използват батерията или да осъществяват достъп до местоположението ви"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Служебните приложения са означени със значка и са видими за системния администратор"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Разбрах"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Поставяне на пауза на служебните приложения"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Включване"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтър"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Поставете на пауза служебните приложения и известия"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Неуспешно: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index b2d3070..4c45b29 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনলোড করা অ্যাপ্লিকেশান নিরাপদ মোডে অক্ষম রয়েছে"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"সুরক্ষিত মোডে উইজেট নিষ্ক্রিয় থাকে"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"শর্টকাটগুলি অনুপলব্ধ"</string>
-    <string name="home_screen" msgid="806512411299847073">"হোম স্ক্রিন"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"কাস্টম অ্যাকশন"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"কোনো উইজেট বেছে নিতে দুবার-আলতো চেপে ধরে থাকুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন৷"</string>
+    <string name="home_screen" msgid="5629429142036709174">"হোম"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"স্প্লিট স্ক্রিন"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"নিজে যোগ করতে টাচ করে ধরে রাখুন"</string>
     <string name="place_automatically" msgid="8064208734425456485">"স্বয়ংক্রিয়ভাবে যোগ করুন"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"সার্চ বক্স থেকে টেক্সট মুছুন"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"কোনও উইজেট ফলাফল নেই"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"কোনও সার্চ ফলাফল নেই"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ব্যক্তিগত"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"অফিস"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"কথোপকথন"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"অ্যাপ খুঁজুন"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"অ্যাপ লোড হচ্ছে…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" এর সাথে মেলে এমন কোনো অ্যাপ পাওয়া যায়নি"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"আরও অ্যাপ্লিকেশানের জন্য খুঁজুন"</string>
     <string name="label_application" msgid="8531721983832654978">"অ্যাপ"</string>
     <string name="notifications_header" msgid="1404149926117359025">"বিজ্ঞপ্তি"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"কোনও শর্টকাট বেছে নিতে টাচ করে ধরে থাকুন।"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"কোনও শর্টকাট বেছে নিতে ডবল ট্যাপ করে ধরে থাকুন অথবা কাস্টম অ্যাকশন ব্যবহার করুন।"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"একটি শর্টকাট সরাতে টাচ করে ধরে রাখুন।"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"একটি শর্টকাট সরাতে বা কাস্টম অ্যাকশন ব্যবহার করতে ডবল ট্যাপ করে ধরে রাখুন।"</string>
     <string name="out_of_space" msgid="4691004494942118364">"এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দসই ট্রে-তে আর কোনো জায়গা নেই"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"অ্যাপ্লিকেশানগুলির তালিকা"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ব্যক্তিগত অ্যাপের তালিকা"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"কাজের অ্যাপের তালিকা"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"হোম"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"সরান"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনস্টল করুন"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"অ্যাপের তথ্য"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ফোল্ডারের নাম পাল্টে <xliff:g id="NAME">%1$s</xliff:g> করা হয়েছে"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি আইটেম"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি বা তার বেশি আইটেম"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"উইজেট"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"স্টাইল এবং ওয়ালপেপার"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"হোম সেটিংস"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"সার্চ"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনের অ্যাপ্লিকেশানটি ইন্সটল করা নাই। আপনি এটি সরাতে পারেন বা অ্যাপ্লিকেশানটি সার্চ করে এটি নিজে ইন্সটল করতে পারেন।"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনস্টল করা হচ্ছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূর্ণ হয়েছে"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনলোড হচ্ছে <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পন্ন হয়েছে"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনস্টলের অপেক্ষায় রয়েছে"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> উইজেট"</string>
     <string name="widgets_list" msgid="796804551140113767">"উইজেটের তালিকা"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"উইজেটের তালিকা বন্ধ করা হয়েছে"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"হোম স্ক্রিনে যোগ করুন"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"পরের"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"বুঝেছি"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"অফিস প্রোফাইল বন্ধ করা আছে"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"অফিসের অ্যাপ আপনাকে বিজ্ঞপ্তি পাঠাতে, ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারবে না"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"অফিসের প্রোফাইল পজ করা আছে। অফিসের অ্যাপ আপনাকে বিজ্ঞপ্তি পাঠাতে, ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারবে না"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"অফিসের অ্যাপ আপনাকে নোটিফিকেশন পাঠাতে পারবে না। এছাড়া, ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারবে না"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"অফিসের প্রোফাইল পজ করা আছে। অফিসের অ্যাপ আপনাকে নোটিফিকেশন পাঠাতে পারবে না। এছাড়া, ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারবে না"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"অফিসের অ্যাপে ব্যাজ যোগ করা হয়েছে এবং আপনার আইটি অ্যাডমিন সেটি দেখতে পাবেন"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"বুঝেছি"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"অফিসের অ্যাপ পজ করুন"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"চালু করুন"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ফিল্টার"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"অফিস অ্যাপ এবং বিজ্ঞপ্তি বন্ধ করুন"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"কাজটি করা যায়নি: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 6ce19d8..ae46d5b 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -26,28 +26,46 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Preuzeta aplikacija je onemogućena u sigurnom načinu rada"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Vidžeti su onemogućeni u sigurnom načinu rada."</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Prečica nije dostupna"</string>
-    <string name="home_screen" msgid="806512411299847073">"Početni ekran"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Prilagođene akcije"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite &amp; i držite da biste uzeli dodatak."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dodirnite dvaput &amp; i držite da biste uzeli vidžet ili koristite prilagođene radnje."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dodirnite i držite da postavite ručno"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Dodaj automatski"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Brisanje teksta iz okvira za pretraživanje"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nijedan vidžet nije dostupan"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nema rezultata pretraživanja"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Lično"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Razgovori"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretražite aplikacije"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Aplikacije se učitavaju…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Obavještenja"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Dodirnite i držite da uzmete prečicu."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dodirnite dvaput i držite da uzmete prečicu ili koristite prilagođene akcije."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom ekranu nema više prostora."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora u ladici Omiljeno"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista aplikacija"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista ličnih aplikacija"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista poslovnih aplikacija"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Inform. o aplikaciji"</string>
@@ -81,7 +99,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Ime foldera je promijenjeno u <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, br. stavki: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadinske slike"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Postavke početnog ekrana"</string>
@@ -102,12 +119,12 @@
     <string name="abandoned_search" msgid="891119232568284442">"Pretraži"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ova aplikacija nije instalirana"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija za ovu ikonu nije instalirana. Možete je ukloniti ili potražiti aplikaciju i ručno je instalirati."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, završeno je <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se preuzima, završeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> čeka da se instalira"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Vidžeti za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Spisak vidžeta"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Spisak vidžeta je zatvoren"</string>
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodaj na početni ekran"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodavanje na početni ekran"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Premjesti stavku ovdje"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Stavka je dodana na Početni ekran."</string>
     <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
@@ -142,8 +159,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Sljedeće"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumijem"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Radni profil je pauziran"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Poslovne aplikacije vam ne mogu slati obavještenja, koristiti bateriju ili pristupiti vašoj lokaciji"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Radni profil je pauziran. Poslovne aplikacije vam ne mogu slati obavještenja, koristiti bateriju ili pristupiti vašoj lokaciji"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Poslovne aplikacije vam ne mogu slati obavještenja, koristiti bateriju niti pristupiti vašoj lokaciji"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Radni profil je pauziran. Poslovne aplikacije vam ne mogu slati obavještenja, koristiti bateriju niti pristupiti vašoj lokaciji"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Poslovne aplikacije su označene i vaš IT administrator ih može vidjeti"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Razumijem"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pauziraj poslovne aplikacije"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Uključi"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte poslovne aplikacije i obavještenja"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index efef421..c49dce5 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'aplicació que has baixat està desactivada al mode segur."</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"En Mode segur, els widgets estan desactivats."</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"La drecera no està disponible"</string>
-    <string name="home_screen" msgid="806512411299847073">"Pantalla d\'inici"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Accions personalitzades"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premut un widget per triar-lo."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Fes doble toc i mantén premut per seleccionar un widget o per utilitzar les accions personalitzades."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Inici"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d d\'amplada per %2$d d\'alçada"</string>
-    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén premut l\'element per afegir-lo manualment"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén-lo premut per afegir-lo manualment"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Afegeix automàticament"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Esborra el text del quadre de cerca"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"No hi ha widgets disponibles"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"No hi ha cap resultat de la cerca"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Treball"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Converses"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cerca aplicacions"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"S\'estan carregant les aplicacions…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notificacions"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Mantén premuda una drecera per seleccionar-la."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Fes doble toc i mantén premut per seleccionar una drecera o per utilitzar accions personalitzades."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Ja no queda espai en aquesta pantalla d\'inici."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No hi ha més espai a la safata Preferits."</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Llista d\'aplicacions"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Llista d\'aplicacions personals"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Llista d\'aplicacions de treball"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Inici"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Suprimeix"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstal·la"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Informació de l\'aplicació"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"S\'ha canviat el nom de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elements"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o més elements"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estils i fons de pantalla"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Config. pantalla d\'inici"</string>
@@ -94,16 +109,16 @@
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Per veure els punts de notificació, activa les notificacions de l\'aplicació <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Canvia la configuració"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostra els punts de notificació"</string>
-    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Afegeix icones a la pantalla d\'inici"</string>
+    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Afegeix icones d\'aplicacions a la pantalla d\'inici"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per a les aplicacions noves"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Suprimeix"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Aquesta aplicació no està instal·lada"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'aplicació d\'aquesta icona no està instal·lada. Pots suprimir-la o cercar l\'aplicació i instal·lar-la manualment."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"S\'està instal·lant <xliff:g id="NAME">%1$s</xliff:g>; s\'ha completat un <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"S\'està baixant <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completat"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"S\'està esperant per instal·lar <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Llista de widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"S\'ha tancat la llista de widgets"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Afegeix a la pantalla d\'inici"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Següent"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entesos"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"El perfil de treball està en pausa"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Les aplicacions de treball no poden enviar-te notificacions, consumir bateria ni accedir a la teva ubicació"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"El perfil de treball està en pausa. Les aplicacions de treball no poden enviar-te notificacions, consumir bateria ni accedir a la teva ubicació"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Les aplicacions de treball no poden enviar-te notificacions, consumir bateria ni accedir a la teva ubicació"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"El perfil de treball està en pausa. Les aplicacions de treball no poden enviar-te notificacions, consumir bateria ni accedir a la teva ubicació."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Les aplicacions de treball tenen una insígnia i són visibles per al teu administrador de TI"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Entesos"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Posa en pausa les aplicacions de treball"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Activa"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Posa en pausa les notificacions i les aplicacions de treball"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 91caf6f..a7f9908 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -26,28 +26,48 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Stažená aplikace je v nouzovém režimu zakázána"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"V nouzovém režimu jsou widgety zakázány."</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Zkratka není k dispozici"</string>
-    <string name="home_screen" msgid="806512411299847073">"Plocha"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Vlastní akce"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Widget vyberete dotykem a podržením."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvojitým klepnutím a podržením vyberte widget, případně použijte vlastní akce."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Domů"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdělená obrazovka"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šířka %1$d, výška %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Chcete-li položku umístit ručně, klepněte na ni a podržte ji"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Přidat automaticky"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Vymazat text z vyhledávacího pole"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nejsou k dispozici žádné widgety"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Žádné výsledky hledání"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobní"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Práce"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Konverzace"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hledat v aplikacích"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Načítání aplikací…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Oznámení"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Zkratku vyberete podržením."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvojitým klepnutím a podržením vyberte zkratku, případně použijte vlastní akce."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Na této ploše již není místo."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Na panelu Oblíbené položky již není místo."</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Seznam aplikací"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Seznam osobních aplikací"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Seznam pracovních aplikací"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Plocha"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstranit"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstalovat"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikaci"</string>
@@ -82,7 +102,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Složka přejmenována na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g> nebo více"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgety"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styly a tapety"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Nastavení plochy"</string>
@@ -97,15 +116,15 @@
     <string name="title_change_settings" msgid="1376365968844349552">"Změnit nastavení"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"Zobrazovat puntíky s oznámením"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Přidat na plochu ikony aplikací"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pro nové aplikace"</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"U nových aplikací"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstranit"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Hledat"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Tato aplikace není nainstalována"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikace pro tuto ikonu není nainstalována. Můžete ikonu odstranit nebo zkusit aplikaci vyhledat a nainstalovat ručně."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Instalace aplikace <xliff:g id="NAME">%1$s</xliff:g>, dokončeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Stahování aplikace <xliff:g id="NAME">%1$s</xliff:g> (dokončeno <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Instalace aplikace <xliff:g id="NAME">%1$s</xliff:g> čeká na zahájení"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgety <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Seznam widgetů"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Seznam widgetů zavřen"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Přidat na plochu"</string>
@@ -143,8 +162,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Další"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Rozumím"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Pracovní profil je pozastaven"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Pracovní aplikace vám nemohou zasílat oznámení, používat vaši baterii ani získat přístup k vaší poloze"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Pracovní profil je pozastaven. Pracovní aplikace vám nemohou zasílat oznámení, používat vaši baterii ani získat přístup k vaší poloze"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Pracovní aplikace vám nemohou zasílat oznámení, používat vaši baterii ani získat přístup k vaší poloze"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Pracovní profil je pozastaven. Pracovní aplikace vám nemohou zasílat oznámení, používat vaši baterii ani získat přístup k vaší poloze"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Pracovní aplikace jsou označené a viditelné vašemu administrátorovi IT"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pozastavit pracovní aplikace"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Zapnout"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtr"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pozastavit pracovní aplikace a oznámení"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Selhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 8a8db67..c6496fa 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloadet app er deaktiveret i sikker tilstand"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets er deaktiveret i Beskyttet tilstand"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Genvejen er ikke tilgængelig"</string>
-    <string name="home_screen" msgid="806512411299847073">"Startskærm"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Brugerdefinerede handlinger"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Tryk på en widget, og hold den nede for at vælge."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryk to gange, og hold fingeren nede for at vælge en widget eller bruge tilpassede handlinger."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i bredden og %2$d i højden"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tryk og hold for at placere manuelt"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Tilføj automatisk"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Ryd teksten i søgefeltet"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Der er ingen tilgængelig widget"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Der er ingen søgeresultater"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personlige"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Arbejde"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Samtaler"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Søg efter apps"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Indlæser apps…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifikationer"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Hold en genvej nede for at samle den op."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tryk to gange, og hold en genvej nede for at samle den op eller bruge tilpassede handlinger."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Der er ikke mere plads på denne startskærm."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Der er ikke mere plads i bakken Favoritter"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Liste med apps"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste over personlige apps"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste over apps til arbejdet"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Hjem"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Afinstaller"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Appinfo"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Mappen er omdøbt til <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Layout og baggrunde"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Indstillinger for startskærm"</string>
@@ -91,9 +106,9 @@
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"Til"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"Fra"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Kræver adgang til notifikationer"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Hvis du vil se notifikationscirkler, skal du aktivere appnotifikationer for <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="msg_missing_notification_access" msgid="281113995110910548">"Hvis du vil se notifikationsprikker, skal du aktivere appnotifikationer for <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Skift indstillinger"</string>
-    <string name="notification_dots_service_title" msgid="4284221181793592871">"Vis notifikationscirkler"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Vis notifikationsprikker"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Føj appikoner til startskærmen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For nye apps"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Søg"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Denne app er ikke installeret"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen, der hører til dette ikon, er ikke installeret. Du kan fjerne den eller prøve at søge efter appen og installere den manuelt."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeres. <xliff:g id="PROGRESS">%2$s</xliff:g> fuldført"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloades. <xliff:g id="PROGRESS">%2$s</xliff:g> er gennemført"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> venter på at installere"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-widgets"</string>
     <string name="widgets_list" msgid="796804551140113767">"Liste med widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Listen med widgets blev lukket"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Føj til startskærm"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Næste"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Arbejdsprofilen er sat på pause"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Arbejdsapps kan ikke sende dig notifikationer, bruge dit batteri eller få adgang til din placering"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Arbejdsprofilen er sat på pause. Arbejdsapps kan ikke sende dig notifikationer, bruge dit batteri eller få adgang til din placering"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Arbejdsapps kan ikke sende dig notifikationer, bruge dit batteri eller få adgang til din placering"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Arbejdsprofilen er sat på pause. Arbejdsapps kan ikke sende dig notifikationer, bruge dit batteri eller få adgang til din placering"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Arbejdsapps har badges og kan ses af din it-administrator"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Sæt arbejdsapps på pause"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aktivér"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Sæt arbejdsapps og notifikationer på pause"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Mislykket: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index fdd9214..77ddc86 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Heruntergeladene App im abgesicherten Modus deaktiviert"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets im abgesicherten Modus deaktiviert"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Verknüpfung nicht verfügbar"</string>
-    <string name="home_screen" msgid="806512411299847073">"Startbildschirm"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Benutzerdefinierte Aktionen"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Zum Hinzufügen Widget berühren und halten"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Zum Hinzufügen auf Widget doppeltippen und gedrückt halten oder benutzerdefinierte Aktionen verwenden."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Startbildschirm"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Splitscreen"</string>
+    <string name="long_press_widget_to_add" msgid="3587712543577675817">"Tippen und halten, um ein Widget zu bewegen."</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breit und %2$d hoch"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Zum manuellen Hinzufügen gedrückt halten"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Automatisch hinzufügen"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Text aus dem Suchfeld löschen"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Keine Widgets verfügbar"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Keine Suchergebnisse"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Privat"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Geschäftlich"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Unterhaltungen"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Apps finden"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Apps werden geladen…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Benachrichtigungen"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tippen und halten, um eine Verknüpfung auszuwählen."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Doppeltippen und halten, um eine Verknüpfung auszuwählen oder benutzerdefinierte Aktionen zu nutzen."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Tippen und halten, um eine Verknüpfung zu bewegen."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Auf diesem Startbildschirm ist kein Platz mehr vorhanden."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ablage \"Favoriten\" ist voll."</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Liste der Apps"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste der privaten Apps"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste der geschäftlichen Apps"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Startseite"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Entfernen"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstallieren"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"App-Info"</string>
@@ -80,12 +96,11 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Ordner umbenannt in <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> Elemente"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> oder mehr Elemente"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Designs und Hintergründe"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Startbildschirm-Einstellungen"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Drehung des Startbildschirms zulassen"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Drehen des Startbildschirms zulassen"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Beim Drehen des Smartphones"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"App-Benachrichtigungspunkte"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"An"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Suchen"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Diese App ist nicht installiert"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Die App für dieses Symbol ist nicht installiert. Du kannst das Symbol entfernen oder die App lokalisieren und dann manuell installieren."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> wird installiert, <xliff:g id="PROGRESS">%2$s</xliff:g> abgeschlossen"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wird heruntergeladen, <xliff:g id="PROGRESS">%2$s</xliff:g> abgeschlossen"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Warten auf Installation von <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-Widgets"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widgetliste"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widgetliste geschlossen"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Zum Startbildschirm hinzufügen"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Weiter"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Arbeitsprofil pausiert"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Geschäftliche Apps können dir keine Benachrichtigungen senden, deinen Akku nicht nutzen und nicht auf deinen Standort zugreifen"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Das Arbeitsprofil ist pausiert. Geschäftliche Apps können dir keine Benachrichtigungen senden, deinen Akku nicht beanspruchen und nicht auf deinen Standort zugreifen."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Geschäftliche Apps können dir keine Benachrichtigungen senden, deinen Akku nicht nutzen und nicht auf deinen Standort zugreifen"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Das Arbeitsprofil ist pausiert. Geschäftliche Apps können dir keine Benachrichtigungen senden, deinen Akku nicht nutzen und nicht auf deinen Standort zugreifen."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Geschäftliche Apps sind gekennzeichnet und für deinen IT-Administrator sichtbar"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Geschäftliche Apps pausieren"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aktivieren"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Geschäftliche Apps und Benachrichtigungen pausieren"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Fehler: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 5d72b77..c81b96a 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Η λήψη εφαρμογών απενεργοποήθηκε στην Ασφαλή λειτουργία"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Τα γραφικά στοιχεία απενεργοποιήθηκαν στην ασφαλή λειτουργία"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Η συντόμευση δεν είναι διαθέσιμη"</string>
-    <string name="home_screen" msgid="806512411299847073">"Αρχική οθόνη"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Προσαρμοσμένες ενέργειες"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Αγγίξτε παρατεταμένα για να πάρετε ένα γραφ.στοιχ."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Πατήστε δύο φορές παρατεταμένα για επιλογή γραφικού στοιχείου ή χρήση προσαρμοσμένων ενεργειών."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Αρχική οθόνη"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Διαχωρισμός οθόνης"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Πλάτος %1$d επί ύψος %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Αγγίξτε παρατεταμένα για μη αυτόματη τοποθέτηση"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Αυτόματη προσθήκη"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Διαγραφή κειμένου από το πλαίσιο αναζήτησης"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Δεν υπάρχουν διαθέσιμα γραφικά στοιχεία"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Κανένα αποτέλεσμα αναζήτησης"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Προσωπικά"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Εργασίας"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Συζητήσεις"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Αναζήτηση εφαρμογών"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Φόρτωση εφαρμογών…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Δεν βρέθηκαν εφαρμογές αντιστοίχισης για \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Αναζήτηση περισσότερων εφαρμογών"</string>
     <string name="label_application" msgid="8531721983832654978">"Εφαρμογή"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Ειδοποιήσεις"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Αγγίξτε παρατεταμένα για επιλογή συντόμευσης."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Πατήσετε δύο φορές παρατεταμένα για επιλογή συντόμευσης ή χρήση προσαρμοσμένων ενεργειών."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Πατήστε παρατεταμένα για μετακίνηση συντόμευσης."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Πατήστε δύο φορές παρατεταμένα για μετακίνηση συντόμευσης ή χρήση προσαρμοσμένων ενεργειών."</string>
     <string name="out_of_space" msgid="4691004494942118364">"Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Λίστα εφαρμογών"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Λίστα προσωπικών εφαρμογών"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Λίστα εφαρμογών εργασίας"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Αρχική οθόνη"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Κατάργηση"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Απεγκατάσταση"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Πληροφ. εφαρμογής"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Ο φάκελος μετονομάστηκε σε <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> στοιχεία"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ή περισσότερα στοιχεία"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Γραφικά στοιχεία"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Στιλ και ταπετσαρίες"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχ. Οθ."</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Αναζήτηση"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Αυτή η εφαρμογή δεν είναι εγκατεστημένη"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Η εφαρμογή γι\' αυτό το εικονίδιο δεν είναι εγκατεστημένη. Μπορείτε να το καταργήσετε ή να αναζητήσετε την εφαρμογή και να την εγκαταστήσετε με μη αυτόματο τρόπο."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Έχει ολοκληρωθεί το <xliff:g id="PROGRESS">%2$s</xliff:g> της εγκατάστασης της εφαρμογής <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Λήψη <xliff:g id="NAME">%1$s</xliff:g>, ολοκληρώθηκε <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> σε αναμονή για εγκατάσταση"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Γραφικά στοιχεία <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Λίστα γραφικών στοιχείων"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Η λίστα γραφικών στοιχείων έκλεισε"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Προσθήκη στην αρχική οθόνη"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Επόμενο"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Το κατάλαβα"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Το προφίλ εργασίας έχει τεθεί σε παύση"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Οι εφαρμογές εργασίας δεν μπορούν να σας στέλνουν ειδοποιήσεις, να χρησιμοποιούν την μπαταρία ή να έχουν πρόσβαση στην τοποθεσία σας."</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Το προφίλ εργασίας έχει τεθεί σε παύση. Οι εφαρμογές εργασιών δεν μπορούν να σας στέλνουν ειδοποιήσεις, να χρησιμοποιούν την μπαταρία ή να έχουν πρόσβαση στην τοποθεσία σας."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Οι εφαρμογές εργασιών δεν έχουν τη δυνατότητα αποστολής ειδοποιήσεων, χρήσης της μπαταρίας ή πρόσβασης στην τοποθεσία σας."</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Το προφίλ εργασίας έχει τεθεί σε παύση. Οι εφαρμογές εργασιών δεν έχουν τη δυνατότητα αποστολής ειδοποιήσεων, χρήσης της μπαταρίας ή πρόσβασης στην τοποθεσία σας."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Οι εφαρμογές εργασιών φέρουν σήμα και είναι ορατές στον διαχειριστή IT σας"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Το κατάλαβα"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Παύση εφαρμογών εργασιών"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Ενεργοποίηση"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Φίλτρο"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Παύση εφαρμογών εργασίας και ειδοποιήσεων"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Αποτυχία: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 3c54e5a..f26f284 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
-    <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Customised actions"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Home"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch and hold to place manually"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Clear text from search box"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"No widgets available"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"No search results"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifications"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Touch &amp; hold to pick up a shortcut."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Double-tap &amp; hold to pick up a shortcut or use custom actions."</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>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Work apps list"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles &amp; wallpapers"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgets"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery or access your location"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Work apps can’t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Work profile is paused. Work apps can’t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Work apps are badged and visible to your IT admin"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Turn on"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 3c54e5a..f26f284 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
-    <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Customised actions"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Home"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch and hold to place manually"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Clear text from search box"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"No widgets available"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"No search results"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifications"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Touch &amp; hold to pick up a shortcut."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Double-tap &amp; hold to pick up a shortcut or use custom actions."</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>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Work apps list"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles &amp; wallpapers"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgets"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery or access your location"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Work apps can’t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Work profile is paused. Work apps can’t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Work apps are badged and visible to your IT admin"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Turn on"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 3c54e5a..f26f284 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
-    <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Customised actions"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Home"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch and hold to place manually"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Clear text from search box"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"No widgets available"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"No search results"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifications"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Touch &amp; hold to pick up a shortcut."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Double-tap &amp; hold to pick up a shortcut or use custom actions."</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>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Work apps list"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles &amp; wallpapers"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgets"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery or access your location"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Work apps can’t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Work profile is paused. Work apps can’t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Work apps are badged and visible to your IT admin"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Turn on"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 3c54e5a..f26f284 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
-    <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Customised actions"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Home"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch and hold to place manually"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Clear text from search box"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"No widgets available"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"No search results"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifications"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Touch &amp; hold to pick up a shortcut."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Double-tap &amp; hold to pick up a shortcut or use custom actions."</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>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Work apps list"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles &amp; wallpapers"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgets"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery or access your location"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Work apps can’t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Work profile is paused. Work apps can’t send you notifications, use your battery or access your location"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Work apps are badged and visible to your IT admin"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pause work apps"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Turn on"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 2e85204..b700502 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎Downloaded app disabled in Safe mode‎‏‎‎‏‎"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎Widgets disabled in Safe mode‎‏‎‎‏‎"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎Shortcut isn\'t available‎‏‎‎‏‎"</string>
-    <string name="home_screen" msgid="806512411299847073">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎Home screen‎‏‎‎‏‎"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎Custom actions‎‏‎‎‏‎"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‏‎Touch &amp; hold to pick up a widget.‎‏‎‎‏‎"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎Double-tap &amp; hold to pick up a widget or use custom actions.‎‏‎‎‏‎"</string>
+    <string name="home_screen" msgid="5629429142036709174">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎Home‎‏‎‎‏‎"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎Split screen‎‏‎‎‏‎"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎%1$d wide by %2$d high‎‏‎‎‏‎"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‏‎Touch &amp; hold to place manually‎‏‎‎‏‎"</string>
     <string name="place_automatically" msgid="8064208734425456485">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎Add automatically‎‏‎‎‏‎"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎Clear text from search box‎‏‎‎‏‎"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎No widgets available‎‏‎‎‏‎"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‎‎No search results‎‏‎‎‏‎"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎Personal‎‏‎‎‏‎"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎Work‎‏‎‎‏‎"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎Conversations‎‏‎‎‏‎"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‎‎Search apps‎‏‎‎‏‎"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎Loading apps…‎‏‎‎‏‎"</string>
     <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="notifications_header" msgid="1404149926117359025">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎Notifications‎‏‎‎‏‎"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎Touch &amp; hold to pick up a shortcut.‎‏‎‎‏‎"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎Double-tap &amp; hold to pick up a shortcut or use custom actions.‎‏‎‎‏‎"</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>
     <string name="out_of_space" msgid="4691004494942118364">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎No more room on this Home screen.‎‏‎‎‏‎"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎No more room in the Favorites tray‎‏‎‎‏‎"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎Apps list‎‏‎‎‏‎"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‎Personal apps list‎‏‎‎‏‎"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎Work apps list‎‏‎‎‏‎"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‏‏‎Home‎‏‎‎‏‎"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎Remove‎‏‎‎‏‎"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎Uninstall‎‏‎‎‏‎"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‏‎App info‎‏‎‎‏‎"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎Folder renamed to ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‎Folder: ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="SIZE">%2$d</xliff:g>‎‏‎‎‏‏‏‎ items‎‏‎‎‏‎"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎Folder: ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="SIZE">%2$d</xliff:g>‎‏‎‎‏‏‏‎ or more items‎‏‎‎‏‎"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎Widgets‎‏‎‎‏‎"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎Wallpapers‎‏‎‎‏‎"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎Styles &amp; wallpapers‎‏‎‎‏‎"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎Home settings‎‏‎‎‏‎"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎Search‎‏‎‎‏‎"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎This app is not installed‎‏‎‎‏‎"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‎The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually.‎‏‎‎‏‎"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ installing, ‎‏‎‎‏‏‎<xliff:g id="PROGRESS">%2$s</xliff:g>‎‏‎‎‏‏‏‎ complete‎‏‎‎‏‎"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ downloading, ‎‏‎‎‏‏‎<xliff:g id="PROGRESS">%2$s</xliff:g>‎‏‎‎‏‏‏‎ complete‎‏‎‎‏‎"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ waiting to install‎‏‎‎‏‎"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ widgets‎‏‎‎‏‎"</string>
     <string name="widgets_list" msgid="796804551140113767">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‎Widgets list‎‏‎‎‏‎"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‏‎‏‎‎‏‎‎Widgets list closed‎‏‎‎‏‎"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎Add to Home screen‎‏‎‎‏‎"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎Next‎‏‎‎‏‎"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎Got it‎‏‎‎‏‎"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎Work profile is paused‎‏‎‎‏‎"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‎‎Work apps can\'t send you notifications, use your battery, or access your location‎‏‎‎‏‎"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎Work profile is paused. Work apps cant send you notifications, use your battery, or access your location‎‏‎‎‏‎"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‎Work apps can’t send you notifications, use your battery, or access your location‎‏‎‎‏‎"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎Work profile is paused. Work apps can’t send you notifications, use your battery, or access your location‎‏‎‎‏‎"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎Work apps are badged and visible to your IT admin‎‏‎‎‏‎"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎Got it‎‏‎‎‏‎"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎Pause work apps‎‏‎‎‏‎"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‎‏‎‎‎‎‎Turn on‎‏‎‎‏‎"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎Filter‎‏‎‎‏‎"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎‎‎‎Pause work apps and notifications‎‏‎‎‏‎"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎Failed: ‎‏‎‎‏‏‎<xliff:g id="WHAT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index e21c279..acd4a4f 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicación descargada inhabilitada en modo seguro"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets inhabilitados en modo seguro"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"El acceso directo no está disponible"</string>
-    <string name="home_screen" msgid="806512411299847073">"Pantalla principal"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Acciones personalizadas"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén presionado el widget que desees elegir."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Presiona dos veces y mantén presionado para elegir un widget o usa una acción personalizada."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Pantalla principal"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
+    <string name="long_press_widget_to_add" msgid="3587712543577675817">"Presiona y 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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén presionado para ubicarlo manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Agregar automáticamente"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Borra el texto del cuadro de búsqueda"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"No hay widgets disponibles"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"La búsqueda no arrojó resultados"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabajo"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversaciones"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar apps"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Cargando apps…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Mantén presionado para elegir un acceso directo."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Presiona dos veces y mantén presionado para elegir un acceso directo o usar acciones personalizadas."</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>
     <string name="out_of_space" msgid="4691004494942118364">"No hay más espacio en esta pantalla principal."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está llena."</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de apps"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de apps personales"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de apps del trabajo"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Pantalla principal"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Información de app"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"El nombre de la carpeta se cambió a <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o más elementos"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos y fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Configuración de pantalla principal"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación no está instalada"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación para este ícono no está instalada. Puedes eliminar el ícono o buscar la aplicación e instarla manualmente."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Se está instalando <xliff:g id="NAME">%1$s</xliff:g>; <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Se completó el <xliff:g id="PROGRESS">%2$s</xliff:g> de la descarga de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Instalación de <xliff:g id="NAME">%1$s</xliff:g> en espera"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista de widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Se cerró la lista de widgets"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Agregar a la pantalla principal"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Siguiente"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"El perfil de trabajo está en pausa"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Las apps de trabajo no pueden enviarte notificaciones, usar la batería ni acceder a tu ubicación"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"El perfil de trabajo está en pausa. Las apps de trabajo no pueden enviarte notificaciones, usar la batería ni acceder a tu ubicación"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Las apps de trabajo no pueden enviarte notificaciones, usar la batería ni acceder a tu ubicación"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"El perfil de trabajo está en pausa. Las apps de trabajo no pueden enviarte notificaciones, usar la batería ni acceder a tu ubicación"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Las apps de trabajo tienen una insignia y el administrador de IT las puede ver"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Entendido"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Detener apps de trabajo"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Activar"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pon en pausa las apps de trabajo y las notificaciones"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index d73247b..8b9ab0c 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicación descargada inhabilitada en modo seguro"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets inhabilitados en modo seguro"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Acceso directo no disponible"</string>
-    <string name="home_screen" msgid="806512411299847073">"Pantalla de inicio"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Acciones personalizadas"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén pulsado el widget que quieras seleccionar."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dos veces y mantén pulsado el widget que quieras seleccionar o utiliza acciones personalizadas."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Inicio"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantenlo pulsado para añadirlo manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Añadir automáticamente"</string>
+    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
+      <item quantity="other">Widgets: <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
+      <item quantity="one">Widget: <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g></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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Borrar texto del cuadro de búsqueda"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"No hay widgets disponibles"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"No hay resultados de búsqueda"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabajo"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversaciones"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar aplicaciones"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Cargando aplicaciones…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Mantén pulsado para elegir un acceso directo."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Toca dos veces y mantén pulsado para elegir un acceso directo o utilizar acciones personalizadas."</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>
     <string name="out_of_space" msgid="4691004494942118364">"No queda espacio en la pantalla de inicio."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está completa"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicaciones"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicaciones personales"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicaciones del trabajo"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Información de la aplicación"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Se ha cambiado el nombre de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SIZE">%2$d</xliff:g> elementos)"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SIZE">%2$d</xliff:g> o más elementos)"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos y fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ajustes de la pantalla de inicio"</string>
@@ -95,15 +110,15 @@
     <string name="title_change_settings" msgid="1376365968844349552">"Cambiar ajustes"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar puntos de notificación"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Añadir aplicaciones a la pantalla de inicio"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para aplicaciones nuevas"</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Añade el icono de una aplicación nueva instalada a la pantalla de inicio"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Quitar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación no está instalada"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación de este icono no está instalada. Puedes quitar el icono o buscar la aplicación e instalarla manualmente."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista de widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgets cerrada"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Añadir a la pantalla de inicio"</string>
@@ -139,10 +154,15 @@
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Los datos personales están separados y ocultos de las aplicaciones de trabajo"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Tu administrador de TI puede ver tus aplicaciones y datos de trabajo"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Siguiente"</string>
-    <string name="work_profile_edu_accept" msgid="6069788082535149071">"Listo"</string>
+    <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"El perfil de trabajo está en pausa"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Las aplicaciones de trabajo no pueden enviarte notificaciones, gastar batería ni acceder a tu ubicación"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"El perfil de trabajo está en pausa. Las aplicaciones de trabajo no pueden enviarte notificaciones, consumir tu batería ni acceder a tu ubicación"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Las aplicaciones de trabajo no pueden enviarte notificaciones, consumir tu batería ni acceder a tu ubicación"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"El perfil de trabajo está en pausa. Las aplicaciones de trabajo no pueden enviarte notificaciones, consumir tu batería ni acceder a tu ubicación"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Las aplicaciones de trabajo tienen una insignia y tu administrador de TI las puede ver"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Entendido"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausar aplicaciones de trabajo"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Activar"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pausa apps y notificaciones de trabajo"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Se ha producido un error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index e97fc6f..08714ad 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Allalaetud rakendus on turvarežiimis keelatud"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Turvarežiimis on vidinad keelatud"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Otsetee pole saadaval"</string>
-    <string name="home_screen" msgid="806512411299847073">"Avakuva"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Kohandatud toimingud"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidina valimiseks vajutage ja hoidke seda all."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Topeltpuudutage ja hoidke vidina valimiseks või kohandatud toimingute kasutamiseks."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Avakuva"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Jagatud ekraanikuva"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lai ja %2$d kõrge"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Puudutage pikalt, et käsitsi asetada"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Lisa automaatselt"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Kustuta otsingukastis olev tekst"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Ühtki vidinat pole saadaval"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Otsingutulemused puuduvad"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Isiklik"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Töö"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Vestlused"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Otsige rakendusi"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Rakenduste laadimine …"</string>
     <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="notifications_header" msgid="1404149926117359025">"Märguanded"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Otsetee valimiseks puudutage seda pikalt."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Topeltpuudutage ja hoidke otsetee valimiseks või kohandatud toimingute kasutamiseks."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Sellel avaekraanil pole enam ruumi."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Salves Lemmikud pole rohkem ruumi"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Rakenduste loend"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Isiklike rakenduste loend"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Töörakenduste loend"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Avakuva"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Eemalda"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduste teave"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Kausta uus nimi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> üksust"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> või rohkem üksust"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiilid ja taustapildid"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Avakuva seaded"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Otsing"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"See rakendus ei ole installitud"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Selle ikooni rakendust pole installitud. Saate selle eemaldada või rakendust otsida ja käsitsi installida."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Üksust <xliff:g id="NAME">%1$s</xliff:g> installitakse, <xliff:g id="PROGRESS">%2$s</xliff:g> on valmis"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Rakenduse <xliff:g id="NAME">%1$s</xliff:g> allalaadimine, <xliff:g id="PROGRESS">%2$s</xliff:g> on valmis"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> on installimise ootel"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Teenuse <xliff:g id="NAME">%1$s</xliff:g> vidinad"</string>
     <string name="widgets_list" msgid="796804551140113767">"Vidinate loend"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Vidinate loend on suletud"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Lisa avakuvasse"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Järgmine"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Selge"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Tööprofiil on peatatud"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurde pääseda"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Tööprofiil on peatatud. Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurde pääseda"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurde pääseda"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Tööprofiil on peatatud. Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurde pääseda"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Töörakendustel on märk ja need on teie IT-administraatorile nähtavad"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Selge"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Peata töörakendused"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Lülita sisse"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Peatage töörakendused ja märguanded"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nurjus: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 18cc766..a485a14 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -26,31 +26,47 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Deskargatutako aplikazioa modu seguruan desgaitu da"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgetak desgaitu egin dira modu seguruan"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Lasterbideak ez daude erabilgarri"</string>
-    <string name="home_screen" msgid="806512411299847073">"Hasierako pantaila"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Ekintza pertsonalizatuak"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Eduki sakatuta widgeta aukeratzeko."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Sakatu birritan eta eduki sakatuta widgeta aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Hasierako pantaila"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Zatitu pantaila"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d zabal eta %2$d luze"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Eduki sakatuta eskuz gehitzeko"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Gehitu automatikoki"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Garbitu bilaketa-koadroko testua"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Ez dago widget erabilgarririk"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Ez dago bilaketa-emaitzarik"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pertsonalak"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Lanekoak"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Elkarrizketak"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Bilatu aplikazioetan"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Aplikazioak kargatzen…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Jakinarazpenak"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Eduki sakatuta lasterbide bat aukeratzeko."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Sakatu birritan eta eduki sakatuta lasterbide bat aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Hasierako pantaila honetan ez dago toki gehiago."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ez dago toki gehiago Gogokoak erretiluan"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Aplikazioen zerrenda"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Aplikazio pertsonalen zerrenda"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Laneko aplikazioen zerrenda"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Hasiera"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Kendu"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalatu"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren datuak"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren informazioa"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instalatu"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ez iradoki aplikazioa"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ainguratu iragarpena"</string>
@@ -73,14 +89,13 @@
     <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>
-    <string name="folder_opened" msgid="94695026776264709">"Karpeta ireki da: <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+    <string name="folder_opened" msgid="94695026776264709">"Karpeta ireki da: <xliff:g id="WIDTH">%1$d</xliff:g> × <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Karpeta ixteko, sakatu hau"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Izen berria gordetzeko, sakatu hau"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Karpeta itxi da"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Karpetari <xliff:g id="NAME">%1$s</xliff:g> izena eman zaio"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu)"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu edo gehiago)"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estiloak eta horma-paperak"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Hasierako pantailaren ezarpenak"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Bilatu"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikazio hau ez dago instalatuta"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ikono honen aplikazioa ez dago instalatuta. Ikonoa ken dezakezu, edo aplikazioa bilatu eta eskuz instalatu."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> instalatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> deskargatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> instalatzeko zain"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgetak"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widget-zerrenda"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Itxi da widget-zerrenda"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Gehitu hasierako pantailan"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Hurrengoa"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ados"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Laneko profila pausatuta dago"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Laneko aplikazioek ezin dute jakinarazpenik bidali, bateria erabili edo kokapena atzitu"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Laneko profila pausatuta dago. Laneko aplikazioek ezin dute jakinarazpenik bidali, bateria erabili edo kokapena atzitu."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Laneko aplikazioek ezin dute jakinarazpenik bidali, bateria erabili edo kokapena atzitu"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Laneko profila pausatuta dago. Laneko aplikazioek ezin dute jakinarazpenik bidali, bateria erabili edo kokapena atzitu."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Laneko aplikazioek bereizgarriak dituzte, eta IKT saileko administratzaileak ikus ditzake"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ados"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausatu laneko aplikazioak"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aktibatu"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Iragazi"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pausatu laneko aplikazioak eta jakinarazpenak"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Huts egin du: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 1597043..0b3e7b0 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -26,34 +26,50 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"برنامه بارگیری شده در حالت ایمن غیرفعال شد"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"ابزارک‌ها در حالت ایمن غیرفعال هستند"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"میان‌بر دردسترس نیست"</string>
-    <string name="home_screen" msgid="806512411299847073">"صفحه اصلی"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"کنش‌های سفارشی"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"برای انتخاب ابزارک لمس کنید و نگه دارید."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"برای انتخاب یک ابزارک، دو ضربه سریع بزنید و نگه‌دارید یا از کنش‌های سفارشی استفاده کنید."</string>
+    <string name="home_screen" msgid="5629429142036709174">"صفحه اصلی"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"صفحهٔ دونیمه"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏%1$d عرض در %2$d طول"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"آن را لمس کنید و بکشید تا به‌صورت دستی اضافه شود"</string>
     <string name="place_automatically" msgid="8064208734425456485">"افزودن خودکار"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"پاک کردن نوشتار از چارگوش جستجو"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ابزارکی دردسترس نیست"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"بدون نتیجه جستجو"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"شخصی"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"کار"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"مکالمه‌ها"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"جستجوی برنامه‌ها"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"درحال بارگیری برنامه‌‌ها…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"هیچ برنامه‌ای در مطابقت با «<xliff:g id="QUERY">%1$s</xliff:g>» پیدا نشد"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"جستجوی برنامه‌های بیشتر"</string>
     <string name="label_application" msgid="8531721983832654978">"برنامه"</string>
     <string name="notifications_header" msgid="1404149926117359025">"اعلان‌ها"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"برای انتخاب میان‌بر، لمس کنید و نگه‌دارید."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"برای انتخاب میان‌بر، دو ضربه سریع بزنید و نگه‌دارید یا از کنش‌های سفارشی استفاده کنید."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"برای جابه‌جا کردن میان‌بر، لمس کنید و نگه دارید."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"برای جابه‌جا کردن میان‌بر یا استفاده از کنش‌های سفارشی، دوضربه بزنید و نگه دارید."</string>
     <string name="out_of_space" msgid="4691004494942118364">"فضای بیشتری در این صفحه اصلی موجود نیست."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"فضای بیشتری در سینی موارد دلخواه وجود ندارد"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"فهرست برنامه‌ها"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"فهرست برنامه‌های شخصی"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"فهرست برنامه‌های کاری"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"صفحه اصلی"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"برداشتن"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"حذف نصب"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"اطلاعات برنامه"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"نصب"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"برنامه پیشنهاد داده نشود"</string>
-    <string name="pin_prediction" msgid="4196423321649756498">"پین کردن پیشنهاد"</string>
+    <string name="pin_prediction" msgid="4196423321649756498">"سنجاق کردن پیشنهاد"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"نصب میان‌برها"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر اضافه کند."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"خواندن تنظیمات و میان‌برهای صفحه اصلی"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"نام پوشه به <xliff:g id="NAME">%1$s</xliff:g> تغییر کرد"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد یا بیشتر"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ابزارک‌ها"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواری‌ها"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"سبک و کاغذدیواری"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"تنظیمات صفحه اصلی"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"جستجو"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"این برنامه نصب نشده است."</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"برنامه برای این نماد نصب نشده است. می‌توانید آن را حذف کنید یا سعی کنید برنامه را جستجو کنید و آن را به صورت دستی نصب کنید."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> درحال نصب است، <xliff:g id="PROGRESS">%2$s</xliff:g> تکمیل شده است"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"درحال بارگیری <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="PROGRESS">%2$s</xliff:g> کامل شد"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> درانتظار نصب"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"ابزارک‌های <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"فهرست ابزارک‌ها"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"فهرست ابزارک‌ها بسته شد"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"افزودن به صفحه اصلی"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"بعدی"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"متوجه‌ام"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"نمایه کاری موقتاً متوقف شده است"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"برنامه‌های کاری نمی‌توانند اعلان ارسال کنند، از باتری استفاده کنند، یا به مکانتان دسترسی داشته باشند"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"نمایه کاری موقتاً متوقف شده است. برنامه‌های کاری نمی‌توانند به شما اعلان ارسال کنند، از باتری استفاده کنند، یا به مکانتان دسترسی داشته باشند"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"برنامه‌های کاری نمی‌توانند برای شما اعلان ارسال کنند، از باتری استفاده کنند، یا به مکانتان دسترسی داشته باشند"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"نمایه کاری موقتاً متوقف شده است. برنامه‌های کاری نمی‌توانند برای شما اعلان ارسال کنند، از باتری استفاده کنند، یا به مکانتان دسترسی داشته باشند"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"برنامه‌های کاری دارای نشان هستند و سرپرست سیستم می‌تواند آن‌ها را ببیند."</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"متوجه‌ام"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"توقف موقت برنامه‌های کاری"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"روشن کردن"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"فیلتر"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"توقف موقت برنامه‌های کاری و اعلان‌ها"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ناموفق بود: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 9fea032..34ed9e0 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ladattu sovellus poistettiin käytöstä suojatussa tilassa"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgetit poistettu käytöstä vikasietotilassa"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Pikakuvake ei ole käytettävissä."</string>
-    <string name="home_screen" msgid="806512411299847073">"Aloitusnäyttö"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Muokatut toiminnot"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Valitse widget painamalla sitä pitkään."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Valitse widget tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla kohdetta pitkään."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Etusivu"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Jaettu näyttö"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Leveys: %1$d, korkeus: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Sijoita manuaalisesti koskettamalla pitkään"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Lisää automaattisesti"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Tyhjennä teksti hakukentästä"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Ei widgetejä käytettävissä"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Ei hakutuloksia"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Henkilökohtainen"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Työ"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Keskustelut"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hae sovelluksia"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Ladataan sovelluksia…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Ilmoitukset"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Valitse pikakuvake painamalla sitä pitkään."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Valitse pikakuvake tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla pitkään."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Tässä aloitusruudussa ei ole enää tilaa."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Suosikit-valikossa ei ole enää tilaa"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Sovellusluettelo"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Omat sovellukset ‑luettelo"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Työsovellusluettelo"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Aloitusruutu"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Poista"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Poista asennus"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Sovelluksen tiedot"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Kansion nimeksi vaihdettiin <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, ainakin <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgetit"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Tyylit ja taustakuvat"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Aloitusnäyttö"</string>
@@ -88,7 +103,7 @@
     <string name="allow_rotation_title" msgid="7728578836261442095">"Salli aloitusnäytön kiertäminen"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kun puhelinta kierretään"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"Pistemerkit"</string>
-    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Käytössä"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Päällä"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"Ei päällä"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Ilmoituksien käyttöoikeus tarvitaan"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"<xliff:g id="NAME">%1$s</xliff:g> tarvitsee ilmoitusten käyttöoikeuden, jotta pistemerkkejä voidaan näyttää."</string>
@@ -101,15 +116,15 @@
     <string name="abandoned_search" msgid="891119232568284442">"Haku"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Sovellusta ei ole asennettu"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Kuvakkeen sovellusta ei ole asennettu. Voit poistaa kuvakkeen tai etsiä sovelluksen ja asentaa sen manuaalisesti."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> asennetaan, <xliff:g id="PROGRESS">%2$s</xliff:g> valmis"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> latautuu, valmiina <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> odottaa asennusta"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgetit: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widget-luettelo"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widget-luettelo suljettu"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Lisää aloitusnäytölle"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Siirrä kohde tänne"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Kohde lisättiin aloitusnäytölle."</string>
-    <string name="item_removed" msgid="851119963877842327">"Kohde poistettiin."</string>
+    <string name="item_removed" msgid="851119963877842327">"Kohde poistettiin"</string>
     <string name="undo" msgid="4151576204245173321">"Kumoa"</string>
     <string name="action_move" msgid="4339390619886385032">"Siirrä kohde"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Siirrä rivin <xliff:g id="NUMBER_0">%1$s</xliff:g> sarakkeeseen <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Seuraava"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Selvä"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Työprofiilin käyttö on keskeytetty"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Työsovellukset eivät voi lähettää sinulle ilmoituksia eivätkä käyttää akkuasi tai sijaintiasi"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Työprofiilin käyttö on keskeytetty. Työsovellukset eivät voi lähettää sinulle ilmoituksia eivätkä käyttää akkuasi tai paikantaa sijaintiasi"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Työsovellukset eivät voi lähettää sinulle ilmoituksia eivätkä käyttää akkuasi tai paikantaa sijaintiasi"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Työprofiilin käyttö on keskeytetty. Työsovellukset eivät voi lähettää sinulle ilmoituksia eivätkä käyttää akkuasi tai paikantaa sijaintiasi"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Työsovellukset on merkitty ja ne näkyvät IT-järjestelmänvalvojalle"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Keskeytä työsovellusten käyttö"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Laita päälle"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Suodatin"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Keskeytä työsovellukset ja ‑ilmoitukset"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Epäonnistui: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index aaffbd1..34fcb36 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -26,31 +26,47 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sans échec."</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets désactivés en mode sans échec"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Le raccourci n\'est pas disponible"</string>
-    <string name="home_screen" msgid="806512411299847073">"Écran d\'accueil"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Actions personnalisées"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Maintenez un doigt sur le widget pour l\'ajouter."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Touchez 2x un widget et maintenez doigt dessus pour l’ajouter ou utiliser des actions personnalisées"</string>
+    <string name="home_screen" msgid="5629429142036709174">"Accueil"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran partagé"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur sur %2$d de hauteur"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Maintenez le doigt sur l\'élément pour le placer manuellement"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Ajouter automatiquement"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Effacer le texte dans le champ de recherche"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Aucun widget n\'est installé"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Aucun résultat de recherche"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personnels"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Professionnels"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Rechercher dans les applications"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Chargement des applications en cours…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifications"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Maintenez un doigt sur le raccourci pour l\'ajouter"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Touchez 2x un raccourci et maintenez doigt dessus pour l’aj. ou utiliser des actions personnalisées."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur l\'écran d\'accueil."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Il n\'y a plus d\'espace dans la zone des favoris"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Liste des applications"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste des applications personnelles"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste des applications professionnelles"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Accueil"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Détails sur l\'appli"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Détails de l\'appli"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'application"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> élément(s)"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles et fonds d\'écran"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Cette application n\'est pas installée"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'application liée à cette icône n\'est pas installée. Vous pouvez la supprimer ou rechercher l\'application et l\'installer manuellement."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Installation de l\'application <xliff:g id="NAME">%1$s</xliff:g> en cours, <xliff:g id="PROGRESS">%2$s</xliff:g> terminée"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Téléchargement de <xliff:g id="NAME">%1$s</xliff:g> : <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> en attente d\'installation"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets pour <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Liste des widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Liste des widgets fermée"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Ajouter à l\'écran d\'accueil"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Suivant"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Le profil professionnel est interrompu"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Les applications professionnelles ne peuvent pas vous envoyer de notifications, utiliser la pile ni accéder à votre position"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Le profil professionnel est interrompu. Les applications professionnelles ne peuvent pas vous envoyer de notifications, utiliser la pile ni accéder à votre position"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Les applications professionnelles ne peuvent ni vous envoyer de notifications, ni utiliser la pile, ni accéder à votre position"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Le profil professionnel est interrompu. Les applications professionnelles ne peuvent ni vous envoyer de notifications, ni utiliser la pile, ni accéder à votre position"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Les applications professionnelles sont indiquées par un badge et sont visibles pour votre administrateur informatique"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Mettre en pause les applications professionnelles"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Activer"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrer"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Interrompre les applications et les notifications professionnelles"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 2628cdb..f336031 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -20,34 +20,50 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Travail"</string>
+    <string name="work_folder_name" msgid="3753320833950115786">"Pro"</string>
     <string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sécurisé."</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Les widgets sont désactivés en mode sécurisé."</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Raccourci non disponible"</string>
-    <string name="home_screen" msgid="806512411299847073">"Écran d\'accueil"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Actions personnalisées"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"App. de manière prolongée pour sélectionner widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Appuyez 2 fois et maintenez la pression pour sélectionner widget ou utilisez actions personnalisées."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Accueil"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran partagé"</string>
+    <string name="long_press_widget_to_add" msgid="3587712543577675817">"Appuyez de manière prolongée pour déplacer widget."</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur et %2$d de hauteur"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Appuyez de manière prolongée pour placer l\'élément manuellement."</string>
     <string name="place_automatically" msgid="8064208734425456485">"Ajouter automatiquement"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Effacer le texte du champ de recherche"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Aucun widget disponible"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Aucun résultat de recherche"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Widgets personnels"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Widgets professionnels"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Rechercher dans les applications"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Chargement des applications…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifications"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Appui prolongé pour sélectionner un raccourci."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Appuyez 2X et maintenez la pression pour choisir un raccourci ou utilisez les actions personnalisées"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Appuyez de manière prolongée pour déplacer 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>
     <string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur cet écran d\'accueil."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Plus d\'espace disponible dans la zone de favoris."</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Liste d\'applications"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste des applications personnelles"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste des applications professionnelles"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Accueil"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Infos sur l\'appli"</string>
@@ -80,10 +96,9 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles et fonds d\'écran"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres de l\'accueil"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Cette application n\'est pas installée"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'application correspondant à cette icône n\'est pas installée. Vous pouvez supprimer cette dernière, ou essayer de rechercher l\'application et de l\'installer manuellement."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Installation de <xliff:g id="NAME">%1$s</xliff:g>… (<xliff:g id="PROGRESS">%2$s</xliff:g> terminés)"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> en cours de téléchargement, <xliff:g id="PROGRESS">%2$s</xliff:g> effectué(s)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> en attente d\'installation"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Liste des widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"La liste des widgets est fermée"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Ajouter à l\'écran d\'accueil"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Suivant"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Profil professionnel en pause"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Les applications professionnelles ne peuvent pas vous envoyer de notifications, utiliser votre batterie ni accéder à votre position"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profil professionnel en pause. Les applications professionnelles ne peuvent pas vous envoyer de notifications, utiliser votre batterie ni accéder à votre localisation"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Les applis professionnelles ne peuvent pas vous envoyer de notifications, utiliser votre batterie ni accéder à votre position"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Profil professionnel en pause. Les applis professionnelles ne peuvent pas vous envoyer de notifications, utiliser votre batterie ni accéder à votre position"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Les applis professionnelles sont identifiées par un badge et votre administrateur informatique peut les voir"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Mettre en pause les applis professionnelles"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Activer"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtre"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Mettre en pause vos applications et notifications professionnelles"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 9fdf22b..382f6ff 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -26,29 +26,45 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"A aplicación que descargaches está desactivada no modo seguro"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Os widgets están desactivados no modo seguro"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"O atallo non está dispoñible"</string>
-    <string name="home_screen" msgid="806512411299847073">"Pantalla de inicio"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Accións personalizadas"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premido un widget para seleccionalo."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dúas veces e mantén premido para seleccionar un widget ou utiliza accións personalizadas."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Inicio"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largo por %2$d de alto"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén premido o elemento para colocalo manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Engadir automaticamente"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Borrar texto da caixa de busca"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Non hai widgets dispoñibles"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Non hai resultados da busca"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Widgets persoais"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Widgets do traballo"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Buscar aplicacións"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Cargando aplicacións…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notificacións"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Mantén premido un atallo para seleccionalo."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Toca dúas veces e mantén premido para seleccionar un atallo ou utiliza accións personalizadas."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Non hai máis espazo nesta pantalla de inicio."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Non hai máis espazo na bandexa de favoritos"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicacións"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicacións persoais"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicacións de traballo"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
-    <string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminar"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicación"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"O cartafol cambiou o nome a <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos ou máis"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos/fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Axustes de Inicio"</string>
@@ -93,23 +108,23 @@
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Necesítase acceso ás notificacións"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Para que se mostren os puntos de notificacións, activa as notificacións da aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Cambiar configuración"</string>
-    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar puntos de notificacións"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostra puntos de notificacións"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Engadir iconas de aplicacións á pantalla de inicio"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas aplicacións"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"Quitar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación non está instalada"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A aplicación para esta icona non está instalada. Podes eliminala ou buscar a aplicación e instalala manualmente."</string>
+    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A aplicación para esta icona non está instalada. Podes quitala ou buscar a aplicación e instalala manualmente."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista de widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Pechouse a lista de widgets"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Engadir á pantalla de inicio"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Engadiuse o elemento á pantalla de inicio"</string>
-    <string name="item_removed" msgid="851119963877842327">"Eliminouse o elemento"</string>
+    <string name="item_removed" msgid="851119963877842327">"Quitouse o elemento"</string>
     <string name="undo" msgid="4151576204245173321">"Desfacer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover á fila <xliff:g id="NUMBER_0">%1$s</xliff:g> columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
@@ -139,10 +154,15 @@
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Os datos persoais sepáranse e ocúltanse das aplicacións do traballo"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"O teu administrador de TI pode ver as aplicacións e os datos do traballo"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Seguinte"</string>
-    <string name="work_profile_edu_accept" msgid="6069788082535149071">"De acordo"</string>
+    <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"O perfil de traballo está en pausa"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"As aplicacións do traballo non poden enviarche notificacións, utilizar a batería nin acceder á túa localización"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"O perfil de traballo está en pausa. As aplicacións do traballo non poden enviarche notificacións, utilizar a batería nin acceder á túa localización"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"As aplicacións do traballo non poden enviarche notificacións, utilizar a batería nin acceder á túa localización"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"O perfil de traballo está en pausa. As aplicacións do traballo non poden enviarche notificacións, utilizar a batería nin acceder á túa localización"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"O administrador de TI pode ver as aplicacións do traballo e engadirlles indicadores"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Entendido"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pór en pausa aplicacións do traballo"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Activar"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pon en pausa as aplicacións e as notificacións do traballo"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Erro: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 01ecf73..c7f8042 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"સુરક્ષિત મોડમાં ડાઉનલોડ કરેલ ઍપ્લિકેશન અક્ષમ કરી"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"સુરક્ષિત મોડમાં વિજેટ્સ અક્ષમ કર્યા"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"શૉર્ટકટ ઉપલબ્ધ નથી"</string>
-    <string name="home_screen" msgid="806512411299847073">"હોમ સ્ક્રીન"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"કસ્ટમ ક્રિયાઓ"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"વિજેટ ચૂંટવા માટે સ્પર્શ કરો અને પકડી રાખો."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"વિજેટ ચૂંટવા અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટેપ કરો અને પકડી રાખો."</string>
+    <string name="home_screen" msgid="5629429142036709174">"હોમ સ્ક્રીન"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"સ્ક્રીનને વિભાજિત કરો"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d પહોળાઈ X %2$d ઊંચાઈ"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"મેન્યુઅલી મૂકવા માટે ટચ કરી દબાવી રાખો"</string>
-    <string name="place_automatically" msgid="8064208734425456485">"આપમેળે ઉમેરો"</string>
+    <string name="place_automatically" msgid="8064208734425456485">"ઑટોમૅટિક રીતે ઉમેરો"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"શોધ બૉક્સમાંથી ટેક્સ્ટ સાફ કરો"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"કોઈ વિજેટ ઉપલબ્ધ નથી"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"કોઈ શોધ પરિણામો નથી"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"વ્યક્તિગત"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ઑફિસ"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"વાતચીતો"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"શોધ ઍપ્લિકેશનો"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ઍપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"વધુ ઍપ્લિકેશનો શોધો"</string>
     <string name="label_application" msgid="8531721983832654978">"ઍપ"</string>
-    <string name="notifications_header" msgid="1404149926117359025">"નોટિફિકેશનો"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"એક શૉર્ટકટ ચૂંટવા માટે સ્પર્શ કરી રાખો."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"એક શૉર્ટકટ ચૂંટવા અથવા કોઈ કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટૅપ કરીને દબાવી રાખો."</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>
     <string name="out_of_space" msgid="4691004494942118364">"આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"મનપસંદ ટ્રે પર વધુ જગ્યા નથી"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"ઍપ્લિકેશનોની સૂચિ"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"વ્યક્તિગત ઍપની સૂચિ"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"કાર્યસ્થળની ઍપની સૂચિ"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"હોમ"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"દૂર કરો"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"અનઇન્સ્ટોલ કરો"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ઍપની માહિતી"</string>
@@ -80,8 +96,7 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ફોલ્ડરનું નામ બદલીને <xliff:g id="NAME">%1$s</xliff:g> કર્યું"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> આઇટમ"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> કે વધુ આઇટમ"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"વિજેટ્સ"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
+    <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"શૈલીઓ અને વૉલપેપર"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"શોધો"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"આ ઍપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"આ આયકન માટેની ઍપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી. તમે તેને દૂર કરી શકો છો અથવા ઍપ્લિકેશન માટે શોધ કરી અને તેને મેન્યુઅલી ઇન્સ્ટોલ કરી શકો છો."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ઇન્સ્ટૉલ કરી રહ્યાં છીએ, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ થયું"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ડાઉનલોડ કરી રહ્યાં છે, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>, ઇન્સ્ટૉલ થવાની રાહ જોઈ રહ્યું છે"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> વિજેટ"</string>
     <string name="widgets_list" msgid="796804551140113767">"વિજેટની સૂચિ"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"વિજેટની સૂચિ બંધ કરવામાં આવી છે"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"હોમ સ્ક્રીન પર ઉમેરો"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"આગળ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"સમજાઈ ગયું"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ઑફિસની પ્રોફાઇલ થોભાવી છે"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"ઑફિસ માટેની ઍપ તમને નોટિફિકેશન મોકલી શકતી નથી, તમારી બૅટરી વાપરી શકતી નથી કે તમારું સ્થાન ઍક્સેસ કરી શકતી નથી"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"કાર્યાલયની પ્રોફાઇલ થોભાવી છે. ઑફિસ માટેની ઍપ તમને નોટિફિકેશન મોકલી શકતી નથી, તમારી બૅટરી વાપરી શકતી નથી કે તમારું સ્થાન ઍક્સેસ કરી શકતી નથી"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"ઑફિસ માટેની ઍપ તમને નોટિફિકેશન મોકલી શકતી નથી, તમારી બૅટરી વાપરી શકતી નથી કે તમારું સ્થાન ઍક્સેસ કરી શકતી નથી"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"ઑફિસની પ્રોફાઇલ થોભાવી છે. ઑફિસ માટેની ઍપ તમને નોટિફિકેશન મોકલી શકતી નથી, તમારી બૅટરી વાપરી શકતી નથી કે તમારું સ્થાન ઍક્સેસ કરી શકતી નથી"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ઑફિસની ઍપને બૅજ આપેલા હોય છે અને તમારા IT વ્યવસ્થાપક તેમને જોઈ શકે છે"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"સમજાઈ ગયું"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ઑફિસની ઍપ થોભાવો"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ચાલુ કરો"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ફિલ્ટર કરો"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ઑફિસ માટેની ઍપ અને નોટિફિકેશન થોભાવો"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"નિષ્ફળ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 0cec9a3..478d87e 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"डाउनलोड किए गए ऐप्लिकेशन सुरक्षित मोड में अक्षम है"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"विजेट सुरक्षित मोड में अक्षम हैं"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"शॉर्टकट उपलब्ध नहीं है"</string>
-    <string name="home_screen" msgid="806512411299847073">"होम स्क्रीन"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"कस्टम कार्रवाइयां"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"विजेट को चुनने के लिए दबाकर रखें"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"कोई विजेट चुनने के लिए दो बार छूएं और दबाये रखें या अपने मुताबिक कार्रवाइयां चुनें."</string>
+    <string name="home_screen" msgid="5629429142036709174">"होम स्क्रीन"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौड़ाई गुणा %2$d ऊंचाई"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"खुद जोड़ने के लिए दबाकर रखें"</string>
-    <string name="place_automatically" msgid="8064208734425456485">"अपने आप जोड़ें"</string>
+    <string name="place_automatically" msgid="8064208734425456485">"अपने-आप जुड़ जाए"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"खोज बॉक्स से टेक्स्ट हटाएं"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"कोई विजेट उपलब्ध नहीं है"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"खोज का कोई नतीजा नहीं मिला"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"निजी विजेट"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ऑफ़िस"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"बातचीत"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ऐप सर्च करें"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ऐप्लिकेशन लोड हो रहे हैं…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" से मिलता-जुलता कोई ऐप्लिकेशन नहीं मिला"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"और ऐप सर्च करें"</string>
     <string name="label_application" msgid="8531721983832654978">"ऐप्लिकेशन"</string>
     <string name="notifications_header" msgid="1404149926117359025">"सूचनाएं"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"शॉर्टकट चुनने के लिए दबाकर रखें."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"शॉर्टकट चुनने या पसंद के मुताबिक कार्रवाई करने के लिए दो बार टैप करें और कुछ देर दबाए रखें."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"किसी शॉर्टकट को एक से दूसरी जगह ले जाने के लिए, उसे दबाकर रखें."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"किसी शॉर्टकट को एक से दूसरी जगह ले जाने के लिए, उस पर दो बार टैप करके दबाकर रखें या पसंद के मुताबिक कार्रवाइयां इस्तेमाल करें."</string>
     <string name="out_of_space" msgid="4691004494942118364">"इस होम स्‍क्रीन पर जगह नहीं बची है"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"पसंदीदा ट्रे में और जगह नहीं है"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"ऐप्लिकेशन सूची"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"निजी ऐप्लिकेशन की सूची"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"काम से जुड़े ऐप्लिकेशन की सूची"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"होम पेज"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"निकालें"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइंस्टॉल करें"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ऐप्लिकेशन की जानकारी"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"फ़ोल्डर का नाम बदलकर <xliff:g id="NAME">%1$s</xliff:g> किया गया"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> आइटम"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> या इससे ज़्यादा आइटम"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"स्टाइल और वॉलपेपर"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"होम पेज की सेटिंग"</string>
@@ -101,15 +116,15 @@
     <string name="abandoned_search" msgid="891119232568284442">"खोजें"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"यह ऐप्लिकेशन इंस्टॉल नहीं है"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"इस आइकॉन का ऐप इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप को खोज कर उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल किया जा रहा है, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरा हो गया"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड हो रहा है, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरी हुई"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> के इंस्टॉल होने की प्रतीक्षा की जा रही है"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेट"</string>
     <string name="widgets_list" msgid="796804551140113767">"विजेट की सूची"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"विजेट की सूची बंद हो गई है"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"होम स्‍क्रीन में जोड़ें"</string>
     <string name="action_move_here" msgid="2170188780612570250">"आइटम यहां ले जाएं"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"होम स्क्रीन में आइटम जोड़ा गया"</string>
-    <string name="item_removed" msgid="851119963877842327">"आइटम निकाला गया"</string>
+    <string name="item_removed" msgid="851119963877842327">"आइटम हटाया गया"</string>
     <string name="undo" msgid="4151576204245173321">"पहले जैसा करें"</string>
     <string name="action_move" msgid="4339390619886385032">"आइटम ले जाएं"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तंभ <xliff:g id="NUMBER_1">%2$s</xliff:g> पर ले जाएं"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"आगे बढ़ें"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ठीक है"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"वर्क प्रोफ़ाइल रोक दी गई है"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन आपको सूचनाएं नहीं भेज सकते. साथ ही, आपकी बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस नहीं कर सकते"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"वर्क प्रोफ़ाइल रोक दी गई है. ऑफ़िस के काम से जुड़े ऐप्लिकेशन आपको सूचनाएं नहीं भेज सकते. साथ ही, आपके डिवाइस की बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस नहीं कर सकते"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन आपको सूचनाएं नहीं भेज सकते. साथ ही, आपकी बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस भी नहीं कर सकते"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन रोक दिए गए हैं. ऑफ़िस के काम से जुड़े ऐप्लिकेशन आपको सूचनाएं नहीं भेज सकते. साथ ही, आपकी बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस भी नहीं कर सकते"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन, बैज किए गए हैं और आईटी एडमिन को दिख रहे हैं"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ठीक है"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन रोकें"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"चालू करें"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"फ़िल्टर"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन और सूचनाएं रोकें"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"पूरा नहीं हुआ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index dd13ef0..8ebc33f 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -26,31 +26,49 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Preuzeta aplikacija onemogućena je u Sigurnom načinu rada"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgeti su onemogućeni u Sigurnom načinu rada"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Prečac nije dostupan"</string>
-    <string name="home_screen" msgid="806512411299847073">"Početni zaslon"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Prilagođene radnje"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite i držite kako biste podigli widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dodirnite dvaput i držite kako biste podigli widget ili pokušajte prilagođenim radnjama."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d širine i %2$d visine"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dodirnite i zadržite stavku da biste je postavili ručno"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Dodaj automatski"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Izbrišite tekst iz okvira za pretraživanje"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nije dostupan nijedan widget"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nema rezultata pretraživanja"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobno"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Razgovori"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pretraži aplikacije"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Učitavanje aplikacija…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Obavijesti"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Dodirnite i zadržite kako biste podigli prečac."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvaput dodirnite i zadržite pritisak kako biste podigli prečac ili pokušajte prilagođenim radnjama."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom zaslonu više nema mjesta."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Favoriti"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Popis aplikacija"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Popis osobnih aplikacija"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Popis radnih aplikacija"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikaciji"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Podaci o aplikaciji"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Prikvači predviđenu apl."</string>
@@ -81,7 +99,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana u <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadine"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Postavke početnog zaslona"</string>
@@ -102,9 +119,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacija nije instalirana"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija ove ikone nije instalirana. Možete je ukloniti ili potražiti aplikaciju i instalirati je ručno."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> dovršeno"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Preuzimanje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, dovršeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Čekanje na instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgeti"</string>
     <string name="widgets_list" msgid="796804551140113767">"Popis widgeta"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Popis widgeta zatvoren"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodavanje na početni zaslon"</string>
@@ -136,14 +153,19 @@
     <string name="notification_dismissed" msgid="6002233469409822874">"Obavijest je odbačena"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobno"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Poslovni profil"</string>
     <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osobni podaci odvojeni su i skriveni od poslovnih aplikacija"</string>
     <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Podaci poslovnih aplikacija vidljivi su vašem IT administratoru"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Dalje"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Shvaćam"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Poslovni profil je pauziran"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Poslovne aplikacije ne mogu vam slati obavijesti, trošiti bateriju ili pristupiti vašoj lokaciji"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Poslovni profil je pauziran. Poslovne aplikacije ne mogu vam slati obavijesti, trošiti bateriju ili pristupiti vašoj lokaciji"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Poslovne aplikacije ne mogu vam slati obavijesti, trošiti bateriju niti pristupiti vašoj lokaciji"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Poslovni profil je pauziran. Poslovne aplikacije ne mogu vam slati obavijesti, trošiti bateriju niti pristupiti vašoj lokaciji"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Poslovne su aplikacije označene i vidljive vašem IT administratoru"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Shvaćam"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pauziraj poslovne aplikacije"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Uključi"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrirajte"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte poslovne aplikacije i obavijesti"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 4f03908..d0aea22 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"A letöltött alkalmazás Csökkentett módban ki van kapcsolva"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"A modulok ki vannak kapcsolva Csökkentett módban"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"A gyorsparancs nem áll rendelkezésre"</string>
-    <string name="home_screen" msgid="806512411299847073">"Kezdőképernyő"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Egyéni műveletek"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Modul felvételéhez érintse meg, és tartsa lenyomva"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Modul mozgatásához koppintson rá duplán és tartsa lenyomva, vagy használjon egyéni műveleteket."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d széles és %2$d magas"</string>
-    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tartsa lenyomva a manuális hozzáadáshoz"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tartsd lenyomva a manuális hozzáadáshoz"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Automatikus hozzáadás"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Keresőmezőben lévő szöveg törlése"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nincs rendelkezésre álló modul"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nincsenek keresési találatok"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Személyes"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Munka"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Beszélgetések"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Alkalmazások keresése"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Alkalmazások betöltése…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Értesítések"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Felvételhez tartsa nyomva a parancsikont."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Parancsikon felvételéhez koppintson rá duplán és tartsa nyomva, vagy használjon egyéni műveleteket."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Nincs több hely ezen a kezdőképernyőn."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nincs több hely a Kedvencek tálcán"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Alkalmazások listája"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Személyes alkalmazások listája"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Munkahelyi alkalmazások listája"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Főoldal"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Törlés"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Eltávolítás"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Alkalmazásinformáció"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"A mappa új neve: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elem"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vagy több elem"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stílusok és háttérképek"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Kezdőképernyő beállításai"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Keresés"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Az alkalmazás nincs telepítve"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Az ikonhoz tartozó alkalmazás nincs telepítve. Törölheti az ikont, vagy az alkalmazás megkeresése után manuálisan telepítheti azt."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Folyamatban van a(z) <xliff:g id="NAME">%1$s</xliff:g> telepítése, <xliff:g id="PROGRESS">%2$s</xliff:g> kész"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"A(z) <xliff:g id="NAME">%1$s</xliff:g> letöltése, <xliff:g id="PROGRESS">%2$s</xliff:g> kész"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"A(z) <xliff:g id="NAME">%1$s</xliff:g> telepítésre vár"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-modulok"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widgetlista"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widgetlista bezárva"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Hozzáadás a kezdőképernyőhöz"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Tovább"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Értem"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"A munkaprofil használata szünetel"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"A munkaprofil használata szünetel. A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez."</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"A munkaprofil szüneteltetve van. A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"A munkahelyi alkalmazások jelvénnyel vannak megjelölve, és láthatók a rendszergazda számára"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Értem"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Munkahelyi alkalmazások szüneteltetése"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Bekapcsolás"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Szűrő"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Munkahelyi alkalmazások és értesítések szüneteltetése"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Sikertelen: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index c098bb3..fbbcf1c 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Վիջեթներն անջատված են անվտանգ ռեժիմում"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Դյուրանցումն անհասանելի է"</string>
-    <string name="home_screen" msgid="806512411299847073">"Հիմնական էկրան"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Հատուկ գործողություններ"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Հպեք և պահեք՝ վիջեթն ընտրելու համար:"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Կրկնակի հպեք և պահեք՝ վիջեթ ավելացնելու համար կամ օգտվեք հարմարեցրած գործողություններից:"</string>
+    <string name="home_screen" msgid="5629429142036709174">"Հիմնական էկրան"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Տրոհել էկրանը"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Լայնությունը՝ %1$d, բարձրությունը՝ %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Հպեք և պահեք՝ ձեռքով տեղադրելու համար"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Ավելացնել ավտոմատ կերպով"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Մաքրել որոնման դաշտի տեքստը"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Վիջեթներ չկան"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Որոնման արդյունքներ չկան"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Անձնական"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Աշխատանքային"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Զրույցներ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Որոնել հավելվածներ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Հավելվածների բեռնում…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"«<xliff:g id="QUERY">%1$s</xliff:g>» հարցմանը համապատասխանող հավելվածներ չեն գտնվել"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Որոնել այլ հավելվածներ"</string>
     <string name="label_application" msgid="8531721983832654978">"Հավելված"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Ծանուցումներ"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար։"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար կամ օգտվեք հարմարեցրած գործողություններից:"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Հպեք և պահեք՝ դյուրանցում տեղափոխելու համար։"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Կրկնակի հպեք և պահեք՝ դյուրանցում տեղափոխելու համար, կամ օգտվեք հատուկ գործողություններից։"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Այլևս տեղ չկա այս հիմնական էկրանին:"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ընտրյալների ցուցակում այլևս ազատ տեղ չկա"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Հավելվածների ցանկ"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Անձնական հավելվածների ցանկ"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Աշխատանքային հավելվածների ցանկ"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Հիմնական"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Հեռացնել"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Ապատեղադրել"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Հավելվածի մասին"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Պանակը վերանվանվեց <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> տարր"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> կամ ավելի տարրեր"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Ոճեր և պաստառներ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Գլխավոր էջի կարգավորումներ"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Գտնել"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Այս ծրագիրը տեղադրված չէ:"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Այս պատկերակի ծրագիրը տեղադրված չէ: Դուք կարող եք հեռացնել այն կամ գտնել ծրագիրը և տեղադրել այն ձեռքով:"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> հավելվածը տեղադրվում է, կատարված է <xliff:g id="PROGRESS">%2$s</xliff:g>-ը"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>–ի ներբեռնում (<xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>-ի տեղադրման սպասում"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> վիջեթներ"</string>
     <string name="widgets_list" msgid="796804551140113767">"Վիջեթների ցանկ"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Վիջեթների ցանկը փակվեց"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Ավելացնել Հիմնական էկրանին"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Առաջ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Եղավ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Աշխատանքային պրոֆիլը դադարեցված է"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել, օգտագործել ձեր մարտկոցը և ձեր տեղադրության մասին տվյալներ ստանալ։"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Աշխատանքային պրոֆիլը դադարեցված է։ Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել և օգտագործել ձեր մարտկոցն ու տեղադրության տվյալները։"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել, օգտագործել ձեր մարտկոցը և ձեր տեղադրության մասին տվյալներ ստանալ։"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Աշխատանքային պրոֆիլը դադարեցված է։ Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել, օգտագործել ձեր մարտկոցը և ձեր տեղադրության մասին տվյալներ ստանալ։"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Աշխատանքային հավելվածները նշանակներ ունեն և տեսանելի են ձեր ՏՏ ադմինիստրատորին"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Եղավ"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Դադարեցնել աշխատանքային հավելվածները"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Միացնել"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Զտեք"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Դադարեցնել աշխատանքային հավելվածներն ու ծանուցումները"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Չհաջողվեց կատարել գործողությունը (<xliff:g id="WHAT">%1$s</xliff:g>)"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 242e808..7bc91c2 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplikasi yang didownload dinonaktifkan dalam mode Aman"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widget dinonaktifkan dalam mode Aman"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Pintasan tidak tersedia"</string>
-    <string name="home_screen" msgid="806512411299847073">"Layar utama"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Tindakan khusus"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Sentuh lama untuk memilih widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ketuk dua kali &amp; tahan untuk mengambil widget atau menggunakan tindakan khusus."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Layar utama"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Layar terpisah"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"lebar %1$d x tinggi %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Sentuh lama untuk menempatkan secara manual"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Tambahkan otomatis"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Hapus teks dari kotak penelusuran"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Widget tidak tersedia"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Tidak ada hasil penelusuran"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pribadi"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Kantor"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Percakapan"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Telusuri aplikasi"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Memuat aplikasi…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifikasi"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Sentuh lama untuk memilih pintasan."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Ketuk dua kali &amp; tahan untuk memilih pintasan atau menggunakan tindakan khusus."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Tidak ada ruang lagi pada layar Utama ini."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Tidak ada ruang tersisa di baki Favorit"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Daftar aplikasi"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Daftar aplikasi pribadi"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Daftar aplikasi kantor"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Layar Utama"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Hapus"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstal"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Info aplikasi"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Folder diganti namanya menjadi <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item atau lebih"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Gaya &amp; wallpaper"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Setelan layar utama"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Telusuri"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikasi ini belum terpasang"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikasi untuk ikon ini belum dipasang. Anda dapat membuangnya, atau menelusuri aplikasi dan memasangnya secara manual."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> sedang diinstal, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> sedang didownload, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> menunggu dipasang"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widget <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Daftar widget"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Daftar widget ditutup"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Tambahkan ke Layar Utama"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Berikutnya"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Oke"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Profil kerja dijeda"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Aplikasi kerja tidak dapat mengirimi Anda notifikasi, menggunakan baterai, atau mengakses lokasi Anda"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profil kerja dijeda. Aplikasi kerja tidak dapat mengirimi Anda notifikasi, menggunakan baterai, atau mengakses lokasi Anda"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Aplikasi kerja tidak dapat mengirimi Anda notifikasi, menggunakan baterai, atau mengakses lokasi Anda"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Profil kerja dijeda. Aplikasi kerja tidak dapat mengirimi Anda notifikasi, menggunakan baterai, atau mengakses lokasi Anda"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Aplikasi kerja diberi badge dan terlihat oleh admin IT"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Oke"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Jeda aplikasi kerja"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aktifkan"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Jeda notifikasi dan aplikasi kerja"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 8e66dea..104b4ac 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Sótt forrit er óvirkt í öryggisstillingu"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Græjur eru óvirkar í öruggri stillingu"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Flýtileið er ekki tiltæk"</string>
-    <string name="home_screen" msgid="806512411299847073">"Heimaskjár"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Sérsniðnar aðgerðir"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Haltu fingri á græju til að grípa hana."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ýttu tvisvar og haltu fingri á græju til að grípa hana eða notaðu sérsniðnar aðgerðir."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Heim"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Skipta skjá"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d á breidd og %2$d á hæð"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Haltu inni til að staðsetja handvirkt"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Bæta sjálfkrafa við"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Hreinsa texta úr leitarreit"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Engar græjur í boði"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Engar leitarniðurstöður"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Persónulegt"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Vinna"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Samtöl"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Leita í forritum"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Hleður forrit…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Tilkynningar"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Haltu fingri á flýtileið til að grípa hana."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Ýttu tvisvar og haltu fingri á flýtileið til að grípa hana eða notaðu sérsniðnar aðgerðir."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Ekki meira pláss á þessum heimaskjá."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ekki meira pláss í bakka fyrir uppáhald"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Forritalisti"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Listi yfir eigin forrit"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Listi yfir vinnuforrit"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Heim"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Taka niður"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Forritsupplýsingar"</string>
@@ -80,12 +96,11 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Heiti möppu breytt í <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atriði"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eða fleiri atriði"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stílar og veggfóður"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Heimastillingar"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gert óvirkt af kerfisstjóra"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Leyfa snúning fyrir heimaskjá"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Leyfa snúning á heimaskjá"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Þegar símanum er snúið"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"Tilkynningapunktar"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"Kveikt"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Leita"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Þetta forrit er ekki uppsett"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Forritið fyrir þetta tákn er ekki uppsett. Þú getur fjarlægt það eða leitað að forritinu og sett það upp handvirkt."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Setur upp <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> lokið"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> í niðurhali, <xliff:g id="PROGRESS">%2$s</xliff:g> lokið"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> bíður uppsetningar"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-græjur"</string>
     <string name="widgets_list" msgid="796804551140113767">"Græjulisti"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Græjulista lokað"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Bæta á heimaskjá"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Áfram"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ég skil"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Hlé gert á vinnusniði"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Vinnuforrit geta ekki sent þér tilkynningar, notað rafhlöðuorku eða fengið aðgang að staðsetningu þinni"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Hlé gert á vinnusniði. Vinnuforrit geta ekki sent þér tilkynningar, notað rafhlöðuorku eða fengið aðgang að staðsetningu þinni"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Vinnuforrit geta ekki sent þér tilkynningar, notað rafhlöðuorku eða fengið aðgang að staðsetningu þinni"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Hlé gert á vinnusniði. Vinnuforrit geta ekki sent þér tilkynningar, notað rafhlöðuorku eða fengið aðgang að staðsetningu þinni"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Vinnuforrit eru merkt og kerfisstjórinn getur séð þau"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ég skil"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Setja vinnuforrit í bið"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Kveikja"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Sía"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Gera hlé á vinnuforritum og tilkynningum"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Mistókst: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 4ffc861..9e13576 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'app scaricata è stata disattivata in modalità provvisoria"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widget disabilitati in modalità provvisoria"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"La scorciatoia non è disponibile"</string>
-    <string name="home_screen" msgid="806512411299847073">"Schermata Home"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Azioni personalizzate"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Tocca e tieni premuto per scegliere un widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tocca due volte e tieni premuto per scegliere un widget o per utilizzare azioni personalizzate."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Home"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Schermo diviso"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d di larghezza per %2$d di altezza"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tieni premuto per posizionare l\'elemento manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Aggiungi automaticamente"</string>
+    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
+      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
+      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
+    </plurals>
+    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
+      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> scorciatoie</item>
+      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> scorciatoia</item>
+    </plurals>
+    <string name="widgets_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Cancella il testo nella casella di ricerca"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nessun widget disponibile"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nessun risultato di ricerca"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personali"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Lavoro"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversazioni"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cerca nelle app"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Caricamento delle app…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notifiche"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tocca e tieni premuto per scegliere la scorciatoia"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tocca due volte e tieni premuto per scegliere una scorciatoia o per usare azioni personalizzate."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Spazio nella schermata Home esaurito."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Spazio esaurito nella barra dei Preferiti"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Elenco di app"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Elenco di app personali"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Elenco di app di lavoro"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Home page"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Rimuovi"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Disinstalla"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Informazioni app"</string>
@@ -80,10 +96,9 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Nome della cartella sostituito con <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementi"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o più elementi"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stili e sfondi"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Impostazioni Home"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Impostazioni schermata Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Consenti rotazione della schermata Home"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Con il telefono ruotato"</string>
@@ -94,16 +109,16 @@
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Per mostrare gli indicatori di notifica, attiva le notifiche per l\'app <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Modifica impostazioni"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostra indicatori di notifica"</string>
-    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Aggiungi icone app a schermata Home"</string>
+    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Aggiungi icone delle app alla schermata Home"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per le nuove app"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Rimuovi"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"L\'app non è installata"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'app per questa icona non è installata. Puoi rimuoverla o cercare l\'app e installarla manualmente."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Installazione di <xliff:g id="NAME">%1$s</xliff:g>, completamento: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Download di <xliff:g id="NAME">%1$s</xliff:g> in corso, <xliff:g id="PROGRESS">%2$s</xliff:g> completato"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> in attesa di installazione"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widget di <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Elenco di widget"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Elenco di widget chiuso"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Aggiungi a schermata Home"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Avanti"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Profilo di lavoro in pausa"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Le app di lavoro non possono inviarti notifiche, usare la tua batteria o accedere alla tua posizione"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profilo di lavoro in pausa. Le app di lavoro non possono inviarti notifiche, usare la tua batteria o accedere alla tua posizione"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Le app di lavoro non possono inviarti notifiche, usare la batteria o accedere alla tua posizione"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Profilo di lavoro in pausa. Le app di lavoro non possono inviarti notifiche, usare la batteria o accedere alla tua posizione"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Le app di lavoro sono contrassegnate con un badge e sono visibili dal tuo amministratore IT"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Metti in pausa le app di lavoro"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Attiva"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtra"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Metti in pausa le app di lavoro e le relative notifiche"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Operazione non riuscita: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index a2068aa..639aa86 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -26,39 +26,59 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"אפליקציה שהורדת הושבתה במצב בטוח"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"ווידג\'טים מושבתים במצב בטוח"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"קיצור הדרך אינו זמין"</string>
-    <string name="home_screen" msgid="806512411299847073">"מסך דף הבית"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"פעולות מותאמות אישית"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"אפשר לבחור את הווידג\'ט אם נוגעים בו נגיעה רציפה."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"הקש פעמיים וגע נגיעה רציפה בווידג\'ט כדי לבחור בו, או השתמש בפעולות מותאמות אישית."</string>
+    <string name="home_screen" msgid="5629429142036709174">"בית"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"מסך מפוצל"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏רוחב %1$d על גובה %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"יש ללחוץ לחיצה ארוכה כדי להוסיף ידנית"</string>
-    <string name="place_automatically" msgid="8064208734425456485">"הוסף באופן אוטומטי"</string>
+    <string name="place_automatically" msgid="8064208734425456485">"הוספה אוטומטית"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"מחיקת טקסט מתיבת החיפוש"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"אין ווידג\'טים זמינים"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"אין תוצאות חיפוש"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"אישי"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"עבודה"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"שיחות"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"חיפוש אפליקציות"</string>
-    <string name="all_apps_loading_message" msgid="5813968043155271636">"טוען אפליקציות…"</string>
+    <string name="all_apps_loading_message" msgid="5813968043155271636">"טעינת אפליקציות מתבצעת…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"לא נמצאו אפליקציות התואמות ל-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="1366263386197059176">"חפש אפליקציות נוספות"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"חיפוש אפליקציות נוספות"</string>
     <string name="label_application" msgid="8531721983832654978">"אפליקציה"</string>
     <string name="notifications_header" msgid="1404149926117359025">"התראות"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"כדי להוסיף קיצור דרך, יש לגעת בו ולהחזיק אותו."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"כדי להוסיף קיצור דרך או להשתמש בפעולות מותאמות אישית, יש להקיש על קיצור הדרך פעמיים ולהחזיק אותו."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"כדי להעביר קיצור דרך למקום אחר יש לגעת ולא להרפות."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"כדי להעביר קיצור דרך למקום אחר או להשתמש בפעולות מותאמות אישית\' יש ללחוץ פעמיים ולא להרפות."</string>
     <string name="out_of_space" msgid="4691004494942118364">"אין עוד מקום במסך דף הבית הזה."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"אין עוד מקום במגש המועדפים"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"רשימת אפליקציות"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"רשימת אפליקציות אישיות"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"רשימת אפליקציות עבודה"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"דף הבית"</string>
-    <string name="remove_drop_target_label" msgid="7812859488053230776">"הסר"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"הסרה"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"להסרת התקנה"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"פרטי אפליקציה"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"התקנה"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"בלי להציע את האפליקציה"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"הצמדת החיזוי"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"התקן קיצורי דרך"</string>
+    <string name="permlab_install_shortcut" msgid="5632423390354674437">"התקנת קיצורי דרך"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"קרא הגדרות וקיצורי דרך של דף הבית"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"קריאת הגדרות וקיצורי דרך של דף הבית"</string>
     <string name="permdesc_read_settings" msgid="5833423719057558387">"מאפשר לאפליקציה לקרוא את ההגדרות וקיצורי הדרך בדף הבית."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"כתוב הגדרות וקיצורי דרך של דף הבית"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"כתיבת הגדרות וקיצורי דרך של דף הבית"</string>
     <string name="permdesc_write_settings" msgid="5440712911516509985">"מאפשר לאפליקציה לשנות את ההגדרות וקיצורי הדרך בדף הבית."</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> אינו רשאי להתקשר"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"בעיה בטעינת ווידג\'ט"</string>
@@ -75,60 +95,59 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"‏דף %1$d מתוך %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏מסך דף הבית %1$d מתוך %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"מסך דף הבית חדש"</string>
-    <string name="folder_opened" msgid="94695026776264709">"תיקיה פתוחה, <xliff:g id="WIDTH">%1$d</xliff:g> על <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"הקש כדי לסגור את התיקייה"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"הקש כדי לשמור שינוי שם"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"התיקיה נסגרה"</string>
-    <string name="folder_renamed" msgid="1794088362165669656">"שם התיקיה שונה ל-<xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="folder_opened" msgid="94695026776264709">"תיקייה פתוחה, <xliff:g id="WIDTH">%1$d</xliff:g> על <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"יש להקיש כדי לסגור את התיקייה"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"יש להקיש כדי לשמור שינוי שם"</string>
+    <string name="folder_closed" msgid="4100806530910930934">"התיקייה נסגרה"</string>
+    <string name="folder_renamed" msgid="1794088362165669656">"שם התיקייה שונה ל-<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, מספר הפריטים: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> פריטים או יותר"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ווידג\'טים"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"סגנונות וטפטים"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"הגדרות דף הבית"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"הגדרות של מסך הבית"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"אפשרות סיבוב של מסך דף הבית"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"סיבוב של מסך הבית"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"כאשר הטלפון מסובב"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"סימני ההתראות"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"מופעל"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"כבוי"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"נדרשת גישה להתראות"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"כדי להציג את סימני ההתראות,יש להפעיל התראות מהאפליקציה <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"שנה את ההגדרות"</string>
+    <string name="title_change_settings" msgid="1376365968844349552">"שינוי ההגדרות"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"הצגת סימני ההתראות"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"הוספת סמלי אפליקציות למסך הבית"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"לאפליקציות חדשות"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"הסר"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"הסרה"</string>
     <string name="abandoned_search" msgid="891119232568284442">"חיפוש"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"אפליקציה זו אינה מותקנת"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"האפליקציה של סמל זה אינה מותקנת. ניתן להסיר אותו, או לחפש את האפליקציה ולהתקין אותה ידנית."</string>
-    <string name="app_downloading_title" msgid="8336702962104482644">"מוריד את <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> הושלמו"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> בתהליך התקנה, <xliff:g id="PROGRESS">%2$s</xliff:g> הושלמו"</string>
+    <string name="app_downloading_title" msgid="8336702962104482644">"הורדת <xliff:g id="NAME">%1$s</xliff:g> מתבצעת, <xliff:g id="PROGRESS">%2$s</xliff:g> הושלמו"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"מחכה להתקנה של <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"ווידג\'טים של <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"רשימת ווידג\'טים"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"רשימת הווידג\'טים נסגרה"</string>
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"הוסף למסך דף הבית"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"העבר את הפריט לכאן"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"הוספה למסך דף הבית"</string>
+    <string name="action_move_here" msgid="2170188780612570250">"העברת הפריט לכאן"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"הפריט הועבר אל מסך דף הבית"</string>
     <string name="item_removed" msgid="851119963877842327">"הפריט הוסר"</string>
     <string name="undo" msgid="4151576204245173321">"ביטול"</string>
-    <string name="action_move" msgid="4339390619886385032">"העבר את הפריט"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"העבר אל שורה <xliff:g id="NUMBER_0">%1$s</xliff:g> עמודה <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"העבר אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"העבר אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g> במועדפים"</string>
+    <string name="action_move" msgid="4339390619886385032">"העברת הפריט"</string>
+    <string name="move_to_empty_cell" msgid="2833711483015685619">"העברה אל שורה <xliff:g id="NUMBER_0">%1$s</xliff:g> עמודה <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_position" msgid="6750008980455459790">"העברה אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+    <string name="move_to_hotseat_position" msgid="6295412897075147808">"העברה אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g> במועדפים"</string>
     <string name="item_moved" msgid="4606538322571412879">"הפריט הועבר"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"הוסף לתיקייה: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"העבר אל התיקייה עם <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="add_to_folder" msgid="9040534766770853243">"הוספה לתיקייה: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="add_to_folder_with_app" msgid="4534929978967147231">"הוספה לתיקייה עם <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="added_to_folder" msgid="4793259502305558003">"הפריט נוסף לתיקייה"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"צור תיקייה עם: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="create_folder_with" msgid="4050141361160214248">"יצירת תיקייה עם: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_created" msgid="6409794597405184510">"התיקייה נוצרה"</string>
     <string name="action_move_to_workspace" msgid="1603837886334246317">"העבר אל מסך דף הבית"</string>
-    <string name="action_resize" msgid="1802976324781771067">"שנה גודל"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"הגדל רוחב"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"הגדל גובה"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"הקטן רוחב"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"הקטן גובה"</string>
+    <string name="action_resize" msgid="1802976324781771067">"שינוי גודל"</string>
+    <string name="action_increase_width" msgid="8773715375078513326">"הגדלת רוחב"</string>
+    <string name="action_increase_height" msgid="459390020612501122">"הגדלת גובה"</string>
+    <string name="action_decrease_width" msgid="1374549771083094654">"הקטנת רוחב"</string>
+    <string name="action_decrease_height" msgid="282377193880900022">"הקטנת גובה"</string>
     <string name="widget_resized" msgid="9130327887929620">"גודל הווידג\'ט שונה - רוחב <xliff:g id="NUMBER_0">%1$s</xliff:g> גובה <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="action_deep_shortcut" msgid="2864038805849372848">"קיצורי דרך"</string>
     <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"קיצורי דרך והתראות"</string>
@@ -143,8 +162,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"הבא"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"הבנתי"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"פרופיל העבודה מושהה"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"אפליקציות לעבודה לא יכולות לשלוח לך התראות, להשתמש בסוללה או לגשת למיקום שלך"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"פרופיל העבודה מושהה. אפליקציות לעבודה לא יכולות לשלוח לך התראות, להשתמש בסוללה או לגשת למיקום שלך"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"האפליקציות לעבודה לא יכולות לשלוח לך התראות, להשתמש בסוללה או לגשת למיקום שלך"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"פרופיל העבודה מושהה. האפליקציות לעבודה לא יכולות לשלוח לך התראות, להשתמש בסוללה או לגשת למיקום שלך"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"‏האפליקציות לעבודה מתויגות ומוצגות למנהל ה-IT"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"הבנתי"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"השהיית האפליקציות לעבודה"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"הפעלה"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"סינון"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"השהיה של התראות ואפליקציות לעבודה"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"הפעולה נכשלה: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 4e0181c..dc92748 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ダウンロードしたアプリは、セーフモードでは無効です"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"セーフモードではウィジェットは無効です"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ショートカットは使用できません"</string>
-    <string name="home_screen" msgid="806512411299847073">"ホーム画面"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"カスタム操作"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ウィジェットを追加するには押し続けます。"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ダブルタップ後に押し続けてウィジェットを選択するか、カスタム操作を使用してください。"</string>
+    <string name="home_screen" msgid="5629429142036709174">"ホーム"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割画面"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"幅 %1$d、高さ %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"長押しすると、手動で追加できます"</string>
     <string name="place_automatically" msgid="8064208734425456485">"自動的に追加"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"検索ボックスからテキストを消去します"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"利用できるウィジェットはありません"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"検索結果がありません"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"個人用"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"仕事用"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"会話"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"アプリを検索"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"アプリを読み込んでいます…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"「<xliff:g id="QUERY">%1$s</xliff:g>」に一致するアプリは見つかりませんでした"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"他のアプリを検索"</string>
     <string name="label_application" msgid="8531721983832654978">"アプリ"</string>
     <string name="notifications_header" msgid="1404149926117359025">"通知"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ショートカットを追加するには押し続けます。"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ダブルタップ後に押し続けてショートカットを選択するか、カスタム操作を使用してください。"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"長押ししてショートカットを移動してください。"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ショートカットをダブルタップして長押ししながら移動するか、カスタム操作を使用してください。"</string>
     <string name="out_of_space" msgid="4691004494942118364">"このホーム画面に空きスペースがありません。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"お気に入りトレイに空きスペースがありません"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"アプリのリスト"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"個人用アプリのリスト"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"仕事用アプリのリスト"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ホーム"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"削除"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"アンインストール"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"アプリ情報"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"フォルダの名前を「<xliff:g id="NAME">%1$s</xliff:g>」に変更しました"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>、<xliff:g id="SIZE">%2$d</xliff:g> 件のアイテム"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>、<xliff:g id="SIZE">%2$d</xliff:g> 件以上のアイテム"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ウィジェット"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"スタイルと壁紙"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ホームの設定"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"検索"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"このアプリはインストールされていません"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"このアイコンのアプリはインストールされていません。このアイコンは削除できます。または、手動でアプリを検索してインストールしください。"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> をインストールしています: <xliff:g id="PROGRESS">%2$s</xliff:g> 完了"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>をダウンロード中、<xliff:g id="PROGRESS">%2$s</xliff:g>完了"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>のインストール待ち"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>のウィジェット"</string>
     <string name="widgets_list" msgid="796804551140113767">"ウィジェット リスト"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ウィジェット リストを閉じました"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ホーム画面に追加"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"次へ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"仕事用プロファイルが一時停止しています"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"仕事用アプリは、通知の送信、電池の使用、位置情報へのアクセスを行えません"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"仕事用プロファイルが一時停止しています。仕事用アプリから通知の送信、電池の使用、位置情報へのアクセスを行えません"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"仕事用アプリは、通知の送信、バッテリーの使用、位置情報へのアクセスを行えません"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"仕事用プロファイルが一時停止しています。仕事用アプリは、通知の送信、バッテリーの使用、位置情報へのアクセスを行えません"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"仕事用アプリはバッジが付き、IT 管理者に公開されます"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"仕事用アプリを一時停止"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ON にする"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"フィルタ"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"仕事用のアプリと通知を一時停止します"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"失敗: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 44ea386..7a0ec8d 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"უსაფრთხო რეჟიმში ვიჯეტი გამორთულია"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"მალსახმობი მიუწვდომელია"</string>
-    <string name="home_screen" msgid="806512411299847073">"მთავარი ეკრანი"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"მორგებული ქმედებები"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"შეეხეთ და დააყოვნეთ ვიჯეტის ასარჩევად."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ორმაგად შეეხეთ და გეჭიროთ ვიჯეტის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
+    <string name="home_screen" msgid="5629429142036709174">"მთავარი გვერდი"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ეკრანის გაყოფა"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"სიგრძე: %1$d, სიგანე: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ხანგრძლივად შეეხეთ ხელით განსათავსებლად"</string>
     <string name="place_automatically" msgid="8064208734425456485">"ავტომატურად დამატება"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ტექსტის გასუფთავება საძიებო ველიდან"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ვიჯეტები მიუწვდომელია"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"ძიების შედეგები ვერ მოიძებნა"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"პირადი"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"სამსახური"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"მიმოწერები"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"აპების ძიება"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"აპები იტვირთება…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"მეტი აპის პოვნა"</string>
     <string name="label_application" msgid="8531721983832654978">"აპი"</string>
     <string name="notifications_header" msgid="1404149926117359025">"შეტყობინებები"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"შეეხეთ და დააყოვნეთ მალსახმობის ასარჩევად."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ორმაგად შეეხეთ და გეჭიროთ მალსახმობის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"შეხებით აირჩიეთ და გეჭიროთ მალსახმობის გადასაადგილებლად."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ორმაგი შეხებით აირჩიეთ და გეჭიროთ მალსახმობის გადასაადგილებლად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
     <string name="out_of_space" msgid="4691004494942118364">"ამ მთავარ ეკრანზე ადგილი აღარ არის."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"რჩეულების თაროზე ადგილი არ არის"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"აპების სია"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"პერსონალური აპების სია"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"სამსახურის აპების სია"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"მთავარი"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ამოშლა"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"დეინსტალაცია"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"აპის შესახებ"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"საქაღალდეს შეეცვალა სახელი „<xliff:g id="NAME">%1$s</xliff:g>“-ად"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ერთეული"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ან მეტი ერთეული"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"სტილები და ფონები"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"მთავარი გვერდის პარამეტრები"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"ძიება"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ეს აპი დაყენებული არ არის"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ამ ხატულის აპი დაყენებული არ არის. შეგიძლიათ ამოშალოთ, ან მოიძიოთ აპი და ხელით მოახდინოთ მისი ინსტალაცია."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"ინსტალირდება <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> დასრულებულია"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"მიმდინარეობს <xliff:g id="NAME">%1$s</xliff:g>-ის ჩამოტვირთვა, <xliff:g id="PROGRESS">%2$s</xliff:g> დასრულდა"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ელოდება ინსტალაციას"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-ის ვიჯეტები"</string>
     <string name="widgets_list" msgid="796804551140113767">"ვიჯეტების სია"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ვიჯეტების სია დაიხურა"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"მთავარ ეკრანზე დამატება"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"შემდეგ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"გასაგებია"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"სამსახურის პროფილი დაპაუზებულია"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"სამსახურის აპებს არ შეუძლია თქვენთვის შეტყობინებების გამოგზავნა, თქვენი ბატარეის გამოყენება, ან თქვენს მდებარეობაზე წვდომა"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"სამსახურის პროფილი დაპაუზებულია. სამსახურის აპებს არ შეუძლია თქვენთვის შეტყობინებების გამოგზავნა, თქვენი ბატარეის გამოყენება, ან თქვენს მდებარეობაზე წვდომა"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"სამსახურის აპებს არ შეუძლია თქვენთვის შეტყობინებების გამოგზავნა, თქვენი ბატარეის გამოყენება, ან თქვენს მდებარეობაზე წვდომა"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"სამსახურის პროფილი დაპაუზებულია. სამსახურის აპებს არ შეუძლია თქვენთვის შეტყობინებების გამოგზავნა, თქვენი ბატარეის გამოყენება, ან თქვენს მდებარეობაზე წვდომა"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"სამსახურის აპები ბეჯით არის მონიშნული და ხილულია IT ადმინისტრატორისთვის"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"გასაგებია"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"სამსახურის აპების დაპაუზება"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ჩართვა"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ფილტრი"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"სამსახურის აპებისა და შეტყობინებების დაპაუზება"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ვერ მოხერხდა: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 7dd4400..dff1311 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Жүктелген қолданба қауіпсіз режимде өшірілген"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Қауіпсіз режимде виджеттер өшіріледі"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Таңбаша қолжетімді емес"</string>
-    <string name="home_screen" msgid="806512411299847073">"Негізгі экран"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Арнаулы әрекеттер"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Виджетті таңдау үшін түртіп, мықтап ұстаңыз."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджетті таңдау немесе арнаулы әрекеттерді таңдау үшін екі рет түртіп, ұстап тұрыңыз."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Негізгі экран"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлу"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ені: %1$d, биіктігі: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Қолмен қою үшін басып тұрыңыз"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Автоматты қосу"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Мәтінді іздеу өрісінен өшіру"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Қолжетімді виджет жоқ."</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Іздеу нәтижелері жоқ."</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Жеке виджеттер"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Жұмыс виджеттері"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Әңгімелер"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Қолданбаларды іздеу"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Қолданбалар жүктелуде…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" сұрауына сәйкес келетін қолданбалар жоқ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Қосымша қолданбалар іздеу"</string>
     <string name="label_application" msgid="8531721983832654978">"Қолданба"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Хабарландырулар"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Таңбашаны таңдау үшін оны басып, ұстап тұрыңыз."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Екі рет басып, ұстап тұрып, таңбашаны таңдаңыз немесе арнаулы әрекеттерді пайдаланыңыз."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Таңбашаны жылжыту үшін түртіп ұстап тұрыңыз."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Таңбашаны жылжыту үшін екі рет түртіңіз де, ұстап тұрыңыз немесе арнаулы әрекеттерді пайдаланыңыз."</string>
     <string name="out_of_space" msgid="4691004494942118364">"Бұл Негізгі экранда орын қалмады."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Қалаулылар науасында орын қалмады"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Қолданбалар тізімі"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Жеке қолданбалар тізімі"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Жұмыс қолданбаларының тізімі"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Негізгі"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Алып тастау"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Жою"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Қолданба ақпараты"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Қалта атауы <xliff:g id="NAME">%1$s</xliff:g> болып өзгертілді"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> элемент бар"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> не одан көп элемент бар"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стильдер мен тұсқағаздар"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Негізгі экран параметрлері"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Іздеу"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Бұл қолданба орнатылмаған"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Осы белгіше үшін қолданба орнатылмаған. Оны жоюға болады немесе қолданбаны іздеп, қолмен орнатуға болады."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> орнатылуда, <xliff:g id="PROGRESS">%2$s</xliff:g> аяқталды"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> жүктелуде, <xliff:g id="PROGRESS">%2$s</xliff:g> аяқталды"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> орнату күтілуде"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> виджеті"</string>
     <string name="widgets_list" msgid="796804551140113767">"Виджеттер тізімі"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Видджеттер тізімі жабылды"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Негізгі экранға қосу"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Келесі"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Түсінікті"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Жұмыс профилі кідіртілді"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Жұмыс қолданбалары сізге хабарландырулар жібермейді, батареяңызды немесе геодерегіңізді пайдаланбайды."</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Жұмыс профилі уақытша тоқтатылды. Жұмыс қолданбалары сізге хабарландырулар жібермейді, батареяңызды немесе геодерегіңізді пайдаланбайды."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Жұмыс қолданбалары сізге хабарландырулар жібере алмайды, батареяңызды немесе локацияңызды пайдалана алмайды."</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Жұмыс профилі уақытша тоқтатылды. Жұмыс қолданбалары сізге хабарландырулар жібере алмайды, батареяңызды немесе локацияңызды пайдалана алмайды."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Жұмыс қолданбаларының танымбелгілері бар және олар әкімшіңізге көрінеді."</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Түсінікті"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Жұмыс қолданбаларын тоқтата тұру"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Қосу"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Сүзгі"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Жұмыс қолданбалары мен хабарландыруларды кідірту"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Қате шықты: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 3ba564e..f460512 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"បាន​បិទ​កម្មវិធី​ដែល​បាន​ទាញ​យក​ក្នុង​របៀប​សុវត្ថិភាព"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"បាន​បិទ​ធាតុ​ក្រាហ្វិក​ក្នុង​របៀប​សុវត្ថិភាព"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ផ្លូវកាត់មិនអាចប្រើបានទេ"</string>
-    <string name="home_screen" msgid="806512411299847073">"អេក្រង់ដើម"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"សកម្មភាព​ផ្ទាល់ខ្លួន"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ប៉ះ &amp; សង្កត់ ដើម្បី​ជ្រើស​ធាតុ​ក្រាហ្វិក។"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ប៉ះពីរដង ហើយចុចឲ្យជាប់ដើម្បីជ្រើសយកធាតុក្រាហ្វិក ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។"</string>
+    <string name="home_screen" msgid="5629429142036709174">"អេក្រង់ដើម"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"មុខងារ​បំបែកអេក្រង់"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ទទឺង %1$d គុណនឹងកម្ពស់ %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ចុច​ឲ្យជាប់​ដើម្បី​បញ្ចូលវា​ដោយផ្ទាល់"</string>
     <string name="place_automatically" msgid="8064208734425456485">"បញ្ចូល​ដោយ​ស្វ័យ​ប្រវត្តិ"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"សម្អាត​ពាក្យឬឃ្លា​ចេញពី​ប្រអប់ស្វែងរក"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"គ្មាន​ធាតុក្រាហ្វិក​ទេ"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"គ្មាន​លទ្ធផល​ស្វែងរក​ទេ"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ផ្ទាល់ខ្លួន"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ការងារ"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"ការសន្ទនា"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ស្វែងរក​កម្មវិធី"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"កំពុងផ្ទុកកម្មវិធី…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"រកមិនឃើញកម្មវិធី​ដែលត្រូវគ្នាជាមួយ \"<xliff:g id="QUERY">%1$s</xliff:g>\" ទេ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ស្វែងរកកម្មវិធីច្រើនទៀត"</string>
     <string name="label_application" msgid="8531721983832654978">"កម្មវិធី"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ការ​ជូនដំណឹង"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ចុច​ឱ្យ​ជាប់​ដើម្បី​ជ្រើស​រើស​ផ្លូវកាត់​មួយ។"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ចុច​ពីរ​ដង ហើយ​ចុច​ឱ្យ​ជាប់​ដើម្បី​ជ្រើសរើស​ផ្លូវកាត់​មួយ ឬ​ប្រើ​សកម្មភាព​ផ្ទាល់ខ្លួន។"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ចុចឱ្យជាប់​ដើម្បីផ្លាស់ទី​ផ្លូវកាត់​។"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ចុចពីរដង រួចសង្កត់ឱ្យជាប់ ដើម្បីផ្លាស់ទី​ផ្លូវកាត់ ឬប្រើ​សកម្មភាព​តាមបំណង​។"</string>
     <string name="out_of_space" msgid="4691004494942118364">"គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"បញ្ជីកម្មវិធី"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"បញ្ជី​កម្មវិធី​ផ្ទាល់ខ្លួន"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"បញ្ជី​កម្មវិធី​ការងារ"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ដើម"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"យកចេញ"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"លុបការដំឡើង"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ព័ត៌មាន​កម្មវិធី"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"បាន​ប្ដូរ​ឈ្មោះ​ថត​ជា <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g> ឬច្រើនជាងនេះ"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ធាតុ​ក្រាហ្វិក"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំង​រូបភាព"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"រចនាប័ទ្ម និង​ផ្ទាំង​រូបភាព"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ការកំណត់​ទំព័រដើម"</string>
@@ -101,15 +116,15 @@
     <string name="abandoned_search" msgid="891119232568284442">"ស្វែងរក"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"មិន​បាន​ដំឡើង​កម្មវិធី​នេះ"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"មិន​បាន​ដំឡើង​កម្មវិធី​សម្រាប់​រូបតំណាង​នេះ។ អ្នក​អាច​លុប​វា ឬ​ស្វែងរក​កម្មវិធី និង​ដំឡើង​វា​ដោយ​ដៃ។"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"កំពុង​ដំឡើង <xliff:g id="NAME">%1$s</xliff:g>, បាន​បញ្ចប់ <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"កំពុងដោនឡូត <xliff:g id="NAME">%1$s</xliff:g> បានបញ្ចប់ <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> កំពុងរង់ចាំការដំឡើង"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"ធាតុ​ក្រាហ្វិក <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"បញ្ជីធាតុ​ក្រាហ្វិក"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"បាន​បិទ​បញ្ជីធាតុ​ក្រាហ្វិក"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"បញ្ចូលទៅអេក្រង់ដើម"</string>
     <string name="action_move_here" msgid="2170188780612570250">"ផ្លាស់ធាតុមកទីនេះ"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"ធាតុដែលត្រូវបានបន្ថែមទៅអេក្រង់ដើម"</string>
-    <string name="item_removed" msgid="851119963877842327">"ធាតុដែលបានដកចេញ"</string>
+    <string name="item_removed" msgid="851119963877842327">"បានដកធាតុចេញ"</string>
     <string name="undo" msgid="4151576204245173321">"ត្រឡប់វិញ"</string>
     <string name="action_move" msgid="4339390619886385032">"ផ្លាស់ទីធាតុ"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"ផ្លាស់ទីទៅជួរដេកទី <xliff:g id="NUMBER_0">%1$s</xliff:g> ជួរឈរទី <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"បន្ទាប់"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"យល់ហើយ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"កម្រងព័ត៌មានការងារត្រូវបាន​ផ្អាក"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"កម្មវិធី​ការងារ​មិនអាចផ្ញើ​ការជូនដំណឹង​ទៅអ្នក ប្រើប្រាស់ថ្ម​របស់អ្នក ឬ​ចូលប្រើ​ទីតាំង​របស់អ្នក​បានទេ"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"កម្រងព័ត៌មាន​ការងារ​ត្រូវបាន​ផ្អាក។ កម្មវិធី​ការងារ​មិនអាច​ផ្ញើ​ការជូនដំណឹងទៅអ្នក ប្រើប្រាស់ថ្មរបស់អ្នក ឬចូលប្រើទីតាំង​របស់អ្នកបានទេ"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"កម្មវិធី​ការងារ​មិនអាចផ្ញើ​ការជូនដំណឹង​ទៅអ្នក ប្រើប្រាស់ថ្ម​របស់អ្នក ឬ​ចូលប្រើ​ទីតាំង​របស់អ្នក​បានទេ"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"កម្រងព័ត៌មាន​ការងារ​ត្រូវបាន​ផ្អាក។ កម្មវិធី​ការងារ​មិនអាចផ្ញើ​ការជូនដំណឹង​ទៅអ្នក ប្រើប្រាស់ថ្ម​របស់អ្នក ឬ​ចូលប្រើ​ទីតាំង​របស់អ្នក​បានទេ"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"កម្មវិធីការងារ​ត្រូវបានដាក់​គ្រឿងសម្គាល់ ហើយ​អ្នកគ្រប់គ្រង​ផ្នែកព័ត៌មានវិទ្យា​របស់អ្នក​អាចមើលឃើញ"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"យល់ហើយ"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ផ្អាក​កម្មវិធី​ការងារ"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"បើក"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"តម្រង"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ផ្អាក​ការជូនដំណឹង និង​កម្មវិធី​ការងារ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"បានបរាជ័យ៖ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 09c1152..b12c631 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾದ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ಶಾರ್ಟ್‌ಕಟ್ ಲಭ್ಯವಿಲ್ಲ"</string>
-    <string name="home_screen" msgid="806512411299847073">"ಮುಖಪುಟದ ಪರದೆ"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳು"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ವಿಜೆಟ್ ಅನ್ನು ಆರಿಸಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ &amp; ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ವಿಜೆಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ"</string>
+    <string name="home_screen" msgid="5629429142036709174">"ಹೋಮ್"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ಅಗಲ ಮತ್ತು %2$d ಎತ್ತರ"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ಹಸ್ತಚಾಲಿತವಾಗಿ ಸೇರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
     <string name="place_automatically" msgid="8064208734425456485">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸೇರಿಸಿ"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ಹುಡುಕಾಟ ಪೆಟ್ಟಿಗೆಯಿಂದ ಪಠ್ಯವನ್ನು ತೆರವುಗೊಳಿಸಿ"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ಯಾವುದೇ ವಿಜೆಟ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"ಯಾವುದೇ ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳಿಲ್ಲ"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ವೈಯಕ್ತಿಕ"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ಕೆಲಸ"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"ಸಂವಾದಗಳು"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ"</string>
     <string name="label_application" msgid="8531721983832654978">"ಆ್ಯಪ್"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ಅಧಿಸೂಚನೆಗಳು"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ಶಾರ್ಟ್‌ಕಟ್ ಸರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ಶಾರ್ಟ್‌ಕಟ್ ಸರಿಸಲು ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಲು ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
     <string name="out_of_space" msgid="4691004494942118364">"ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ವೈಯಕ್ತಿಕ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ಮುಖಪುಟ"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ತೆಗೆದುಹಾಕಿ"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ಫೋಲ್ಡರ್‌ ಅನ್ನು <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಮರುಹೆಸರಿಸಲಾಗಿದೆ"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಐಟಂಗಳು"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಅಥವಾ ಹೆಚ್ಚಿನ ಐಟಂಗಳು"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ಶೈಲಿಗಳು &amp; ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ಈ ಐಕಾನ್ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ. ನೀವು ಅದನ್ನು ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಬಹುದು ಮತ್ತು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅದನ್ನು ಸ್ಥಾಪಿಸಬಹುದು."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ಡೌನ್‌ಲೋಡ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ಸ್ಥಾಪಿಸಲು ಕಾಯಲಾಗುತ್ತಿದೆ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="widgets_list" msgid="796804551140113767">"ವಿಜೆಟ್ ಪಟ್ಟಿ"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ವಿಜೆಟ್ ಪಟ್ಟಿಯನ್ನು ಮುಚ್ಚಲಾಗಿದೆ"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸಿ"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ಮುಂದೆ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ಸರಿ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್‌ಗಳಿಗೆ ನಿಮಗೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ಕಳುಹಿಸಲು, ನಿಮ್ಮ ಬ್ಯಾಟರಿಯನ್ನು ಬಳಸಲು ಅಥವಾ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ. ನಿಮಗೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ಕಳುಹಿಸಲು, ನಿಮ್ಮ ಬ್ಯಾಟರಿ ಬಳಸಲು ಅಥವಾ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್‌ಗಳಿಗೆ ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್‌ಗಳಿಗೆ ನಿಮಗೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ಕಳುಹಿಸಲು, ನಿಮ್ಮ ಬ್ಯಾಟರಿಯನ್ನು ಬಳಸಲು ಅಥವಾ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ. ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್‌ಗಳಿಗೆ ನಿಮಗೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ಕಳುಹಿಸಲು, ನಿಮ್ಮ ಬ್ಯಾಟರಿಯನ್ನು ಬಳಸಲು ಅಥವಾ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್‌ಗಳು ಬ್ಯಾಡ್ಜ್ ಮಾಡಲಾಗಿದೆ ಮತ್ತು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರಿಗೆ ಗೋಚರಿಸುತ್ತದೆ"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ಅರ್ಥವಾಯಿತು"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್‌ಗಳನ್ನು ವಿರಾಮಗೊಳಿಸಿ"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ಆನ್ ಮಾಡಿ"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ಫಿಲ್ಟರ್‌"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಗಳನ್ನು ವಿರಾಮಗೊಳಿಸಿ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ವಿಫಲವಾಗಿದೆ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index aaf6dff..81285ca 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"다운로드한 앱은 안전 모드에서 사용할 수 없습니다."</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"안전 모드에서 위젯 사용 중지됨"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"바로가기를 사용할 수 없음"</string>
-    <string name="home_screen" msgid="806512411299847073">"홈 화면"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"맞춤 작업"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"위젯을 선택하려면 길게 터치하세요."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"위젯을 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다."</string>
+    <string name="home_screen" msgid="5629429142036709174">"홈"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"화면 분할"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"너비 %1$d, 높이 %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"길게 터치하여 직접 추가하세요."</string>
     <string name="place_automatically" msgid="8064208734425456485">"자동으로 추가"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"검색창에서 텍스트 지우기"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"사용 가능한 위젯 없음"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"검색결과 없음"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"개인 위젯"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"직장 위젯"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"대화"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"앱 검색"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"앱 로드 중…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\'<xliff:g id="QUERY">%1$s</xliff:g>\'과(와) 일치하는 앱이 없습니다."</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"더 많은 앱 검색"</string>
     <string name="label_application" msgid="8531721983832654978">"앱"</string>
     <string name="notifications_header" msgid="1404149926117359025">"알림"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"바로가기를 선택하려면 길게 터치하세요."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"바로가기를 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 동작을 사용하세요."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"길게 터치하여 바로가기를 이동하세요."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"두 번 탭한 다음 길게 터치하여 바로가기를 이동하거나 맞춤 작업을 사용하세요."</string>
     <string name="out_of_space" msgid="4691004494942118364">"홈 화면에 더 이상 공간이 없습니다."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"즐겨찾기 트레이에 더 이상 공간이 없습니다."</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"앱 목록"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"개인 앱 목록"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"업무용 앱 목록"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"홈"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"삭제"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"제거"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"앱 정보"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"폴더 이름 변경: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개 이상"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"위젯"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"스타일 및 배경화면"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"홈 설정"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"검색"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"이 앱이 설치되어 있지 않음"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"이 아이콘의 앱이 설치되어 있지 않습니다. 아이콘을 삭제하거나 앱을 검색하여 수동으로 설치하세요."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> 설치 중, <xliff:g id="PROGRESS">%2$s</xliff:g> 완료"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> 다운로드 중, <xliff:g id="PROGRESS">%2$s</xliff:g> 완료"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> 설치 대기 중"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> 위젯"</string>
     <string name="widgets_list" msgid="796804551140113767">"위젯 목록"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"위젯 목록 닫힘"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"홈 화면에 추가"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"다음"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"확인"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"직장 프로필이 일시중지됨"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"직장 앱에서 알림을 보내거나 배터리를 사용하거나 위치 정보에 액세스할 수 없습니다."</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"직장 프로필이 일시중지되었습니다. 직장 앱에서 알림을 보내거나 배터리를 사용하거나 위치 정보에 액세스할 수 없습니다."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"직장 앱에서 알림을 보내거나 배터리를 사용하거나 내 위치 정보에 액세스할 수 없습니다."</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"직장 프로필이 일시중지되었습니다. 직장 앱에서 알림을 보내거나 배터리를 사용하거나 내 위치 정보에 액세스할 수 없습니다."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"직장 앱에는 배지가 있으며, IT 관리자는 직장 앱을 확인할 수 있습니다."</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"확인"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"직장 앱 일시중지"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"사용 설정"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"필터"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"직장 앱 및 알림 일시중지"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"실패: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index d4b4c5d..50c36d5 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -26,29 +26,45 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Жүктөп алынган колдонмо Коопсуз режиминде иштен чыгарылды"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Виджеттер Коопсуз режимде өчүрүлгөн"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Кыска жол жок"</string>
-    <string name="home_screen" msgid="806512411299847073">"Башкы экран"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Ыңгайлаштырылган аракеттер"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Виджетти тандаш үчүн, басып туруңуз"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджет тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Башкы экран"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлүү"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Туурасы: %1$d, бийиктиги: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Кол менен кошуу үчүн кое бербей басып туруңуз"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Автоматтык түрдө кошуу"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Текстти издөө кутучасынан тазалоо"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Виджет жок"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Издөө жыйынтыктары жок"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Жеке"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Жумуш"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Сүйлөшүүлөр"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Колдонмолорду издөө"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Колдонмолор жүктөлүүдө…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" сурамына дал келген колдонмолор табылган жок"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Көбүрөөк колдонмолорду издөө"</string>
     <string name="label_application" msgid="8531721983832654978">"Колдонмо"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Билдирмелер"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Кыска жолду тандоо үчүн басып туруңуз."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Кыска жолду тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Ыкчам баскычты жылдыруу үчүн коё бербей басып туруңуз."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Ыкчам баскычты жылдыруу үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
     <string name="out_of_space" msgid="4691004494942118364">"Бул Үй экранында бош орун жок."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Тандамалдар тайпасында орун калган жок"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Колдонмолор тизмеси"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Жеке колдономолордун тизмеси"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Жумуш колдонмолорунун тизмеси"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Үйгө"</string>
-    <string name="remove_drop_target_label" msgid="7812859488053230776">"Алып салуу"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Өчүрүү"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Чыгарып салуу"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Колдонмо тууралуу"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Орнотуу"</string>
@@ -80,13 +96,12 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Фолдердин аты <xliff:g id="NAME">%1$s</xliff:g> деп өзгөртүлдү"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g> же андан көбүрөөк"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилдер жана тушкагаздар"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Башкы бет жөндөөлөрү"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Башкы экранды айлантууга уруксат берүү"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон айланганда"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Башкы экранды бурууга уруксат берүү"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон бурулганда"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"Билдирмелер белгилери"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"Күйүк"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"Өчүк"</string>
@@ -97,13 +112,13 @@
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Колдонмонун сүрөтчөсүн Башкы экранга кошуу"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңы колдонмолор үчүн"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып салуу"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"Өчүрүү"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Издөө"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Бул колдонмо орнотулган эмес"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Бул сүрөтчөнүн колдонмосу орнотулган эмес. Аны алып салсаңыз же колдонмону издеп, кол менен орнотсоңуз болот."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> орнотулууда, <xliff:g id="PROGRESS">%2$s</xliff:g> аткарылды"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> жүктөлүп алынууда, <xliff:g id="PROGRESS">%2$s</xliff:g> аяктады"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> орнотулушу күтүлүүдө"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> виджеттери"</string>
     <string name="widgets_list" msgid="796804551140113767">"Виджеттердин тизмеси"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Виджеттердин тизмеси жабык"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Башкы экранга кошуу"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Кийинки"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Түшүндүм"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Жумуш профили тындырылган"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Жумуш колдонмолору билдирмелерди жөнөтүп, түзмөгүңүздүн батареясын керектеп же кайда жүргөнүңүздү көрө албайт"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Жумуш профили тындырылган. Жумуш колдонмолору билдирмелерди жөнөтүп, түзмөгүңүздүн батареясын керектеп же кайда жүргөнүңүздү көрө албайт"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Жумуш колдонмолору билдирмелерди жөнөтүп, түзмөгүңүздүн батареясын керектеп же кайда жүргөнүңүздү көрө албайт"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Жумуш профили тындырылган. Жумуш колдонмолору билдирмелерди жөнөтүп, түзмөгүңүздүн батареясын керектеп же кайда жүргөнүңүздү көрө албайт"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Жумуш колдонмолору белгиленип, аларды IT администраторлору көрөт"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Түшүндүм"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Жумуш колдонмолорун тындыруу"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Күйгүзүү"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Чыпкалоо"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Жумуш колдонмолорун жана билдирмелерди тындыруу"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Аткарылган жок: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index b8ca8a4..3ce2c20 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"​ວິດ​ເຈັດ​ຖືກ​ປິດ​ໃນ Safe mode"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ບໍ່ສາມາດໃຊ້ທາງລັດໄດ້"</string>
-    <string name="home_screen" msgid="806512411299847073">"ໜ້າຈໍຫຼັກ"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"ຄຳສັ່ງແບບກຳນົດເອງ"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ສຳພັດຄ້າງໄວ້ ເພື່ອຈັບວິດເຈັດ."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ແຕະ​ຄ້າງ​ໄວ້ ເພື່ອ​ເລືອກວິດ​ເຈັດ ຫຼື ໃຊ້​ການ​ດຳ​ເນີນ​ການ​ກຳ​ນົດ​ເອງ."</string>
+    <string name="home_screen" msgid="5629429142036709174">"ໂຮມສະກຣີນ"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ແບ່ງໜ້າຈໍ"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ກວ້າງ %1$d ຄູນສູງ %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ແຕະຄ້າງໄວ້ເພື່ອວາງດ້ວຍຕົນເອງ"</string>
     <string name="place_automatically" msgid="8064208734425456485">"ເພີ່ມໂດຍອັດຕະໂນມັດ"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ລຶບລ້າງຂໍ້ຄວາມຈາກກ່ອງຊອກຫາ"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ບໍ່ມີວິດເຈັດທີ່ສາມາດໃຊ້ໄດ້"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"ບໍ່​ມີ​ຜົນ​​ການ​ຊອກຫາ​"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ສ່ວນຕົວ"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ວຽກ"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"ການສົນທະນາ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ຊອກຫາແອັບ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ກໍາລັງໂຫຼດແອັບ…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"ບໍ່ພົບແອັບທີ່ກົງກັບ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ຊອກຫາແອັບເພີ່ມເຕີມ"</string>
     <string name="label_application" msgid="8531721983832654978">"ແອັບ"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ການແຈ້ງເຕືອນ"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ແຕະຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ ຫຼື ໃຊ້ຄຳສັ່ງແບບກຳນົດເອງ."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ແຕະຄ້າງໄວ້ເພື່ອຍ້າຍທາງລັດ."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຍ້າຍທາງລັດ ຫຼື ໃຊ້ຄຳສັ່ງກຳນົດເອງ."</string>
     <string name="out_of_space" msgid="4691004494942118364">"ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"ລາຍຊື່ແອັບ"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ລາຍຊື່ແອັບສ່ວນຕົວ"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"ລາຍຊື່ແອັບເຮັດວຽກ"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ໜ້າຫຼັກ"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ເອົາ​ອອກ"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ຖອນ​ການ​ຕິດ​ຕັ້ງ"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ຂໍ້ມູນແອັບ"</string>
@@ -80,10 +96,9 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ປ່ຽນຊື່ໂຟນເດີເປັນ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ລາຍການ"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ຫຼື ລາຍການເພີ່ມເຕີມ"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ຮູບແບບ ແລະ ຮູບພື້ນຫຼັງ"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າ Home"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າໜ້າຫຼັກ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍຢູ່ໜ້າຫຼັກໄດ້"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ເມື່ອໝຸນໂທລະສັບ"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"ຊອກຫາ"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"ກຳລັງຕິດຕັ້ງ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> ສຳເລັດແລ້ວ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ກຳ​ລັງ​ດາວ​ໂຫຼດ, <xliff:g id="PROGRESS">%2$s</xliff:g> ສຳ​ເລັດ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ກຳ​ລັງ​ລໍ​ຖ້າ​ຕິດ​ຕັ້ງ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"ວິດເຈັດ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"ລາຍຊື່ວິດເຈັດ"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ປິດລາຍຊື່ວິດເຈັດແລ້ວ"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ຕໍ່ໄປ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ເຂົ້າໃຈແລ້ວ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ຢຸດໂປຣໄຟລ໌ວຽກໄວ້ຊົ່ວຄາວແລ້ວ"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"ແອັບວຽກບໍ່ສາມາດສົ່ງການແຈ້ງເຕືອນໃຫ້ທ່ານ, ໃຊ້ແບັດເຕີຣີຂອງທ່ານ ຫຼື ເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ຢຸດໂປຣໄຟລ໌ບ່ອນເຮັດວຽກໄວ້ຊົ່ວຄາວແລ້ວ. ແອັບວຽກຈະບໍ່ສາມາດສົ່ງການແຈ້ງເຕືອນໃຫ້ທ່ານ, ໃຊ້ແບັດເຕີຣີຂອງທ່ານ ຫຼື ເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"ແອັບບ່ອນເຮັດວຽກຈະບໍ່ສາມາດສົ່ງການແຈ້ງເຕືອນຫາທ່ານ, ໃຊ້ແບັດເຕີຣີຂອງທ່ານ ຫຼື ເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"ຢຸດໂປຣໄຟລ໌ບ່ອນເຮັດວຽກໄວ້ຊົ່ວຄາວແລ້ວ. ແອັບບ່ອນເຮັດວຽກຈະບໍ່ສາມາດສົ່ງການແຈ້ງເຕືອນຫາທ່ານ, ໃຊ້ແບັດເຕີຣີຂອງທ່ານ ຫຼື ເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ແອັບບ່ອນເຮັດວຽກແມ່ນຖືກຕິດປ້າຍ ແລະ ສະແດງໃຫ້ຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານເຫັນ"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ເຂົ້າໃຈແລ້ວ"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ຢຸດແອັບບ່ອນເຮັດວຽກຊົ່ວຄາວ"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ເປີດໃຊ້"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ກັ່ນຕອງ"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ຢຸດແອັບວຽກ ແລະ ການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ບໍ່ສຳເລັດ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index d959788..c75afbc 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -26,28 +26,48 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Atsisiųsta programa išjungta Saugos režimu"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Valdikliai išjungti Saugiame režime"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Sparčiojo klavišo negalima naudoti"</string>
-    <string name="home_screen" msgid="806512411299847073">"Pagrindinis ekranas"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Tinkinti veiksmai"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Palieskite ir laikykite, kad pasirinkt. valdiklį."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dukart palieskite ir laikykite, kad pasirinktumėte valdiklį ar naudotumėte tinkintus veiksmus."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plotis ir %2$d aukštis"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Palieskite ir palaikykite, kad padėtumėte patys"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Pridėti automatiškai"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Išvalyti tekstą iš paieškos laukelio"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nėra jokių pasiekiamų valdiklių"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nėra jokių paieškos rezultatų"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Asmeniniai"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Darbas"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Pokalbiai"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Paieškos programos"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Įkeliamos programos…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Pranešimai"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Paliesk. ir palaikyk., kad pasirinkt. spart. klav."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dukart palieskite ir palaikykite, kad pasirinkt. spartųjį klavišą ar naudotumėte tinkintus veiksmus."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Šiame pagrindiniame ekrane vietos nebėra."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Mėgstamiausių dėkle nebėra vietos"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Programų sąrašas"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Asmeninių programų sąrašas"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Darbo programų sąrašas"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Pagrindinis"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Ištrinti"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Pašalinti"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Programos inform."</string>
@@ -82,7 +102,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Aplankas pervardytas kaip „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“, elementų: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“, elementų: <xliff:g id="SIZE">%2$d</xliff:g> ar daugiau"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Valdikliai"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiliai ir ekrano fonai"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"„Home“ nustatymai"</string>
@@ -103,9 +122,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Ieškoti"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ši programa neįdiegta"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Šios piktogramos programa neįdiegta. Galite ją pašalinti arba bandyti ieškoti programos ir ją įdiegti patys."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Įdiegiama: „<xliff:g id="NAME">%1$s</xliff:g>“; baigta: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Atsisiunčiama programa „<xliff:g id="NAME">%1$s</xliff:g>“, <xliff:g id="PROGRESS">%2$s</xliff:g> baigta"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Laukiama, kol bus įdiegta programa „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"„<xliff:g id="NAME">%1$s</xliff:g>“ valdikliai"</string>
     <string name="widgets_list" msgid="796804551140113767">"Valdiklių sąrašas"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Valdiklių sąrašas uždarytas"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Pridėti prie pagrind. ekrano"</string>
@@ -143,8 +162,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Kitas"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Supratau"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Darbo profilis pristabdytas"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Darbo programos negali siųsti jums pranešimų, naudoti jūsų akumuliatoriaus ar pasiekti jūsų vietovės"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Darbo profilis pristabdytas. Darbo programos negali siųsti jums pranešimų, naudoti jūsų akumuliatoriaus ar pasiekti jūsų vietovės"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Darbo programos negali siųsti jums pranešimų, naudoti jūsų akumuliatoriaus ar pasiekti jūsų vietovės"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Darbo profilis pristabdytas. Darbo programos negali siųsti jums pranešimų, naudoti jūsų akumuliatoriaus ar pasiekti jūsų vietovės"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Darbo programos yra pažymėtos ženkleliu ir matomos IT administratoriui"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Supratau"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pristabdyti darbo programas"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Įjungti"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruoti"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pristabdykite darbo programas ir pranešimus"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Nepavyko: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index aa01d6d..dc2117f 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -26,28 +26,46 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Lejupielādētā lietotne ir atspējota drošajā režīmā."</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Logrīki atspējoti drošajā režīmā"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Saīsne nav pieejama."</string>
-    <string name="home_screen" msgid="806512411299847073">"Sākuma ekrāns"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Pielāgotās darbības"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Lai izvēlētos logrīku, pieskarieties un turiet to."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Lai atlasītu logrīku, veiciet dubultskārienu uz tā un turiet to vai arī veiciet pielāgotas darbības."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plats un %2$d augsts"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Pieskarieties un turiet, lai manuāli pievienotu"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Pievienot automātiski"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Notīrīt tekstu no meklēšanas lodziņa"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nav pieejams neviens logrīks"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nav meklēšanas rezultātu"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personīgie"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Darba"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Sarunas"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Meklēt lietotnes"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Notiek lietotņu ielāde…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Paziņojumi"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Lai atlasītu saīsni, pieskarieties un turiet to."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Lai atlasītu saīsni, veiciet dubultskārienu uz tās un turiet to. Varat arī veikt pielāgotas darbības."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Šajā sākuma ekrānā vairs nav vietas."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Izlases joslā vairs nav vietas."</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lietotņu saraksts"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personīgo lietotņu saraksts"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Darba lietotņu saraksts"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Sākums"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Noņemt"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Atinstalēt"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Par lietotni"</string>
@@ -81,7 +99,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Mape pārdēvēta par: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mape <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vienumi"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mape <xliff:g id="NAME">%1$s</xliff:g>, vienumu skaits mapē: vismaz <xliff:g id="SIZE">%2$d</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Logrīki"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stili un fona tapetes"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Sākumlapas iestatījumi"</string>
@@ -102,9 +119,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Meklēt"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Šī lietotne nav instalēta"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Šai ikonai paredzētā lietotne nav instalēta. Varat noņemt ikonu vai meklēt lietotni un instalēt to manuāli."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Notiek lietotnes “<xliff:g id="NAME">%1$s</xliff:g>” instalēšana. Norise: <xliff:g id="PROGRESS">%2$s</xliff:g>."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Lietotnes <xliff:g id="NAME">%1$s</xliff:g> lejupielāde (<xliff:g id="PROGRESS">%2$s</xliff:g> pabeigti)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Notiek <xliff:g id="NAME">%1$s</xliff:g> instalēšana"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> logrīki"</string>
     <string name="widgets_list" msgid="796804551140113767">"Logrīku saraksts"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Logrīku saraksts aizvērts"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Pievienot sākuma ekrānam"</string>
@@ -142,8 +159,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Tālāk"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Labi"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Darba profila darbība ir pārtraukta"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Darba lietotnes nevar nosūtīt jums paziņojumus, izmantot akumulatoru un piekļūt jūsu atrašanās vietai."</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Darba profila darbība ir pārtraukta. Darba lietotnes nevar sūtīt jums paziņojumus, izmantot akumulatoru un piekļūt jūsu atrašanās vietai."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Darba lietotnes nevar sūtīt jums paziņojumus, izmantot akumulatoru un piekļūt jūsu atrašanās vietai."</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Darba profila darbība ir pārtraukta. Darba lietotnes nevar sūtīt jums paziņojumus, izmantot akumulatoru un piekļūt jūsu atrašanās vietai."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Darba lietotnēm ir pievienota emblēma, un tās ir redzamas jūsu IT administratoram"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Labi"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pārtraukt darba lietotņu darbību"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Ieslēgt"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrs"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pārtraukt darba lietotņu darbību un paziņojumu sūtīšanu"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Neizdevās: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 82ffdd5..ce79bde 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Преземената апликација е оневозможена во безбеден режим"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Додатоците се оневозможени во безбеден режим"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Кратенката не е достапна"</string>
-    <string name="home_screen" msgid="806512411299847073">"Почетен екран"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Приспособени дејства"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Допри и задржи за да се избере виџетот."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Допрете двапати и задржете за да изберете додаток или да користите приспособени дејства."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Почетен екран"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Поделен екран"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d широк на %2$d висок"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Допрете и задржете за рачно поставување"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Додај автоматски"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Избриши го текстот од полето за пребарување"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Нема достапни виџети"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Нема резултати од пребарувањето"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Лични"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Работни"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Разговори"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Пребарувајте апликации"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Се вчитуваат апликации…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Не се најдени апликации што одговараат на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Пребарај други апликации"</string>
     <string name="label_application" msgid="8531721983832654978">"Апликација"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Известувања"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Допрете двапати и задржете за избор на кратенка."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Допрете двапати и задржете за избор на кратенка или користете приспособени дејства."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Допрете и задржете за да преместите кратенка."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Допрете двапати и задржете за да преместите кратенка или користете приспособени дејства."</string>
     <string name="out_of_space" msgid="4691004494942118364">"Нема повеќе простор на овој екран на почетната страница."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема повеќе простор на лентата „Омилени“"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Список со апликации"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Список со лични апликации"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Список со апликации за работа"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна страница"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Отстрани"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Инф. за апликација"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана во <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставки"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или повеќе ставки"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилови и тапети"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Поставки за почетен екран"</string>
@@ -94,19 +109,19 @@
     <string name="msg_missing_notification_access" msgid="281113995110910548">"За да се прикажуваат „Точки за известување“, вклучете ги известувањата за апликацијата <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Промени ги поставките"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"Прикажувај точки за известување"</string>
-    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Додавај икони за апликации на почетен екран"</string>
+    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Додавај икони за апликации на почетниот екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови апликации"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Отстрани"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Барај"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Апликацијата не е инсталирана"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Апликацијата за оваа икона не е инсталирана. Може да ја отстраните или да се обидете да ја најдете апликацијата и да ја инсталирате рачно."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> завршено"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Се презема <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> завршено"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чека да се инсталира"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Виџети за <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Список со виџети"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Списокот со виџети е затворен"</string>
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"Додај на Почетен екран"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Додај на почетниот екран"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Премести ја ставката овде"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Ставката е додадена на почетниот екран"</string>
     <string name="item_removed" msgid="851119963877842327">"Ставката е отстранета"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Следно"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Сфатив"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Работниот профил е паузиран"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Работните апликации не можат да ви испраќаат известувања, да ја користат батеријата или да пристапуваат до вашата локација"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Работниот профил е паузиран. Работните апликации не можат да ви испраќаат известувања, да ја користат батеријата или да пристапуваат до вашата локација"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Работните апликации не можат да ви испраќаат известувања, да ја користат батеријата или да пристапуваат до вашата локација"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Работниот профил е паузиран. Работните апликации не можат да ви испраќаат известувања, да ја користат батеријата или да пристапуваат до вашата локација"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Работните апликации имаат значка и се видливи за IT-администраторот"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Сфатив"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Паузирај ги работните апликации"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Вклучи"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Паузирајте работни апликации и известувања"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не успеа: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 8156318..3ab0b35 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ഡൗൺലോഡുചെയ്‌ത അപ്ലിക്കേഷൻ സുരക്ഷാ മോഡിൽ പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"സുരക്ഷിത മോഡിൽ വിജറ്റുകൾ പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"കുറുക്കുവഴി ലഭ്യമല്ല"</string>
-    <string name="home_screen" msgid="806512411299847073">"ഹോം സ്‌ക്രീൻ"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"ഇഷ്‌ടാനുസൃത പ്രവർത്തനങ്ങൾ"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ഒരു വിജറ്റ് ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"വിജറ്റ് തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</string>
+    <string name="home_screen" msgid="5629429142036709174">"ഹോം"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"സ്‌ക്രീൻ വിഭജന മോഡ്"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d വീതിയും %2$d ഉയരവും"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"നേരിട്ട് സ്ഥാപിക്കുന്നതിന് സ്‌പർശിച്ചുപിടിക്കുക"</string>
     <string name="place_automatically" msgid="8064208734425456485">"സ്വയമേവ ചേർക്കുക"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"സെർച്ച് ബോക്‌സിൽ നിന്ന് ടെക്‌സ്‌റ്റ് മായ്‌ക്കുക"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ഒരു വിജറ്റുകളും ലഭ്യമല്ല"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"തിരയൽ ഫലങ്ങളൊന്നുമില്ല"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"വ്യക്തിപരം"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ജോലി"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"സംഭാഷണങ്ങൾ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ആപ്പുകൾ തിരയുക"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ആപ്പുകൾ ലോഡുചെയ്യുന്നു..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പുകളൊന്നും കണ്ടെത്തിയില്ല"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക"</string>
     <string name="label_application" msgid="8531721983832654978">"ആപ്പ്"</string>
     <string name="notifications_header" msgid="1404149926117359025">"അറിയിപ്പുകൾ"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"തിരഞ്ഞെടുക്കുന്നതിന് കുറുക്കുവഴി സ്‌പർശിച്ച് പിടിക്കുക."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"കുറുക്കുവഴി തിരഞ്ഞെടുക്കാനോ ഇഷ്‌ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ 2 തവണ ടാപ്പ് ചെയ്‌ത് പിടിക്കുക."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"കുറുക്കുവഴി നീക്കാൻ സ്‌പർശിച്ച് പിടിക്കുക."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"കുറുക്കുവഴി നീക്കാൻ ഡബിൾ ടാപ്പ് ചെയ്യൂ, ഹോൾഡ് ചെയ്യൂ അല്ലെങ്കിൽ ഇഷ്‌ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കൂ."</string>
     <string name="out_of_space" msgid="4691004494942118364">"ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"അപ്ലിക്കേഷനുകളുടെ ലിസ്‌റ്റ്"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"വ്യക്തിഗത ആപ്പുകളുടെ ലിസ്റ്റ്"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"ഔദ്യോഗിക ആപ്പുകളുടെ ലിസ്റ്റ്"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ഹോം"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"നീക്കംചെയ്യുക"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ആപ്പ് വിവരങ്ങൾ"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ഫോൾഡറിന്റെ പേര് <xliff:g id="NAME">%1$s</xliff:g> എന്നായി മാറ്റി"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ഇനങ്ങൾ"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> അല്ലെങ്കിൽ അതിലധികം ഇനങ്ങൾ"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"വിഡ്ജെറ്റുകൾ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"സ്‌റ്റൈലുകളും വാൾപേപ്പറുകളും"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ഹോം ക്രമീകരണം"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വമേധയാ ഇൻസ്റ്റാളുചെയ്യുക."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ഇൻസ്‌റ്റാൾ ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ഡൗൺലോഡ് ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"ഇൻസ്റ്റാൾ ചെയ്യാൻ <xliff:g id="NAME">%1$s</xliff:g> കാക്കുന്നു"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> വിജറ്റുകൾ"</string>
     <string name="widgets_list" msgid="796804551140113767">"വിജറ്റുകളുടെ ലിസ്‌റ്റ്"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"വിജറ്റുകളുടെ ലിസ്‌റ്റ് അവസാനിപ്പിച്ചു"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ഹോം സ്ക്രീനിൽ ചേർക്കുക"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"അടുത്തത്"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"മനസ്സിലായി"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ഔദ്യോഗിക പ്രൊഫൈൽ തൽക്കാലം നിർത്തിയിരിക്കുന്നു"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"നിങ്ങൾക്ക് അറിയിപ്പുകൾ അയയ്ക്കാനോ നിങ്ങളുടെ ബാറ്ററി ഉപയോഗിക്കാനോ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാനോ ഔദ്യോഗിക ആപ്പുകൾക്ക് കഴിയില്ല"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ഔദ്യോഗിക പ്രൊഫൈൽ തൽക്കാലം നിർത്തിയിരിക്കുന്നു. നിങ്ങൾക്ക് അറിയിപ്പുകൾ അയയ്ക്കാനോ നിങ്ങളുടെ ബാറ്ററി ഉപയോഗിക്കാനോ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാനോ ഔദ്യോഗിക ആപ്പുകൾക്ക് കഴിയില്ല"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"നിങ്ങൾക്ക് അറിയിപ്പുകൾ അയയ്ക്കാനോ ബാറ്ററി ഉപയോഗിക്കാനോ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാനോ ഔദ്യോഗിക ആപ്പുകൾക്ക് കഴിയില്ല"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"ഔദ്യോഗിക പ്രൊഫൈൽ താൽക്കാലികമായി നിർത്തിയിരിക്കുന്നു. നിങ്ങൾക്ക് അറിയിപ്പുകൾ അയയ്ക്കാനോ ബാറ്ററി ഉപയോഗിക്കാനോ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാനോ ഔദ്യോഗിക ആപ്പുകൾക്ക് കഴിയില്ല"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ഔദ്യോഗിക ആപ്പുകൾക്ക് ബാഡ്‌ജ് നൽകിയിരിക്കുന്നു, അവ നിങ്ങളുടെ ഐടി അഡ്‌മിന് കാണാനും കഴിയും"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"മനസ്സിലായി"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ഔദ്യോഗിക ആപ്പുകൾ താൽക്കാലികമായി നിർത്തുക"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ഓണാക്കുക"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ഫിൽട്ടർ ചെയ്യുക"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ഔദ്യോഗിക ആപ്പുകളും അറിയിപ്പുകളും താൽക്കാലികമായി നിർത്തുക"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"പരാജയപ്പെട്ടു: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 1d680ab..b93c535 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Татаж авсан апп-г Аюулгүй горим дотроос идэвхгүйжүүлсэн"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Safe горимд виджетүүдийг идэвхгүйжүүлсэн"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Товчлол алга"</string>
-    <string name="home_screen" msgid="806512411299847073">"Үндсэн нүүр"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Захиалгат үйлдэл"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Виджетийг авах бол хүрээд барина уу."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Жижиг хэрэгсэл авах болон тохируулсан үйлдлийг ашиглахын тулд 2 удаа товшоод барина уу."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Нүүр"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Дэлгэцийг хуваах"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d өргөн %2$d өндөр"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Гараар байршуулахын тулд дараад хүлээнэ үү"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Автоматаар нэмэх"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Хайх хэсгээс текстийг арилгах"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Боломжтой жижиг хэрэгсэл байхгүй"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Хайлтын илэрц байхгүй"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Хувийн"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Ажил"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Харилцан яриа"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Апп хайх"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Аппыг ачаалж байна..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-д тохирох апп олдсонгүй"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Бусад апп-г хайх"</string>
     <string name="label_application" msgid="8531721983832654978">"Апп"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Мэдэгдэл"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Товчлол авах бол удаан дарна уу."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Товчлол авах эсвэл тохируулсан үйлдлийг ашиглахын тулд давхар товшоод хүлээнэ үү."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Товчлолыг зөөхийн тулд хүрээд, удаан дарна уу."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Товчлолыг зөөх эсвэл захиалгат үйлдлийг ашиглахын тулд хоёр товшоод, удаан дарна уу."</string>
     <string name="out_of_space" msgid="4691004494942118364">"Энэ Нүүр дэлгэц зайгүй."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"\"Дуртай\" трей дээр өөр зай байхгүй байна"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Апп-н жагсаалт"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Хувийн аппын жагсаалт"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Ажлын аппын жагсаалт"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Нүүр"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Арилгах"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Устгах"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Аппын мэдээлэл"</string>
@@ -80,9 +96,8 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Фолдерын нэр <xliff:g id="NAME">%1$s</xliff:g> болов"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> зүйл"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> эсвэл үүнээс олон зүйл"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
-    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Загвар ба ханын зураг"</string>
+    <string name="wallpaper_button_text" msgid="8404103075899945851">"Дэлгэцийн зураг"</string>
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Загвар ба дэлгэцийн зураг"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Нүүр хуудасны тохиргоо"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Нүүр дэлгэцийг эргүүлэхийг зөвшөөрөх"</string>
@@ -101,15 +116,15 @@
     <string name="abandoned_search" msgid="891119232568284442">"Хайх"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Энэ апп-г суулгаагүй байна"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Энэ дүрсний апп-г суулгаагүй байна. Та үүнийг устгах буюу апп-г хайж суулгах боломжтой."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g>-г суулгаж байна. <xliff:g id="PROGRESS">%2$s</xliff:g> дууссан"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>-г татаж байна, <xliff:g id="PROGRESS">%2$s</xliff:g> татсан"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> нь суулгахыг хүлээж байна"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> жижиг хэрэгсэл"</string>
     <string name="widgets_list" msgid="796804551140113767">"Жижиг хэрэгслийн жагсаалт"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Жижиг хэрэгслийн жагсаалтыг хаасан"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Нүүр дэлгэцэд нэмэх"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Энд байршуулах"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Нүүр дэлгэцэнд нэмсэн зүйл"</string>
-    <string name="item_removed" msgid="851119963877842327">"Арилгасан зүйл"</string>
+    <string name="item_removed" msgid="851119963877842327">"Зүйлийг устгалаа"</string>
     <string name="undo" msgid="4151576204245173321">"Болих"</string>
     <string name="action_move" msgid="4339390619886385032">"Зөөх"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> мөр <xliff:g id="NUMBER_1">%2$s</xliff:g> баганад зөөх"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Дараах"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ойлголоо"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Ажлын профайлыг түр зогсоосон"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Ажлын аппууд танд мэдэгдэл илгээх, таны батарейг ашиглах эсвэл байршилд тань хандах боломжгүй"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Ажлын профайлыг түр зогсоосон. Ажлын апп танд мэдэгдэл илгээх боломжгүй тул батарейгаа ашиглах эсвэл байршилдаа хандана уу"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Ажлын аппууд танд мэдэгдэл илгээх, таны батарейг ашиглах эсвэл байршилд тань хандах боломжгүй"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Ажлын профайлыг түр зогсоосон. Ажлын аппууд танд мэдэгдэл илгээх, таны батарейг ашиглах эсвэл байршилд тань хандах боломжгүй"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Ажлын аппуудыг тэмдэглэсэн бөгөөд танай IT админд харагдана"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ойлголоо"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Ажлын аппуудыг түр зогсоох"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Асаах"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Шүүлтүүр"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Ажлын апп болон мэдэгдлийг түр зогсоох"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Амжилтгүй болсон: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 6f50f05..7fc330e 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"विजेट सुरक्षित मोडमध्ये अक्षम झाले"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"शॉर्टकट उपलब्ध नाही"</string>
-    <string name="home_screen" msgid="806512411299847073">"होम स्क्रीन"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"कस्टम क्रिया"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"एक विजेट निवडण्यासाठी दोनदा टॅप करा आणि धरून ठेवा किंवा कस्टम क्रिया वापरा."</string>
+    <string name="home_screen" msgid="5629429142036709174">"होम"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d रूंद बाय %2$d उंच"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा"</string>
     <string name="place_automatically" msgid="8064208734425456485">"आपोआप जोडा"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"सर्च बॉक्समधून मजकूर साफ करा"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"कोणतीही विजेट उपलब्ध नाहीत"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"कोणतेही शोध परिणाम नाहीत"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"वैयक्तिक"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ऑफिस"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"संभाषणे"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"अ‍ॅप्स शोधा"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"अ‍ॅप्स लोड करत आहे…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अ‍ॅप्स आढळले नाहीत"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"अधिक अ‍ॅप्स शोधा"</string>
     <string name="label_application" msgid="8531721983832654978">"ॲप"</string>
     <string name="notifications_header" msgid="1404149926117359025">"सूचना"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"शॉर्टकट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"शॉर्टकट निवडण्यासाठी किंवा कस्टम क्रिया वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"शॉर्टकट हलवण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"शॉर्टकट हलवण्यासाठी किंवा कस्टम कृती वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
     <string name="out_of_space" msgid="4691004494942118364">"या मुख्य स्क्रीनवर आणखी जागा नाही."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"अ‍ॅप्स सूची"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"वैयक्तिक अ‍ॅप्स सूची"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"कामाच्या ठिकाणी वापरली जाणाऱ्या ॲप्सची सूची"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"होम"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"काढा"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइंस्टॉल करा"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"अ‍ॅप माहिती"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"फोल्डरचे नाव बदलून <xliff:g id="NAME">%1$s</xliff:g> असे ठेवले"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> आयटम"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> किंवा त्याहून अधिक आयटम"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"शैली आणि वॉलपेपर"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग्‍ज"</string>
@@ -95,15 +110,15 @@
     <string name="title_change_settings" msgid="1376365968844349552">"सेटिंग्ज बदला"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"सूचना बिंदू दाखवा"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"होम स्क्रीनवर ॲप आयकन जोडा"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नवीन ॲप्ससाठी"</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नवीन अ‍ॅप्ससाठी"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"हा अ‍ॅप इंस्टॉल केलेला नाही"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"या चिन्हासाठी अ‍ॅप इंस्टॉल केलेला नाही. तुम्ही ते काढू शकता किंवा अ‍ॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे इंस्टॉल करू शकता."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करत आहे, <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करत आहे"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेट"</string>
     <string name="widgets_list" msgid="796804551140113767">"विजेट सूची"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"विजेट सूची बंद केली"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"होम स्क्रीनवर जोडा"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"पुढील"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"समजले"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"कार्य प्रोफाइल थांबवली आहे"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"कामासंबंधित अ‍ॅप्स तुम्हाला सूचना पाठवू शकत नाहीत, तुमची बॅटरी वापरू शकत नाहीत किंवा तुमचे स्थान अ‍ॅक्सेस करू शकत नाहीत"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"कार्य प्रोफाइल थांबवली आहे. कार्य अ‍ॅप्स तुम्हाला सूचना पाठवू शकत नाहीत, तुमची बॅटरी वापरू शकत नाहीत किंवा तुमचे स्थान अ‍ॅक्सेस करू शकत नाहीत"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Work apps तुम्हाला सूचना पाठवू शकत नाहीत, तुमची बॅटरी वापरू शकत नाहीत किंवा तुमचे स्थान अ‍ॅक्सेस करू शकत नाहीत"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"कार्य प्रोफाइल थांबवली आहे. Work apps तुम्हाला सूचना पाठवू शकत नाहीत, तुमची बॅटरी वापरू शकत नाहीत किंवा तुमचे स्थान अ‍ॅक्सेस करू शकत नाहीत"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Work apps ही बॅज केलेली असून तुमच्या IT ॲडमिनला दृश्यमान आहेत"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"समजले"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Work apps थांबवा"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"सुरू करा"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"फिल्टर"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"कार्य अ‍ॅप्स आणि सूचना थांबवा"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"हे करता आले नाही: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 38e87f1..e54c6fe 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Apl yang dimuat turun dilumpuhkan dalam mod Selamat"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widget dilumpuhkan dalam mod Selamat"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Pintasan tidak tersedia"</string>
-    <string name="home_screen" msgid="806512411299847073">"Skrin utama"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Tindakan tersuai"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Sentuh &amp; tahan untuk mengambil widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ketik dua kali &amp; tahan untuk mengambil widget atau menggunakan tindakan tersuai"</string>
+    <string name="home_screen" msgid="5629429142036709174">"Rumah"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Skrin pisah"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Lebar %1$d kali tinggi %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Sentuh &amp; tahan untuk meletakkan widget/ikon secara manual"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Tambahkan secara automatik"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Kosongkan teks daripada kotak carian"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Widget tidak tersedia"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Tiada hasil carian"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Peribadi"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Tempat kerja"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Perbualan"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cari apl"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Memuatkan apl…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Pemberitahuan"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Sentuh &amp; tahan untuk mengambil pintasan."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Ketik dua kali &amp; tahan untuk mengambil pintasan atau menggunakan tindakan tersuai."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Tiada lagi ruang pada skrin Laman Utama ini."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Tiada ruang dalam dulang Kegemaran lagi"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Senarai apl"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Senarai apl peribadi"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Senarai apl kerja"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Laman Utama"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Alih keluar"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Nyahpasang"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Maklumat apl"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Folder dinamakan semula kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atau lebih banyak item"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Gaya &amp; kertas dinding"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Tetapan laman utama"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Carian"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Apl ini tidak dipasang"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Apl untuk ikon ini tidak dipasang. Anda boleh mengalih keluar atau mencari dan memasang apl itu secara manual."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> dipasang, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> memuat turun, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> menunggu untuk dipasang"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widget <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Senarai widget"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Senarai widget ditutup"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Tambahkan pada Skrin Utama"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Seterusnya"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Profil kerja dijeda"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Apl kerja tidak boleh menghantar pemberitahuan kepada anda, menggunakan bateri anda atau mengakses lokasi anda"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profil kerja dijeda. Apl kerja tidak boleh menghantar pemberitahuan kepada anda, menggunakan bateri anda atau mengakses lokasi anda"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Apl kerja tidak boleh menghantar pemberitahuan kepada anda, menggunakan bateri anda atau mengakses lokasi anda"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Profil kerja dijeda. Apl kerja tidak boleh menghantar pemberitahuan kepada anda, menggunakan bateri anda atau mengakses lokasi anda"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Apl kerja mempunyai lencana dan kelihatan kepada pentadbir IT anda"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Jeda apl kerja"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Hidupkan"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Tapis"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Jeda apl kerja dan pemberitahuan"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index d753021..b429602 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ဒေါင်းလုဒ် အက်ပ်ကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"လုံခြုံရေး မုဒ်ထဲမှာ ဝီဂျက်များကို ပိတ်ထား"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ဖြတ်လမ်း မရနိုင်ပါ"</string>
-    <string name="home_screen" msgid="806512411299847073">"ပင်မစာမျက်နှာ"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"စိတ်ကြိုက် လုပ်ဆောင်ချက်များ"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ဝဒ်ဂျက်တစ်ခုကို ကောက်ယူရန် ဖိနှိပ်ထားပါ"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ဝစ်ဂျက်တစ်ခုကိုရယူရန် သို့မဟုတ် စိတ်ကြိုက်လုပ်ဆောင်မှုများကို အသုံးပြုရန် နှစ်ချက်တို့ပြီး ကိုင်ထားပါ။"</string>
+    <string name="home_screen" msgid="5629429142036709174">"ပင်မစာမျက်နှာ"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"အလျား %1$d နှင့် အမြင့် %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ကိုယ်တိုင်ထည့်ရန် ထိထားပါ"</string>
     <string name="place_automatically" msgid="8064208734425456485">"အလိုအလျောက် ထည့်ရန်"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ရှာဖွေစာလုံးဖြည့်ကွက်လပ်မှ စာသားကို ရှင်းလင်းပါ"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ဝိဂျက်များ မရနိုင်ပါ"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"ရှာဖွေမှု ရလဒ်များ မရှိပါ"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ကိုယ်ပိုင်"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"အလုပ်"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"စကားဝိုင်းများ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ရှာဖွေမှု အက်ပ်များ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"အက်ပ်များကို ဖွင့်နေသည်…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ပ်များကို မတွေ့ပါ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"နောက်ထပ် အက်ပ်များကို ရှာပါ"</string>
     <string name="label_application" msgid="8531721983832654978">"အက်ပ်"</string>
     <string name="notifications_header" msgid="1404149926117359025">"အကြောင်းကြားချက်များ"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ဖြတ်လမ်းလင့်ခ်တစ်ခုကို ရွေးရန် ထိပြီး ဖိထားပါ။"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ဖြတ်လမ်းလင့်ခ်ကို ရွေးရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ဖြတ်လမ်းလင့်ခ်ကို ရွှေ့ရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ဖြတ်လမ်းလင့်ခ်ကို ရွှေ့ရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"အက်ပ်စာရင်း"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"တစ်ကိုယ်ရေသုံး အက်ပ်စာရင်း"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"အလုပ်သုံး အက်ပ်စာရင်း"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ပင်မစာမျက်နှာ"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ဖယ်ရှားမည်"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ဖြုတ်ရန်"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"အက်ပ်အချက်အလက်"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ပြောင်းလဲလိုက်သော အကန့်အမည် <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ဖိုင်တွဲ - <xliff:g id="NAME">%1$s</xliff:g>၊ <xliff:g id="SIZE">%2$d</xliff:g> ဖိုင်များ"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ဖိုင်တွဲ - <xliff:g id="NAME">%1$s</xliff:g>၊ <xliff:g id="SIZE">%2$d</xliff:g> သို့မဟုတ် နောက်ထပ်ဖိုင်များ"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ပုံစံနှင့် နောက်ခံပုံများ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ပင်မဆက်တင်များ"</string>
@@ -88,22 +103,22 @@
     <string name="allow_rotation_title" msgid="7728578836261442095">"ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုပါ"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ဖုန်းကိုလှည့်ထားစဉ်"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"သတိပေးချက် အစက်များ"</string>
-    <string name="notification_dots_desc_on" msgid="1679848116452218908">"ဖွင့်ထားသည်"</string>
-    <string name="notification_dots_desc_off" msgid="1760796511504341095">"ပိတ်ထားသည်"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"ဖွင့်"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"ပိတ်"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"အကြောင်းကြားချက် အသုံးပြုခွင့် လိုအပ်သည်"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"အကြောင်းကြားချက် အစက်များကို ပြသရန် <xliff:g id="NAME">%1$s</xliff:g> အတွက် အက်ပ်အကြောင်းကြားချက်များကို ဖွင့်ပါ"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"ဆက်တင်များ ပြောင်းရန်"</string>
-    <string name="notification_dots_service_title" msgid="4284221181793592871">"သတိပေးချက် အစက်များ ပြရန်"</string>
-    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"\'ပင်မစာမျက်နှာ\' တွင် အက်ပ်သင်္ကေတထည့်ရန်"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"အကြောင်းကြားချက် အစက်များ ပြရန်"</string>
+    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ပင်မစာမျက်နှာတွင် အက်ပ်သင်္ကေတထည့်ရန်"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"အက်ပ်အသစ်များအတွက်"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"မသိ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ဖယ်ရှားရန်"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ရှာဖွေရန်"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"အက်ပ်မတပ်ဆင်ရသေးပါ"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ဤအိုင်ကွန်အတွက် အက်ပ်အားမထည့်သွင်းထားပါ။ You can remove it, or search for the အက်ပ်and install it manually."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ကို ထည့်သွင်းနေသည်၊ <xliff:g id="PROGRESS">%2$s</xliff:g> ပြီးပါပြီ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ဒေါင်းလုဒ်လုပ်နေသည်၊ <xliff:g id="PROGRESS">%2$s</xliff:g> ပြီးပါပြီ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ကိုထည့်သွင်းရန်စောင့်နေသည်"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ဝိဂျက်များ"</string>
     <string name="widgets_list" msgid="796804551140113767">"ဝိဂျက်စာရင်း"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ဝိဂျက်စာရင်းကို ပိတ်ထားသည်"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ပင်မမျက်နှာစာသို့ ထည့်ပါ"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ရှေ့သို့"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ok"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"အလုပ်ပရိုဖိုင် ခဏရပ်ထားသည်"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"အလုပ်ပရိုဖိုင် ခဏရပ်ထားသည်။ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"အလုပ်ပရိုဖိုင် ခဏရပ်ထားသည်။ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"အလုပ်သုံးအက်ပ်များကို တံဆိပ်တပ်ထားပြီး သင်၏ IT စီမံခန့်ခွဲသူက မြင်နိုင်ပါသည်"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ရပြီ"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"အလုပ်သုံးအက်ပ်များကို ခဏရပ်ရန်"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ဖွင့်ရန်"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"စစ်ထုတ်ရန်"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"အလုပ်သုံးအက်ပ်နှင့် အကြောင်းကြားချက်များ ခဏရပ်ရန်"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"မအောင်မြင်ပါ− <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 47b4635..8245608 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"En nedlastet app er deaktivert i sikker modus"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Moduler er deaktivert i sikker modus"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Snarveien er ikke tilgjengelig"</string>
-    <string name="home_screen" msgid="806512411299847073">"Startskjerm"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Tilpassede handlinger"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Trykk og hold inne for å plukke opp en modul."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dobbelttrykk og hold inne for å velge en modul eller bruke tilpassede handlinger."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Startskjerm"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Delt skjerm"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bredde x %2$d høyde"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Trykk og hold for å plassere manuelt"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Legg til automatisk"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Tøm tekst fra søkefeltet"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Ingen moduler er tilgjengelige"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Ingen søkeresultater"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personlig"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Jobb"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Samtaler"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Søk etter apper"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Laster inn appene …"</string>
     <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="notifications_header" msgid="1404149926117359025">"Varsler"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Trykk og hold for å velge en snarvei."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Denne startsiden er full."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritter-skuffen er full"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"App-liste"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personlige apper-liste"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Jobbapper-liste"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Startside"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstaller"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Mappen heter nå <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Moduler"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiler og bakgrunner"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Startsideinnstillinger"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Søk"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Denne appen er ikke installert"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen for dette ikonet er ikke installert. Du kan fjerne det, eller prøve å søke etter appen og installere den manuelt."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installerer, <xliff:g id="PROGRESS">%2$s</xliff:g> er fullført"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Laster ned <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> er fullført"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Venter på å installere <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-moduler"</string>
     <string name="widgets_list" msgid="796804551140113767">"Modulliste"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Modullisten er lukket"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Legg til på startskjermen"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Neste"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Greit"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Jobbprofilen er satt på pause"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Jobbapper kan ikke sende deg varsler, bruke batteriet eller få tilgang til posisjonen din"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Jobbprofilen er satt på pause. Jobbapper kan ikke sende deg varsler, bruke batteriet eller få tilgang til posisjonen din"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Jobbapper kan ikke sende deg varsler, bruke batteriet eller få tilgang til posisjonen din"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Jobbprofilen er satt på pause. Jobbapper kan ikke sende deg varsler, bruke batteriet eller få tilgang til posisjonen din"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Jobbapper er merket og synlige for IT-administratoren din"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Greit"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Sett jobbapper på pause"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Slå på"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Sett jobbapper og -varsler på pause"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Mislyktes: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 23394a7..9c348e3 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -26,31 +26,55 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"सुरक्षित मोडमा डाउनलोड गरेको एप अक्षम गरिएको छ"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"सुरक्षित मोडमा विगेटहरू अक्षम गरियो"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"सर्टकट उपलब्ध छैन"</string>
-    <string name="home_screen" msgid="806512411299847073">"गृह स्क्रिन"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"आफू अनुकूलका कारबाहीहरू"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"एउटा विजेटलाई टिप्नको लागि टच गरेर होल्ड गर्नुहोस्।"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"विजेटलाई छान्न वा आफू अनुकूल कार्यहरू प्रयोग गर्न डबल ट्याप गरी होल्ड गर्नुहोस्।"</string>
+    <!-- no translation found for home_screen (5629429142036709174) -->
+    <skip />
+    <!-- no translation found for recent_task_option_split_screen (6690461455618725183) -->
+    <skip />
+    <!-- no translation found for long_press_widget_to_add (3587712543577675817) -->
+    <skip />
+    <!-- no translation found for long_accessible_way_to_add (2733588281439571974) -->
+    <skip />
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौडाइ गुणा %2$d उचाइ"</string>
-    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"म्यानुअल तरिकाले थप्न छुनुहोस् र थिची राख्नुहोस्‌"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"थप्न टच एण्ड होल्ड गर्नुहोस्"</string>
     <string name="place_automatically" msgid="8064208734425456485">"स्वतः थप्नुहोस्"</string>
+    <!-- no translation found for widgets_count (656794749266073027) -->
+    <!-- no translation found for shortcuts_count (8080294865447938455) -->
+    <!-- no translation found for widgets_and_shortcuts_count (7209136747878365116) -->
+    <skip />
+    <string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
+    <!-- no translation found for widgets_full_sheet_search_bar_hint (8484659090860596457) -->
+    <skip />
+    <!-- no translation found for widgets_full_sheet_cancel_button_description (5766167035728653605) -->
+    <skip />
+    <!-- no translation found for no_widgets_available (9140948620298620513) -->
+    <skip />
+    <!-- no translation found for no_search_results (6518732304311458580) -->
+    <skip />
+    <!-- no translation found for widgets_full_sheet_personal_tab (2743540105607120182) -->
+    <skip />
+    <!-- no translation found for widgets_full_sheet_work_tab (3767150027110633765) -->
+    <skip />
+    <!-- no translation found for widget_category_conversations (8894438636213590446) -->
+    <skip />
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"खोजसम्बन्धी एपहरू"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"एपहरू लोड गर्दै…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै एप भेटिएन"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"थप एपहरू खोज्नुहोस्"</string>
     <string name="label_application" msgid="8531721983832654978">"एप"</string>
     <string name="notifications_header" msgid="1404149926117359025">"सूचनाहरू"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"कुनै सर्टकट छनौट गर्न छोइराख्नुहोस्।"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"कुनै सर्टकट छनौट गर्न वा रोजेका कारबाहीहरू प्रयोग गर्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
+    <!-- no translation found for long_press_shortcut_to_add (5405328730817637737) -->
+    <skip />
+    <!-- no translation found for long_accessible_way_to_add_shortcut (2199537273817090740) -->
+    <skip />
     <string name="out_of_space" msgid="4691004494942118364">"यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"मन पर्ने ट्रे अब कुनै ठाँउ छैन"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"एपको सूची"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"व्यक्तिगत अनुप्रयोगहरूको सूची"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"कार्यसम्बन्धी अनुप्रयोगहरूको सूची"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"गृह"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"हटाउनुहोस्"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"विस्थापित गर्नुहोस्"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"एपको जानकारी"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"एपसम्बन्धी जानकारी"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"यो एप सिफारिस नगरियोस्"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"सिफारिस गरिएको एप पिन गर्नुहोस्"</string>
@@ -80,30 +104,30 @@
     <string name="folder_renamed" msgid="1794088362165669656">"फोल्डर <xliff:g id="NAME">%1$s</xliff:g> मा पुनःनामाकरण गरियो।"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> वस्तुहरू"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> वा सोभन्दा बढी वस्तुहरू"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"शैली तथा वालपेपरहरू"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"गृहपृष्ठका सेटिङहरू"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"गृह स्क्रिनलाई घुम्ने अनुमति दिनुहोस्"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"गृह स्क्रिनलाई रोटेट हुन दिइयोस्"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"फोनलाई घुमाइँदा"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"सूचनाको प्रतीक जनाउने थोप्लाहरू"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"सक्रिय"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"निष्क्रिय"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"सूचनासम्बन्धी पहुँच आवश्यक हुन्छ"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउन <xliff:g id="NAME">%1$s</xliff:g> को अनुप्रयोगसम्बन्धी सूचनाहरूलाई सक्रिय गर्नुहोस्"</string>
+    <string name="msg_missing_notification_access" msgid="281113995110910548">"सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउन <xliff:g id="NAME">%1$s</xliff:g> को एपसम्बन्धी सूचनाहरूलाई सक्रिय गर्नुहोस्"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"सेटिङहरू बदल्नुहोस्"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउनुहोस्"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"गृह स्क्रिनमा एपका आइकनहरू थप्नुहोस्"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नयाँ अनुप्रयोगका लागि"</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नयाँ एपका लागि"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
     <string name="abandoned_search" msgid="891119232568284442">"खोजी गर्नुहोस्"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"यो एप स्थापित छैन"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"यो प्रतिमाका लागि एपलाई स्थापना गरिएको छैन। तपाईं यसलाई हटाउन, वा एप खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।"</string>
+    <!-- no translation found for app_installing_title (5864044122733792085) -->
+    <skip />
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड गर्दै, <xliff:g id="PROGRESS">%2$s</xliff:g> सम्पन्‍न"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> स्थापना गर्न प्रतीक्षा गर्दै"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेटहरू"</string>
     <string name="widgets_list" msgid="796804551140113767">"विजेटहरूको सूची"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"विजेटहरूको सूची बन्द गरियो"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"गृह स्क्रिनमा थप्नुहोस्"</string>
@@ -141,8 +165,20 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"अर्को"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"बुझेँ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको यन्त्रको ब्याट्री प्रयोग गर्न वा तपाईंको स्थान हेर्न सक्दैनन्"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ। कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको यन्त्रको ब्याट्री प्रयोग गर्न वा तपाईंको स्थान हेर्न सक्दैनन्"</string>
+    <!-- no translation found for work_apps_paused_body (4209084728264328628) -->
+    <skip />
+    <!-- no translation found for work_apps_paused_content_description (4473292417145736203) -->
+    <skip />
+    <!-- no translation found for work_apps_paused_edu_banner (8872412121608402058) -->
+    <skip />
+    <!-- no translation found for work_apps_paused_edu_accept (6377476824357318532) -->
+    <skip />
+    <!-- no translation found for work_apps_pause_btn_text (4669288269140620646) -->
+    <skip />
+    <!-- no translation found for work_apps_enable_btn_text (82111102541971856) -->
+    <skip />
+    <!-- no translation found for developer_options_filter_hint (5896817443635989056) -->
+    <skip />
     <string name="work_switch_tip" msgid="808075064383839144">"कामसम्बन्धी एप र सूचनाहरू अस्थायी रूपमा रोक्का गर्नुहोस्"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"कार्य पूरा गर्न सकिएन: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-night-v26/styles.xml b/res/values-night/styles.xml
similarity index 77%
rename from res/values-night-v26/styles.xml
rename to res/values-night/styles.xml
index 510e1f4..d41eb7e 100644
--- a/res/values-night-v26/styles.xml
+++ b/res/values-night/styles.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-* Copyright (C) 2018 The Android Open Source Project
+* Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -18,9 +18,8 @@
 -->
 
 <resources>
-
-    <style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
+    <style name="AddItemActivityTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
+        <item name="android:windowTranslucentStatus">true</item>
     </style>
-
-</resources>
\ No newline at end of file
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index ded05b0..5af9252 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -23,31 +23,47 @@
     <string name="work_folder_name" msgid="3753320833950115786">"Werk"</string>
     <string name="activity_not_found" msgid="8071924732094499514">"App is niet geïnstalleerd."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"App is niet beschikbaar"</string>
-    <string name="safemode_shortcut_error" msgid="9160126848219158407">"Gedownloade app uitgeschakeld in veilige modus"</string>
-    <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets uitgeschakeld in Veilige modus"</string>
+    <string name="safemode_shortcut_error" msgid="9160126848219158407">"Gedownloade app uitgezet in veilige modus"</string>
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets uitgezet in veilige modus"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Snelkoppeling is niet beschikbaar"</string>
-    <string name="home_screen" msgid="806512411299847073">"Startscherm"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Aangepaste acties"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Blijf aanraken om een widget toe te voegen."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dubbeltik en blijf aanraken om een widget toe te voegen of aangepaste acties te gebruiken."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Startscherm"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Gesplitst scherm"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed en %2$d hoog"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tik en houd vast om handmatig te plaatsen"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Automatisch toevoegen"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Tekst in zoekvak wissen"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Geen widgets beschikbaar"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Geen zoekresultaten"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Persoonlijk"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Werk"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Gesprekken"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Apps zoeken"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Apps laden…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Meldingen"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tik en houd vast om snelkoppeling toe te voegen."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dubbeltik en houd vast om een snelkoppeling toe te voegen of aangepaste acties te gebruiken."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Er is geen ruimte meer op dit startscherm."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen ruimte meer in het vak \'Favorieten\'"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lijst met apps"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lijst met persoonlijke apps"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lijst met werk-apps"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Homepage"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Verwijderen"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleren"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"App-info"</string>
@@ -65,7 +81,7 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuratie"</string>
     <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> is uitgeschakeld"</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>
@@ -80,20 +96,19 @@
     <string name="folder_renamed" msgid="1794088362165669656">"De naam van de map is gewijzigd in <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stijl en achtergrond"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Instellingen start"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgezet door je beheerder"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Draaien van startscherm toestaan"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Wanneer de telefoon gedraaid is"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"Meldingsstipjes"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"Aan"</string>
     <string name="notification_dots_desc_off" msgid="1760796511504341095">"Uit"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Toegang tot meldingen vereist"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Als je meldingsstipjes wilt weergeven, schakel je app-meldingen in voor <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="msg_missing_notification_access" msgid="281113995110910548">"Als je meldingsstipjes wilt tonen, zet je app-meldingen aan voor <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Instellingen wijzigen"</string>
-    <string name="notification_dots_service_title" msgid="4284221181793592871">"Meldingsstipjes weergeven"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Meldingsstipjes tonen"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"App-iconen toevoegen aan startscherm"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Voor nieuwe apps"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Zoeken"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Deze app is niet geïnstalleerd"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"De app voor dit icoon is niet geïnstalleerd. Je kunt het icoon verwijderen of de app zoeken en handmatig installeren."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeren, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wordt gedownload, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wacht op installatie"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-widgets"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lijst met widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lijst met widgets gesloten"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Toevoegen aan startscherm"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Volgende"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Werkprofiel is onderbroken"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Werk-apps kunnen je geen meldingen sturen, niet je batterij gebruiken en geen toegang krijgen tot je locatie"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Werkprofiel is onderbroken. Werk-apps kunnen je geen meldingen sturen, niet je batterij gebruiken en geen toegang krijgen tot je locatie"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Werk-apps kunnen je geen meldingen sturen, niet je batterij gebruiken en geen toegang krijgen tot je locatie"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Werkprofiel is gepauzeerd. Werk-apps kunnen je geen meldingen sturen, niet je batterij gebruiken en geen toegang krijgen tot je locatie."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Werk-apps hebben badges en zijn zichtbaar voor je IT-beheerder"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Werk-apps pauzeren"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aanzetten"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filteren"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Werk-apps en -meldingen onderbreken"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Mislukt: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 994b620..8505c8e 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ନିରାପଦ ମୋଡରେ ଡାଉନଲୋଡ୍‌ ହେଇଥିବା ଆପ୍‌ ଅକ୍ଷମ କରାଗଲା"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"ନିରାପଦ ମୋଡରେ ୱିଜେଟ୍‌ ଅକ୍ଷମ କରାଗଲା"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ଶର୍ଟକଟ୍‌ ଉପଲବ୍ଧ ନାହିଁ"</string>
-    <string name="home_screen" msgid="806512411299847073">"ହୋମ୍‌ ସ୍କ୍ରୀନ୍‌"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"କାର୍ଯ୍ୟ କଷ୍ଟମ୍ କରନ୍ତୁ"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ୱିଜେଟ୍‌ ନେବାକୁ ସ୍ପର୍ଶ କରନ୍ତୁ ଏବଂ ଧରି ରଖନ୍ତୁ।"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ଡବଲ୍‌-ଟାପ୍‌ କରନ୍ତୁ ଏବଂ ଏକ ୱିଜେଟ୍‌ ନେବାକୁ ଧରି ରଖନ୍ତୁ କିମ୍ୱା କଷ୍ଟମ୍ କାର୍ଯ୍ୟପ୍ରକ୍ରିୟା ବ୍ୟବହାର କରନ୍ତୁ।"</string>
+    <string name="home_screen" msgid="5629429142036709174">"ମୂଳପୃଷ୍ଠା"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ସ୍କ୍ରିନ‌କୁ ସ୍ପ୍ଲିଟ୍ କରନ୍ତୁ"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ଓସାର ଓ %2$d ଉଚ୍ଚ"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ମାନୁଆଲ୍‌ ଭାବରେ ରଖିବାକୁ ସ୍ପର୍ଶ କରନ୍ତୁ ଏବଂ ଧରି ରଖନ୍ତୁ"</string>
-    <string name="place_automatically" msgid="8064208734425456485">"ସ୍ୱଚାଳିତ ଭାବେ ଯୋଡ଼ନ୍ତୁ"</string>
+    <string name="place_automatically" msgid="8064208734425456485">"ସ୍ୱଚାଳିତ ଭାବେ ଯୋଗ କରନ୍ତୁ"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ସନ୍ଧାନ ବାକ୍ସରୁ ଟେକ୍ସଟ୍ ଖାଲି କରନ୍ତୁ"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"କୌଣସି ୱିଜେଟ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"କୌଣସି ସନ୍ଧାନ ଫଳାଫଳ ନାହିଁ"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ବ୍ୟକ୍ତିଗତ"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ୱାର୍କ"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ଆପ୍‌ ଖୋଜନ୍ତୁ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ଆପ୍‌ ଲୋଡ୍‌ ହେଉଛି..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ସହିତ ମେଳ ହେଉଥିବା କୌଣସି ଆପ୍‌ ମିଳିଲା ନାହିଁ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ଅଧିକ ଆପ୍‌ ଖୋଜନ୍ତୁ"</string>
     <string name="label_application" msgid="8531721983832654978">"ଆପ୍"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ବିଜ୍ଞପ୍ତି"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ଏକ ଶର୍ଟକଟ୍ ଚୟନ କରିବାକୁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ଡବଲ୍‌-ଟାପ୍‌ କରନ୍ତୁ ଏବଂ ଏକ ଶର୍ଟକଟ୍ ଚୟନ କରିବାକୁ ଧରି ରଖନ୍ତୁ କିମ୍ୱା କଷ୍ଟମ୍ ପ୍ରକ୍ରିୟା ବ୍ୟବହାର କରନ୍ତୁ।"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ଏକ ସର୍ଟକଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ।"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ଏକ ସର୍ଟକଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଦୁଇଥର-ଟାପ୍ କରି ଧରି ରଖନ୍ତୁ କିମ୍ବା କଷ୍ଟମ୍ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ହୋମ୍‌ ସ୍କ୍ରୀନ ପାଇଁ ଆଉ କୋଠରୀ ନାହିଁ"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ମନପସନ୍ଦ ଟ୍ରେରେ ଆଉ କୋଠରୀ ନାହିଁ"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"ଆପ୍‌ ତାଲିକା"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ବ୍ୟକ୍ତିଗତ ଆପ୍ ତାଲିକା"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"କାର୍ଯ୍ୟକାରୀ ଆପ୍‌ ତାଲିକା"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ହୋମ୍"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ବାହାର କରନ୍ତୁ"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ଅନଇନଷ୍ଟଲ୍‌ କରନ୍ତୁ"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ଆପ୍‌ ସୂଚନା"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ଫୋଲ୍ଡରର ନାମ <xliff:g id="NAME">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ଆଇଟମଗୁଡ଼ିକ"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> କିମ୍ବା ଅଧିକ ଆଇଟମ୍"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ୱିଜେଟ୍‌"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ୱାଲପେପର୍‌"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ଷ୍ଟାଇଲ୍ ଏବଂ ୱାଲ୍‍‍ପେପର୍"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ୍‌ ସେଟିଂସ୍"</string>
@@ -97,13 +112,13 @@
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ହୋମ୍ ସ୍କ୍ରିନରେ ଆପ୍ ଆଇକନଗୁଡ଼ିକୁ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ନୂଆ ଆପ୍‌ ପାଇଁ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ଅଜଣା"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"ବାହାର କରନ୍ତୁ"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ଏହି ଆପ୍‌ ଇନଷ୍ଟଲ୍‌ ହୋଇନାହିଁ"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ଏହି ଆଇକନ୍‌ ପାଇଁ ଆପ୍‌ ଇନଷ୍ଟଲ୍‌ ହୋଇନାହିଁ। ଏହାକୁ ଆପଣ ଆପ୍‌ ପାଇଁ ବାହାର କରିପାରିବେ କିମ୍ୱା ସର୍ଚ୍ଚ କରି ପାରିବେ ଏବଂ ଏହାକୁ ମାନୁଆଲ୍‌ ଭାବରେ ଇନଷ୍ଟଲ୍‌ କରିପାରିବେ।"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍ କରାଯାଉଛି, <xliff:g id="PROGRESS">%2$s</xliff:g> ସମ୍ପୂର୍ଣ୍ଣ ହୋଇଛି"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ଡାଉନଲୋଡ୍‌ ହେଉଛି, <xliff:g id="PROGRESS">%2$s</xliff:g> ସମ୍ପୂର୍ଣ୍ଣ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍‌ ହେବାକୁ ଅପେକ୍ଷା କରିଛି"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ୱିଜେଟ୍‌"</string>
     <string name="widgets_list" msgid="796804551140113767">"ୱିଜେଟ୍ ତାଲିକା"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ୱିଜେଟ୍ ତାଲିକା ବନ୍ଦ ହୋଇଛି"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ହୋମ୍‌ ସ୍କ୍ରୀନରେ ଯୋଡ଼ନ୍ତୁ"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ପରବର୍ତ୍ତୀ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ବୁଝିଗଲି"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ୱାର୍କ ପ୍ରୋଫାଇଲକୁ ବିରତ କରାଯାଇଛି"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପଗୁଡ଼ିକ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ୱାର୍କ ପ୍ରୋଫାଇଲ୍ ବିରତ କରାଯାଇଛି। କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"ୱାର୍କ ଆପଗୁଡ଼ିକ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀକୁ ବ୍ୟବହାର କରିପାରିବ ନାହିଁ କିମ୍ବା ଆପଣଙ୍କର ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ ନାହିଁ"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"ୱାର୍କ ପ୍ରୋଫାଇଲକୁ ବିରତ କରାଯାଇଛି। ୱାର୍କ ଆପଗୁଡ଼ିକ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀକୁ ବ୍ୟବହାର କରିପାରିବ ନାହିଁ କିମ୍ବା ଆପଣଙ୍କର ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ ନାହିଁ"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ବ୍ୟାଜ୍ କରାଯାଇଛି ଏବଂ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଉଛି"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ବୁଝିଗଲି"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ୱାର୍କ ଆପଗୁଡ଼ିକୁ ବିରତ କରନ୍ତୁ"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ଚାଲୁ କରନ୍ତୁ"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ଫିଲ୍ଟର୍"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରନ୍ତୁ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ବିଫଳ ହୋଇଛି: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index c1d7dfa..f97f255 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ਡਾਊਨਲੋਡ ਕੀਤਾ ਐਪ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"ਵਿਜੇਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
-    <string name="home_screen" msgid="806512411299847073">"ਹੋਮ ਸਕ੍ਰੀਨ"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋT &amp; ਹੋਲਡ ਕਰੋ।"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਜਾਂ ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ ਵਰਤਣ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
+    <string name="home_screen" msgid="5629429142036709174">"ਮੁੱਖ ਪੰਨਾ"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ"</string>
-    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ"</string>
-    <string name="place_automatically" msgid="8064208734425456485">"ਸਵੈਚਲਿਤ ਤਰੀਕੇ ਨਾਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪਰਸ਼ ਕਰਕੇ ਰੱਖੋ"</string>
+    <string name="place_automatically" msgid="8064208734425456485">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ਖੋਜ ਬਾਕਸ ਤੋਂ ਸਪੱਸ਼ਟ ਲਿਖਤ"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ਕੋਈ ਵਿਜੇਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"ਕੋਈ ਖੋਜ ਨਤੀਜਾ ਨਹੀਂ"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ਨਿੱਜੀ"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ਕਾਰਜ-ਸਥਾਨ"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"ਗੱਲਾਂਬਾਤਾਂ"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ਐਪਾਂ ਖੋਜੋ"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮੇਲ ਖਾਂਦੀਆਂ ਕੋਈ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ਹੋਰ ਐਪਾਂ ਖੋਜੋ"</string>
     <string name="label_application" msgid="8531721983832654978">"ਐਪ"</string>
     <string name="notifications_header" msgid="1404149926117359025">"ਸੂਚਨਾਵਾਂ"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ।"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਡਬਲ ਟੈਪ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ ਜਾਂ ਵਿਉਂਤੀਆਂ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ਕਿਸੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਲਿਜਾਉਣ ਲਈ ਸਪੱਰਸ਼ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ।"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ਕਿਸੇ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਲਿਜਾਉਣ ਲਈ ਡਬਲ ਟੈਪ ਕਰਕੇ ਦਬਾਈ ਰੱਖੋ ਜਾਂ ਵਿਉਂਤੀਆਂ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"ਐਪ ਸੂਚੀ"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ਨਿੱਜੀ ਐਪਾਂ ਦੀ ਸੂਚੀ"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"ਕਾਰਜ-ਸਥਾਨ ਸੰਬੰਧੀ ਐਪਾਂ ਦੀ ਸੂਚੀ"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ਹੋਮ"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ਹਟਾਓ"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ਅਣਸਥਾਪਤ ਕਰੋ"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ਐਪ ਜਾਣਕਾਰੀ"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ਫੋਲਡਰ ਨੂੰ <xliff:g id="NAME">%1$s</xliff:g> ਮੁੜ ਨਾਮ ਦਿੱਤਾ ਗਿਆ"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਆਈਟਮਾਂ"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਜਾਂ ਹੋਰ ਆਈਟਮਾਂ"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜੇਟ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ਸ਼ੈਲੀਆਂ ਅਤੇ ਵਾਲਪੇਪਰ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ਹੋਮ ਸੈਟਿੰਗਾਂ"</string>
@@ -101,15 +116,15 @@
     <string name="abandoned_search" msgid="891119232568284442">"ਖੋਜੋ"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ਇਹ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ਇਸ ਪ੍ਰਤੀਕ ਲਈ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਸਥਾਪਤ ਕਰ ਸਕਦੇ ਹੋ।"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ਨੂੰ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਪੂਰਾ ਹੋਇਆ"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਸੰਪੂਰਣ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ਸਥਾਪਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ਵਿਜੇਟ"</string>
     <string name="widgets_list" msgid="796804551140113767">"ਵਿਜੇਟਾਂ ਦੀ ਸੂਚੀ"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ਵਿਜੇਟਾਂ ਦੀ ਸੂਚੀ ਬੰਦ ਕੀਤੀ ਗਈ"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="action_move_here" msgid="2170188780612570250">"ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"ਆਈਟਮ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ"</string>
-    <string name="item_removed" msgid="851119963877842327">"ਅਈਟਮ ਹਟਾਈ ਗਈ"</string>
+    <string name="item_removed" msgid="851119963877842327">"ਆਈਟਮ ਹਟਾਈ ਗਈ"</string>
     <string name="undo" msgid="4151576204245173321">"ਅਣਕੀਤਾ ਕਰੋ"</string>
     <string name="action_move" msgid="4339390619886385032">"ਆਈਟਮ ਨੂੰ ਮੂਵ ਕਰੋ"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"ਕਤਾਰ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਕਾਲਮ <xliff:g id="NUMBER_1">%2$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ਅੱਗੇ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ਸਮਝ ਲਿਆ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੁਹਾਨੂੰ ਸੂਚਨਾਵਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀਆਂ, ਤੁਹਾਡੀ ਬੈਟਰੀ ਨਹੀਂ ਵਰਤ ਸਕਦੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ। ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੁਹਾਨੂੰ ਸੂਚਨਾਵਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀਆਂ, ਤੁਹਾਡੀ ਬੈਟਰੀ ਨਹੀਂ ਵਰਤ ਸਕਦੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੁਹਾਨੂੰ ਸੂਚਨਾਵਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀਆਂ, ਤੁਹਾਡੀ ਬੈਟਰੀ ਨਹੀਂ ਵਰਤ ਸਕਦੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ। ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੁਹਾਨੂੰ ਸੂਚਨਾਵਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀਆਂ, ਤੁਹਾਡੀ ਬੈਟਰੀ ਨਹੀਂ ਵਰਤ ਸਕਦੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਨੂੰ ਬੈਜ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਅਤੇ ਇਹ ਤੁਹਾਡੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਦਿਸਣਗੀਆਂ"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ਸਮਝ ਲਿਆ"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ਕੰਮ ਸੰਬੰਧੀ ਐਪ ਰੋਕੋ"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ਚਾਲੂ ਕਰੋ"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ਫਿਲਟਰ"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਰੋਕੋ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ਇਹ ਕਾਰਵਾਈ ਅਸਫਲ ਹੋਈ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 24fe6f9..06074e0 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -20,34 +20,54 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Praca"</string>
+    <string name="work_folder_name" msgid="3753320833950115786">"Służbowy"</string>
     <string name="activity_not_found" msgid="8071924732094499514">"Aplikacja nie jest zainstalowana."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikacja niedostępna"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Pobrana aplikacja została wyłączona w trybie awaryjnym"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widżety są wyłączone w trybie bezpiecznym"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Skrót nie jest dostępny"</string>
-    <string name="home_screen" msgid="806512411299847073">"Ekran główny"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Działania niestandardowe"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Aby dodać widżet, kliknij go i przytrzymaj."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Kliknij dwukrotnie i przytrzymaj, by wybrać widżet lub użyć działań niestandardowych."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Kliknij i przytrzymaj, by umieścić ręcznie"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Dodaj automatycznie"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Wyczyść tekst w polu wyszukiwania"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Brak dostępnych widżetów"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Brak wyników wyszukiwania"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobiste"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Służbowe"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Rozmowy"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Wyszukaj aplikacje"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Ładuję aplikacje…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Powiadomienia"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Kliknij i przytrzymaj, by wybrać skrót."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Kliknij dwukrotnie i przytrzymaj, by wybrać skrót lub użyć działań niestandardowych."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Naciśnij i przytrzymaj, aby wybrać 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>
     <string name="out_of_space" msgid="4691004494942118364">"Brak miejsca na tym ekranie głównym."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Brak miejsca w Ulubionych"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista aplikacji"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista aplikacji osobistych"</string>
-    <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista aplikacji do pracy"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Ekran główny"</string>
+    <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista aplikacji służbowych"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Usuń"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstaluj"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikacji"</string>
@@ -82,7 +102,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Nazwa folderu zmieniona na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementy"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, liczba elementów: <xliff:g id="SIZE">%2$d</xliff:g> lub więcej"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widżety"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Style i tapety"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ustawienia ekranu głównego"</string>
@@ -103,9 +122,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Szukaj"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacja nie jest zainstalowana"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacja, której odpowiada ta ikona, nie jest zainstalowana. Możesz usunąć ikonę lub wyszukać aplikację i zainstalować ją ręcznie."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Instaluję aplikację <xliff:g id="NAME">%1$s</xliff:g>, postęp: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Pobieranie elementu <xliff:g id="NAME">%1$s</xliff:g>, ukończono: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> oczekuje na instalację"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> – widżety"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista widgetów"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lista widgetów zamknięta"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodaj do ekranu głównego"</string>
@@ -137,14 +156,19 @@
     <string name="notification_dismissed" msgid="6002233469409822874">"Powiadomienie odrzucone"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobiste"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Służbowe"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil do pracy"</string>
-    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Dane osobiste znajdują się w innym miejscu i są niewidoczne dla aplikacji do pracy"</string>
-    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Dane i aplikacje z profilu do pracy są widoczne dla Twojego administratora IT"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil służbowy"</string>
+    <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Dane osobiste znajdują się w innym miejscu i są niewidoczne dla aplikacji służbowych"</string>
+    <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Dane i aplikacje z profilu służbowego są widoczne dla Twojego administratora IT"</string>
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Dalej"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Wstrzymano profil służbowy"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Aplikacje do pracy nie mogą wysyłać powiadomień, używać baterii ani mieć dostępu do Twojej lokalizacji"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Wstrzymano profil do pracy. Aplikacje do pracy nie mogą wysyłać powiadomień, używać baterii ani uzyskiwać dostępu do Twojej lokalizacji"</string>
-    <string name="work_switch_tip" msgid="808075064383839144">"Wstrzymaj aplikacje do pracy i powiadomienia"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Aplikacje służbowe nie mogą wysyłać powiadomień, używać baterii ani uzyskiwać dostępu do Twojej lokalizacji"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Wstrzymano profil służbowy. Aplikacje służbowe nie mogą wysyłać powiadomień, używać baterii ani uzyskiwać dostępu do Twojej lokalizacji"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Aplikacje służbowe mają plakietki i są widoczne dla administratora IT"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Wstrzymaj aplikacje służbowe"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Włącz"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtruj"</string>
+    <string name="work_switch_tip" msgid="808075064383839144">"Wstrzymaj aplikacje służbowe i powiadomienia"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Niepowodzenie: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index a6b72db..fa2a50e 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicação transferida desativada no Modo de segurança"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no Modo de segurança"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"O atalho não está disponível"</string>
-    <string name="home_screen" msgid="806512411299847073">"Ecrã principal"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Ações personalizadas"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Prima sem soltar para escolher um widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toque duas vezes sem soltar para escolher um widget ou utilize ações personalizadas."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Toque sem soltar para colocar manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Adicionar automaticamente"</string>
+    <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
+      <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
+      <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
+    </plurals>
+    <plurals name="shortcuts_count" formatted="false" msgid="8080294865447938455">
+      <item quantity="other"><xliff:g id="SHORTCUTS_COUNT_1">%1$d</xliff:g> atalhos</item>
+      <item quantity="one"><xliff:g id="SHORTCUTS_COUNT_0">%1$d</xliff:g> atalho</item>
+    </plurals>
+    <string name="widgets_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Limpe o texto da caixa de pesquisa"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nenhum widget disponível"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nenhum resultado da pesquisa"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pessoais"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabalho"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Pesquisar aplicações"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"A carregar aplicações…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notificações"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Toque sem soltar para escolher um atalho."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Toque duas vezes sem soltar para escolher um atalho ou utilize ações personalizadas."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Sem espaço suficiente neste Ecrã principal."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Não existe mais espaço no tabuleiro de Favoritos"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicações"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicações pessoais"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicações de trabalho"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Ecrã principal"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da app"</string>
@@ -80,9 +96,8 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Nome de pasta alterado para <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
-    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e imagens de fundo"</string>
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e fundo"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Definições de início"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo gestor"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação do ecrã principal"</string>
@@ -95,15 +110,15 @@
     <string name="title_change_settings" msgid="1376365968844349552">"Alterar definições"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar pontos de notificação"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Adic. ícones de apps ao ecrã principal"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas aplicações"</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas apps"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta app não está instalada"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A app deste ícone não está instalada. Pode removê-lo ou pesquisar a app e instalá-la manualmente."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"A instalar <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"A transferir o <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"A aguardar a instalação do <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista de widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgets fechada."</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Adicionar ao ecrã principal"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Seguinte"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Perfil de trabalho em pausa"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"As apps de trabalho não podem enviar-lhe notificações, utilizar a sua bateria ou aceder à sua localização."</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"O perfil de trabalho está em pausa. As apps de trabalho não podem enviar-lhe notificações, utilizar a sua bateria ou aceder à sua localização."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"As apps de trabalho não podem enviar-lhe notificações, utilizar a sua bateria ou aceder à sua localização"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"O perfil de trabalho está em pausa. As apps de trabalho não podem enviar-lhe notificações, utilizar a sua bateria ou aceder à sua localização"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"As apps de trabalho têm um emblema e estão visíveis para o seu administrador de TI"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Colocar apps de trabalho em pausa"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Ativar"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Coloque as apps de trabalho e as notificações em pausa."</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Falhou: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 165c8fc..165a013 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"App transferido por download desativado no modo de segurança"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no modo de segurança"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"O atalho não está disponível"</string>
-    <string name="home_screen" msgid="806512411299847073">"Tela inicial"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Ações personalizadas"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Toque e pressione para selecionar um widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toque duas vezes e segure para selecionar um widget ou usar ações personalizadas."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Início"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Tela dividida"</string>
+    <string name="long_press_widget_to_add" msgid="3587712543577675817">"Toque e mantenha a tela pressionada 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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Toque e mantenha pressionado para mover manualmente"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Adicionar automaticamente"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Limpar texto da caixa de pesquisa"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nenhum widget disponível"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Nenhum resultado de pesquisa"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Pessoal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabalho"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Apps de pesquisa"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Carregando apps…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notificações"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Toque e segure para selecionar um atalho."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Toque duas vezes na tela e segure para selecionar um atalho ou usar ações personalizadas."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Não há mais espaço na tela inicial."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Sem espaço na bandeja de favoritos"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de apps"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de apps pessoais"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de apps profissionais"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Início"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Informações do app"</string>
@@ -80,10 +96,9 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Pasta renomeada para <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e planos de fundo"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Config. tela inicial"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configurações da tela inicial"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação da tela inicial"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o smartphone for girado"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Este app não está instalado"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"O app deste ícone não está instalado. Você pode remover o ícone, ou procurar o app e instalá-lo manualmente."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>. <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Fazendo download de <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Aguardando instalação de <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets do <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista de widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgets fechada"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Adicionar à tela inicial"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Próxima"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ok"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"O perfil de trabalho está pausado"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Apps de trabalho não podem enviar notificações, usar a bateria nem acessar o local"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"O perfil de trabalho está pausado. Apps de trabalho não podem enviar notificações, usar a bateria nem acessar seu local"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Os apps de trabalho não podem enviar notificações, usar a bateria nem acessar o local"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"O perfil de trabalho está pausado. Os apps de trabalho não podem enviar notificações, usar a bateria nem acessar o local"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Os apps de trabalho têm um ícone e ficam visíveis para o administrador de TI"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ok"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausar apps de trabalho"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Ativar"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrar"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pausar apps e notificações de trabalho"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Falha: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
deleted file mode 100644
index 0758148..0000000
--- a/res/values-rm/strings.xml
+++ /dev/null
@@ -1,203 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2008 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT 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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for application_name (5181331383435256801) -->
-    <skip />
-    <!-- no translation found for home (7658288663002113681) -->
-    <skip />
-    <!-- no translation found for uid_name (7820867637514617527) -->
-    <skip />
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <!-- no translation found for activity_not_found (8071924732094499514) -->
-    <skip />
-    <!-- no translation found for widgets_tab_label (2921133187116603919) -->
-    <skip />
-    <!-- no translation found for widget_adder (3201040140710381657) -->
-    <skip />
-    <!-- no translation found for toggle_weight_watcher (5645299835184636119) -->
-    <skip />
-    <!-- no translation found for long_press_widget_to_add (7699152356777458215) -->
-    <skip />
-    <!-- no translation found for market (2619650989819296998) -->
-    <skip />
-    <!-- no translation found for widget_dims_format (2370757736025621599) -->
-    <skip />
-    <!-- no translation found for external_drop_widget_error (3165821058322217155) -->
-    <skip />
-    <!-- no translation found for external_drop_widget_pick_title (3486317258037690630) -->
-    <skip />
-    <!-- no translation found for rename_folder_label (3727762225964550653) -->
-    <skip />
-    <!-- no translation found for rename_folder_title (3771389277707820891) -->
-    <skip />
-    <!-- no translation found for rename_action (5559600076028658757) -->
-    <skip />
-    <!-- no translation found for cancel_action (7009134900002915310) -->
-    <skip />
-    <!-- no translation found for menu_item_add_item (1264911265836810421) -->
-    <skip />
-    <!-- no translation found for group_applications (3797214114206693605) -->
-    <skip />
-    <!-- no translation found for group_shortcuts (6012256992764410535) -->
-    <skip />
-    <!-- no translation found for group_widgets (1569030723286851002) -->
-    <skip />
-    <!-- no translation found for completely_out_of_space (6106288382070760318) -->
-    <skip />
-    <!-- no translation found for out_of_space (4691004494942118364) -->
-    <skip />
-    <!-- no translation found for hotseat_out_of_space (7448809638125333693) -->
-    <skip />
-    <!-- no translation found for invalid_hotseat_item (5779907847267573691) -->
-    <skip />
-    <!-- no translation found for shortcut_installed (1701742129426969556) -->
-    <skip />
-    <!-- no translation found for shortcut_uninstalled (8176767991305701821) -->
-    <skip />
-    <!-- no translation found for shortcut_duplicate (9167217446062498127) -->
-    <skip />
-    <!-- no translation found for title_select_shortcut (6680642571148153868) -->
-    <skip />
-    <!-- no translation found for title_select_application (3280812711670683644) -->
-    <skip />
-    <!-- no translation found for all_apps_button_label (9110807029020582876) -->
-    <skip />
-    <!-- no translation found for all_apps_home_button_label (252062713717058851) -->
-    <skip />
-    <!-- no translation found for delete_zone_label_workspace (4009607676751398685) -->
-    <skip />
-    <!-- no translation found for delete_zone_label_all_apps (8083826390278958980) -->
-    <skip />
-    <!-- no translation found for delete_target_label (1822697352535677073) -->
-    <skip />
-    <!-- no translation found for delete_target_uninstall_label (5100785476250872595) -->
-    <skip />
-    <!-- no translation found for info_target_label (8053346143994679532) -->
-    <skip />
-    <!-- no translation found for accessibility_search_button (1628520399424565142) -->
-    <skip />
-    <!-- no translation found for accessibility_voice_search_button (4637324840434406584) -->
-    <skip />
-    <!-- no translation found for accessibility_all_apps_button (2603132375383800483) -->
-    <skip />
-    <!-- no translation found for accessibility_delete_button (6466114477993744621) -->
-    <skip />
-    <!-- no translation found for delete_zone_label_all_apps_system_app (449755632749610895) -->
-    <skip />
-    <!-- no translation found for cab_menu_delete_app (7435191475867183689) -->
-    <skip />
-    <!-- no translation found for cab_menu_app_info (8593722221450362342) -->
-    <skip />
-    <!-- no translation found for cab_app_selection_text (374688303047985416) -->
-    <skip />
-    <!-- no translation found for cab_widget_selection_text (1833458597831541241) -->
-    <skip />
-    <!-- no translation found for cab_folder_selection_text (7999992513806132118) -->
-    <skip />
-    <!-- no translation found for cab_shortcut_selection_text (2103811025667946450) -->
-    <skip />
-    <!-- no translation found for permlab_install_shortcut (5632423390354674437) -->
-    <skip />
-    <!-- no translation found for permdesc_install_shortcut (923466509822011139) -->
-    <skip />
-    <!-- no translation found for permlab_uninstall_shortcut (864595034498083837) -->
-    <skip />
-    <!-- no translation found for permdesc_uninstall_shortcut (5134129545001836849) -->
-    <skip />
-    <!-- no translation found for permlab_read_settings (1941457408239617576) -->
-    <skip />
-    <!-- no translation found for permdesc_read_settings (5833423719057558387) -->
-    <skip />
-    <!-- no translation found for permlab_write_settings (3574213698004620587) -->
-    <skip />
-    <!-- no translation found for permdesc_write_settings (5440712911516509985) -->
-    <skip />
-    <!-- no translation found for gadget_error_text (6081085226050792095) -->
-    <skip />
-    <!-- no translation found for uninstall_system_app_text (4172046090762920660) -->
-    <skip />
-    <!-- no translation found for dream_name (1530253749244328964) -->
-    <skip />
-    <!-- no translation found for folder_hint_text (6617836969016293992) -->
-    <skip />
-    <!-- no translation found for workspace_description_format (2950174241104043327) -->
-    <skip />
-    <!-- no translation found for default_scroll_format (7475544710230993317) -->
-    <skip />
-    <!-- no translation found for workspace_scroll_format (8458889198184077399) -->
-    <skip />
-    <!-- no translation found for apps_customize_apps_scroll_format (370005296147130238) -->
-    <skip />
-    <!-- no translation found for apps_customize_widgets_scroll_format (3106209519974971521) -->
-    <skip />
-    <!-- no translation found for first_run_cling_title (2459738000155917941) -->
-    <skip />
-    <!-- no translation found for first_run_cling_description (6447072552696253358) -->
-    <skip />
-    <!-- no translation found for first_run_cling_create_screens_hint (6950729526680114157) -->
-    <skip />
-    <!-- no translation found for migration_cling_title (9181776667882933767) -->
-    <skip />
-    <!-- no translation found for migration_cling_description (2752413805582227644) -->
-    <skip />
-    <!-- no translation found for migration_cling_copy_apps (946331230090919440) -->
-    <skip />
-    <!-- no translation found for migration_cling_use_default (2626475813981258626) -->
-    <skip />
-    <!-- no translation found for workspace_cling_title (5626202359865825661) -->
-    <skip />
-    <!-- no translation found for workspace_cling_move_item (528201129978005352) -->
-    <skip />
-    <!-- no translation found for folder_cling_title (3894908818693254164) -->
-    <skip />
-    <!-- no translation found for folder_cling_create_folder (6158215559475836131) -->
-    <skip />
-    <!-- no translation found for cling_dismiss (8962359497601507581) -->
-    <skip />
-    <!-- no translation found for folder_opened (94695026776264709) -->
-    <skip />
-    <!-- no translation found for folder_tap_to_close (1884479294466410023) -->
-    <skip />
-    <!-- no translation found for folder_tap_to_rename (9191075570492871147) -->
-    <skip />
-    <!-- no translation found for folder_closed (4100806530910930934) -->
-    <skip />
-    <!-- no translation found for folder_renamed (1794088362165669656) -->
-    <skip />
-    <!-- no translation found for folder_name_format (6629239338071103179) -->
-    <skip />
-    <!-- no translation found for widget_button_text (2880537293434387943) -->
-    <skip />
-    <!-- no translation found for wallpaper_button_text (8404103075899945851) -->
-    <skip />
-    <!-- no translation found for settings_button_text (8119458837558863227) -->
-    <skip />
-    <!-- no translation found for package_state_enqueued (6227252464303085641) -->
-    <skip />
-    <!-- no translation found for package_state_downloading (4088770468458724721) -->
-    <skip />
-    <!-- no translation found for package_state_installing (7588193972189849870) -->
-    <skip />
-    <!-- no translation found for package_state_unknown (7592128424511031410) -->
-    <skip />
-    <!-- no translation found for package_state_error (7672093962724223588) -->
-    <skip />
-</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index fb8e361..20b3353 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -26,28 +26,46 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicația descărcată este dezactivată în modul de siguranță"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgeturile sunt dezactivate în modul de siguranță"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Comanda rapidă nu este disponibilă"</string>
-    <string name="home_screen" msgid="806512411299847073">"Ecran de pornire"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Acțiuni personalizate"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Atingeți lung un widget pentru a-l alege."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Atingeți de două ori și mențineți apăsat ca să alegeți un widget sau folosiți acțiuni personalizate."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lățime și %2$d înălțime"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Atingeți lung pentru a plasa manual"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Adăugați automat"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Ștergeți textul din caseta de căutare"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Niciun widget disponibil"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Niciun rezultat al căutării"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personale"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Serviciu"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Conversații"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Căutați aplicații"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Se încarcă aplicații…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Notificări"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Atingeți lung pentru a selecta o comandă rapidă."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Atingeți lung pentru a selecta o comandă rapidă sau folosiți acțiuni personalizate."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Nu mai este loc pe acest Ecran de pornire."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Spațiu epuizat în bara Preferate"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicații"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicații personale"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicații de serviciu"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Ecran de pornire"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminați"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalați"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Informații despre aplicații"</string>
@@ -81,7 +99,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Dosar redenumit <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elemente"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> sau mai multe elemente"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiluri și imagini de fundal"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Setări ecran de pornire"</string>
@@ -102,9 +119,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Căutați"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplicația nu este instalată"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplicația pentru această pictogramă nu este instalată. Puteți să ștergeți pictograma sau să căutați aplicația și s-o instalați manual."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se instalează, <xliff:g id="PROGRESS">%2$s</xliff:g> finalizat"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se descarcă (finalizat <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> așteaptă instalarea"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgeturi <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Listă de widgeturi"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgeturi este închisă"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Adăugați pe ecranul de pornire"</string>
@@ -142,8 +159,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Înainte"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Profilul de serviciu este întrerupt"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Aplicațiile pentru lucru nu pot să vă trimită notificări, să folosească bateria sau să vă acceseze locația"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profilul de serviciu este întrerupt. Aplicațiile pentru lucru nu pot să vă trimită notificări, să folosească bateria sau să vă acceseze locația"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Aplicațiile pentru lucru nu pot să vă trimită notificări, să folosească bateria sau să vă acceseze locația"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Profilul de serviciu este întrerupt. Aplicațiile pentru lucru nu pot să vă trimită notificări, să folosească bateria sau să vă acceseze locația"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Aplicațiile pentru lucru sunt marcate și vizibile pentru administratorul IT"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Întrerupeți aplicațiile pentru lucru"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Activați"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtru"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Întrerupeți aplicațiile pentru lucru și notificările"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Eșuare: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 4d8ba88..24aabc5 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -26,28 +26,48 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Скачанное приложение отключено в безопасном режиме"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Виджеты отключены в безопасном режиме"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Ярлык недоступен"</string>
-    <string name="home_screen" msgid="806512411299847073">"Главный экран"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Специальные действия"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Чтобы выбрать виджет, нажмите на значок и удерживайте его."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Чтобы выбрать виджет или использовать специальные действия, нажмите на него дважды и не отпускайте."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Главный экран"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделить экран"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d, высота %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Нажмите и удерживайте, чтобы добавить вручную."</string>
     <string name="place_automatically" msgid="8064208734425456485">"Добавить автоматически"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Удалить текст из окна поиска"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Нет доступных виджетов."</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Ничего не найдено."</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Личные виджеты"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Рабочие виджеты"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Разговоры"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Поиск приложений"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Загрузка приложений…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"По запросу \"<xliff:g id="QUERY">%1$s</xliff:g>\" ничего не найдено"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Искать другие приложения"</string>
     <string name="label_application" msgid="8531721983832654978">"Приложение"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Уведомления"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Нажмите и удерживайте, чтобы выбрать ярлык."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Нажмите дважды и удерживайте, чтобы выбрать ярлык или использовать специальные действия."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Нажмите и удерживайте для переноса ярлыка."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Чтобы использовать специальные действия или перенести ярлык, нажмите на него дважды и удерживайте."</string>
     <string name="out_of_space" msgid="4691004494942118364">"На этом экране все занято"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"В разделе \"Избранное\" больше нет места"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Список приложений"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Открыть список личных приложений"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Открыть список приложений для работы"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Главный экран"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Убрать"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Удалить"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"О приложении"</string>
@@ -82,7 +102,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Папка переименована в \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g>)"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g> или больше)"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стили и обои"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Главный экран"</string>
@@ -103,15 +122,15 @@
     <string name="abandoned_search" msgid="891119232568284442">"Найти"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Приложение не установлено"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Приложение не установлено. Вы можете удалить значок или найти приложение и установить его вручную."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Установка приложения \"<xliff:g id="NAME">%1$s</xliff:g>\" (выполнено <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Скачивается \"<xliff:g id="NAME">%1$s</xliff:g>\" (<xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Ожидание установки \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>: виджеты"</string>
     <string name="widgets_list" msgid="796804551140113767">"Список виджетов"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Список виджетов закрыт"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Добавить на главный экран"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Переместить элемент сюда"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент добавлен на главный экран"</string>
-    <string name="item_removed" msgid="851119963877842327">"Элемент удален"</string>
+    <string name="item_removed" msgid="851119963877842327">"Элемент удален."</string>
     <string name="undo" msgid="4151576204245173321">"Отменить"</string>
     <string name="action_move" msgid="4339390619886385032">"Переместить элемент"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Переместить в ячейку <xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
@@ -143,8 +162,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Далее"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ОК"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Действие рабочего профиля приостановлено."</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Рабочие приложения не могут отправлять уведомления, расходовать заряд батареи и получать доступ к вашему местоположению."</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Рабочий профиль приостановлен. Рабочие приложения не могут отправлять уведомления, расходовать заряд батареи и получать доступ к данным о вашем местоположении."</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Рабочие приложения не могут отправлять уведомления, расходовать заряд батареи и получать доступ к данным о вашем местоположении."</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Рабочий профиль приостановлен. Рабочие приложения не могут отправлять уведомления, расходовать заряд батареи и получать доступ к данным о вашем местоположении."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"У рабочих приложений есть специальная пометка. Они видны системному администратору."</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"ОК"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Приостановить рабочие приложения"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Включить"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Фильтр"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Приостановить рабочие приложения и уведомления"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не удалось выполнить действие (<xliff:g id="WHAT">%1$s</xliff:g>)."</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index bfc6b7a..22baa2a 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ආරක්ෂිත ආකාරය තුළ බාගන්න ලද යෙදුම් අබල කරන්න"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"සුරක්ෂිත ආකාරය තුළ විජටය අබල කරන ලදි"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"කෙටි මග ලබා ගත නොහැකිය"</string>
-    <string name="home_screen" msgid="806512411299847073">"මුල් පිටු තිරය"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"අභිරුචි ක්‍රියා"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"විජට් එක ස්පර්ශ කර අහුලා ගැනීමට අල්ලාගෙන සිටින්න."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"විජට් එකක් අහුලා ගැනීමට හෝ අභිරුචි ක්‍රියා කිරීමට ඩබල් ටැප් කර අල්ලා ගෙන සිටින්න."</string>
+    <string name="home_screen" msgid="5629429142036709174">"මුල් පිටුව"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"බෙදුම් තිරය"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"පළල %1$d උස %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"අතින් ස්ථානගත කිරීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න"</string>
     <string name="place_automatically" msgid="8064208734425456485">"ස්වයංක්‍රියව එක් කරන්න"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"සෙවීම් කොටුවෙන් පෙළ හිස් කරන්න"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ලබා ගත හැකි විජට් නැත"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"සෙවීම් ප්‍රතිඵල නැත"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"පුද්ගලික"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"කාර්යාලය"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"සංවාද"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"යෙදුම් සොයන්න"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"යෙදුම් පූරණය වෙමින්…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" සමග ගැළපෙන යෙදුම් හමු නොවිණි"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"තව යෙදුම් සඳහා සොයන්න"</string>
     <string name="label_application" msgid="8531721983832654978">"යෙදුම"</string>
     <string name="notifications_header" msgid="1404149926117359025">"දැනුම්දීම්"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"කෙටි මගක් තෝරා ගැනීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"විජට් එකක් තෝරා ගැනීමට හෝ අභිරුචි භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"කෙටි මගක් ගෙන යාමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"කෙටි මගක් ගෙන යාමට හෝ අභිරුචි ක්‍රියා භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
     <string name="out_of_space" msgid="4691004494942118364">"මෙම මුල් පිටු තිරය මත තවත් අවසර නැත."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"යෙදුම් ලැයිස්තුව"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"පෞද්ගලික යෙදුම් ලැයිස්තුව"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"වැඩ යෙදුම් ලැයිස්තුව"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"මුල් පිටුව"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ඉවත් කරන්න"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"අස්ථාපනය කරන්න"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"යෙදුම් තොරතුරු"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"<xliff:g id="NAME">%1$s</xliff:g> වෙත ෆෝල්ඩරය නැවත නම් කෙරිණි"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ෆෝල්ඩර: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>ක් හෝ වැඩි ගණනක්"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"විලාස සහ වෝල්පේපර"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"නිවසේ සැකසීම්"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"සොයන්න"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"මෙම යෙදුම ස්ථාපනය කර නොමැත"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"මෙම නිරුපකයට යෙදුම ස්ථාපනය කර නොමැත. ඔබට එය ඉවත් කළ හැක, හෝ යෙදුම් සඳහා සොයන්න සහ අතින් ස්ථාපනය කරන්න."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ස්ථාපනය කරමින්, <xliff:g id="PROGRESS">%2$s</xliff:g> සම්පූර්ණයි"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> බාගත කරමින්, <xliff:g id="PROGRESS">%2$s</xliff:g> සම්පූර්ණයි"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ස්ථාපනය කිරීමට බලා සිටිමින්"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> විජට්"</string>
     <string name="widgets_list" msgid="796804551140113767">"විජට් ලැයිස්තුව"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"විජට් ලැයිස්තුව වසා ඇත"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"මුල් තිරය වෙත එක් කරන්න"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ඊළඟ"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"තේරුණා"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"කාර්යාල පැතිකඩ විරාම කර ඇත"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"කාර්යාල යෙදුම්වලට ඔබට දැනුම් දීම් එවීමට, ඔබගේ බැටරිය භාවිත කිරීමට හෝ ඔබගේ ස්ථානයට ප්‍රවේශ විය නොහැකිය"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"කාර්යාල පැතිකඩ විරාම කර ඇත. කාර්යාල යෙදුම්වලට ඔබට දැනුම් දීම් එවීමට, ඔබගේ බැටරිය භාවිත කිරීමට හෝ ඔබගේ ස්ථානයට ප්‍රවේශ විය නොහැකිය"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"කාර්යාල යෙදුම්වලට ඔබට දැනුම්දීම් එවීමට, ඔබගේ බැටරිය භාවිත කිරීමට හෝ ඔබගේ ස්ථානයට ප්‍රවේශ වීමට නොහැකිය"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"කාර්යාල පැතිකඩ විරාම කර ඇත. කාර්යාල යෙදුම්වලට ඔබට දැනුම්දීම් එවීමට, ඔබගේ බැටරිය භාවිත කිරීමට හෝ ඔබගේ ස්ථානයට ප්‍රවේශ වීමට නොහැකිය"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"කාර්යාල යෙදුම්වලට ලාංඡන යොදා ඇති අතර ඔබගේ IT පරිපාලකට දෘශ්‍යමාන වේ"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"තේරුණා"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"කාර්යාල යෙදුම් විරාම කරන්න"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ක්‍රියාත්මක කරන්න"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"පෙරහන"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"කාර්යාල යෙදුම් සහ දැනුම් දීම් විරාම කරන්න"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"අසාර්ථකයි: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index e3b7894..1bcf528 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -26,28 +26,48 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Stiahnutá aplikácia je v núdzovom režime zakázaná"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Miniaplikácie sú v núdzovom režime zakázané"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Skratky nie sú k dispozícii"</string>
-    <string name="home_screen" msgid="806512411299847073">"Plocha"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Vlastné akcie"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Miniaplikáciu pridáte stlačením a podržaním."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Miniaplikáciu pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Domov"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdeliť obrazovku"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šírka %1$d, výška %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Ak chcete položku umiestniť ručne, pridržte ju"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Pridať automaticky"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Vymazať text z vyhľadávacieho poľa"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nie sú k dispozícii žiadne miniaplikácie"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Žiadne výsledky vyhľadávania"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osobné"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Práca"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Konverzácie"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hľadať aplikácie"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Načítavajú sa aplikácie…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Upozornenia"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Skratku pridáte pridržaním."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Skratku pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Na tejto ploche už nie je miesto"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Na paneli Obľúbené položky už nie je miesto"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Zoznam aplikácií"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Zoznam osobných aplikácií"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Zoznam pracovných aplikácií"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Domovská stránka"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrániť"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinštalovať"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikácii"</string>
@@ -82,7 +102,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Priečinok bol premenovaný na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> položky"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> alebo viac položiek"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Štýly a tapety"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia plochy"</string>
@@ -103,9 +122,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Vyhľadať"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Táto aplikácia nie je nainštalovaná"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikácia, ktorú zastupuje táto ikona, nie je nainštalovaná. Ikonu môžete odstrániť alebo vyhľadajte aplikáciu a ručne ju nainštalujte."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Inštaluje sa <xliff:g id="NAME">%1$s</xliff:g>. Dokončené: <xliff:g id="PROGRESS">%2$s</xliff:g>."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Sťahuje sa aplikácia <xliff:g id="NAME">%1$s</xliff:g>. Stiahnuté: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Aplikácia <xliff:g id="NAME">%1$s</xliff:g> čaká na inštaláciu"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Miniaplikácie <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Zoznam miniaplikácií"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Zoznam miniaplikácií je zavretý"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Pridať na plochu"</string>
@@ -143,8 +162,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Ďalej"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Dobre"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Pracovný profil je pozastavený"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Pracovný profil je pozastavený. Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Pracovný profil je pozastavený. Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Pracovné aplikácie majú odznak a zobrazujú sa správcovi IT"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Dobre"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pozastaviť pracovné aplikácie"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Zapnúť"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtrujte"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pozastavte pracovné aplikácie a upozornenia"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Zlyhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 7ec3652..9e60efb 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -26,28 +26,48 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Prenesena aplikacija je onemogočena v Varnem načinu"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Pripomočki so onemogočeni v varnem načinu"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Bližnjica ni na voljo"</string>
-    <string name="home_screen" msgid="806512411299847073">"Začetni zaslon"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Dejanja po meri"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Za izbiro pripomočka se ga dotaknite in pridržite."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Če želite izbrati pripomoček ali uporabiti dejanja po meri, se ga dvakrat dotaknite in ga pridržite."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, višina %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dotaknite se elementa in ga pridržite, da ga ročno dodate"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Samodejno dodaj"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Brisanje besedila v iskalnem polju"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Ni pripomočkov"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Ni rezultatov iskanja"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Osebno"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Služba"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Pogovori"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Iskanje programov"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Nalaganje aplikacij …"</string>
     <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="notifications_header" msgid="1404149926117359025">"Obvestila"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Pridržite bližnjico, da jo izberete."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvakrat se dotaknite bližnjice in jo pridržite, da jo izberete, ali pa uporabite dejanja po meri."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Na tem začetnem zaslonu ni več prostora."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"V vrstici za priljubljene ni več prostora"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Seznam aplikacij"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Seznam osebnih aplikacij"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Seznam delovnih aplikacij"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Začetni zaslon"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrani"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odmesti"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Podatki o aplikaciji"</string>
@@ -82,12 +102,11 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana v <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, št. elementov: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ali več elementov"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Pripomočki"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Slogi in ozadja"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Domače nastavitve"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogočil skrbnik."</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Omogočanje sukanja začetnega zaslona"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Omogoči sukanje začetnega zaslona"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Ko se telefon zasuka"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"Obvestilne pike"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"Vklopljeno"</string>
@@ -103,9 +122,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Iskanje"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacija ni nameščena."</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija za to ikono ni nameščena. Lahko jo odstranite ali poiščete aplikacijo in to namestite ročno."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se namešča, dokončano: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Prenašanje aplikacije <xliff:g id="NAME">%1$s</xliff:g>; preneseno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> čaka na namestitev"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Pripomočki za <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Seznam pripomočkov"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Seznam pripomočkov se je zaprl"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodajanje na začetni zaslon"</string>
@@ -143,8 +162,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Naprej"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumem"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Delovni profil je začasno zaustavljen"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Delovne aplikacije ne smejo pošiljati obvestil, porabljati energije baterije ali dostopati do lokacije"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Delovni profil je začasno zaustavljen. Delovne aplikacije ne smejo pošiljati obvestil, porabljati energije baterije ali dostopati do lokacije"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Delovne aplikacije ne smejo pošiljati obvestil, porabljati energije baterije ali dostopati do lokacije."</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Delovni profil je začasno zaustavljen. Delovne aplikacije ne smejo pošiljati obvestil, porabljati energije baterije ali dostopati do lokacije."</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Delovne aplikacije so označene z značko in vidne skrbniku za IT."</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"V redu"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Začasno zaustavi delovne aplikacije"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Vklopi"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtriranje"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Začasna zaustavitev delovnih aplikacij in obvestil"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Ni uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index c021061..1ed8070 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplikacioni i shkarkuar është i çaktivizuar në modalitetin e sigurt"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Miniaplikacionet janë të çaktivizuara në modalitetin e sigurt"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Shkurtorja nuk është e disponueshme"</string>
-    <string name="home_screen" msgid="806512411299847073">"Ekrani bazë"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Veprimet e personalizuara"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Prek dhe mbaj shtypur për të zgjedhur një miniaplikacion."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Prek dy herë dhe mbaj shtypur për të zgjedhur një miniaplikacion ose për të përdorur veprimet e personalizuara."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i gjerë me %2$d i lartë"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Prek dhe mbaj të shtypur për të vendosur në mënyrë manuale"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Shto automatikisht"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Pastro tekstin nga kutia e kërkimit"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Nuk ka asnjë miniaplikacion"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Asnjë rezultat kërkimi"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personale"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Puna"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Bisedat"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Kërko për aplikacione"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Po ngarkon aplikacionet..."</string>
     <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="notifications_header" msgid="1404149926117359025">"Njoftimet"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Prek dhe mbaj të shtypur për të zgjedhur një shkurtore."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Prek dy herë dhe mbaj të shtypur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Nuk ka më hapësirë në këtë ekran bazë."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nuk ka më hapësirë në tabakanë \"Të preferuarat\""</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Lista e aplikacioneve"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista e aplikacioneve personale"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista e aplikacioneve të punës"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Faqja kryesore"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Hiqe"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Çinstalo"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Info mbi aplikacionin"</string>
@@ -80,12 +96,11 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Dosja u riemërtua në <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> artikuj"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ose më shumë artikuj"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilet dhe imazhet e sfondit"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Cilësimet e ekranit bazë"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Çaktivizuar nga administratori"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Lejo rrotullimin e ekranit kryesor"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Lejo rrotullimin e ekranit bazë"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kur telefoni rrotullohet"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"Pikat e njoftimeve"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktiv"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Kërko"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikacioni nuk është i instaluar"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacioni për këtë ikonë nuk është i instaluar. Mund ta heqësh ose të kërkosh aplikacionin dhe ta instalosh atë në mënyrë manuale."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> po instalohet, <xliff:g id="PROGRESS">%2$s</xliff:g> i përfunduar"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> po shkarkohet, <xliff:g id="PROGRESS">%2$s</xliff:g> të përfunduara"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> po pret të instalohet"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Miniaplikacionet e <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista e miniaplikacioneve"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lista e miniaplikacioneve u mbyll"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Shto në Ekranin bazë"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Para"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"E kuptova"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Profili i punës është në pauzë"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Aplikacionet e punës nuk mund të të dërgojnë njoftime, të përdorin baterinë tënde apo të kenë qasje në vendndodhjen tënde"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profili i punës është në pauzë. Aplikacionet e punës nuk mund të të dërgojnë njoftime, të përdorin baterinë tënde apo të kenë qasje në vendndodhjen tënde"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Aplikacionet e punës nuk mund të të dërgojnë njoftime, të përdorin baterinë tënde apo të kenë qasje në vendndodhjen tënde"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Profili i punës është në pauzë. Aplikacionet e punës nuk mund të të dërgojnë njoftime, të përdorin baterinë tënde apo të kenë qasje në vendndodhjen tënde"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Aplikacionet e punës janë të shënuara dhe të dukshme për administratorin e teknologjisë së informacionit"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"E kuptova"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Vendos në pauzë aplikacionet e punës"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aktivizo"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtro"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Vendos në pauzë aplikacionet e punës dhe njoftimet"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Dështoi: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index a197f9e..4809087 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -26,28 +26,46 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Преузета апликација је онемогућена у Безбедном режиму"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Виџети су онемогућени у Безбедном режиму"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Пречица није доступна"</string>
-    <string name="home_screen" msgid="806512411299847073">"Почетни екран"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Прилагођене радње"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Додирните и задржите да бисте изабрали виџет."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Двапут додирните и задржите да бисте изабрали виџет или користите прилагођене радње."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Почетни екран"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Подељени екран"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ширина од %1$d и висина од %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Додирните и задржите да бисте поставили ручно"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Аутоматски додај"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Обришите текст из оквира за претрагу"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Није доступан ниједан виџет"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Нема резултата претраге"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Лично"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Посао"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Конверзације"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Претражите апликације"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Апликације се учитавају…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Није пронађена ниједна апликација за „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Претражи још апликација"</string>
     <string name="label_application" msgid="8531721983832654978">"Апликација"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Обавештења"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Додирните и задржите да бисте изабрали пречицу."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Двапут додирните и задржите да бисте изабрали пречицу или користите прилагођене радње."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Двапут додирните и задржите ради померања пречице."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Двапут додирните и задржите да бисте померали пречицу или користите прилагођене радње."</string>
     <string name="out_of_space" msgid="4691004494942118364">"Нема више простора на овом почетном екрану."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема више простора на траци Омиљено"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Листа апликација"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Листа личних апликација"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Листа пословних апликација"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Уклони"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Инфор. о апликацији"</string>
@@ -74,14 +92,13 @@
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. страница од %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. почетни екран од %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова страница почетног екрана"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Директоријум је отворен, <xliff:g id="WIDTH">%1$d</xliff:g> пута <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Додирните да бисте затворили директоријум"</string>
+    <string name="folder_opened" msgid="94695026776264709">"Фолдер је отворен, <xliff:g id="WIDTH">%1$d</xliff:g> пута <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Додирните да бисте затворили фолдер"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Додирните да бисте сачували преименовање"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Директоријум је затворен"</string>
-    <string name="folder_renamed" msgid="1794088362165669656">"Директоријум је преименован у <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_name_format_exact" msgid="8626242716117004803">"Директоријум: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставке"</string>
-    <string name="folder_name_format_overflow" msgid="4270108890534995199">"Директоријум: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или више ставки"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
+    <string name="folder_closed" msgid="4100806530910930934">"Фолдер је затворен"</string>
+    <string name="folder_renamed" msgid="1794088362165669656">"Фолдер је преименован у <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="folder_name_format_exact" msgid="8626242716117004803">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставке"</string>
+    <string name="folder_name_format_overflow" msgid="4270108890534995199">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или више ставки"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилови и позадине"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Подешавања почетног екрана"</string>
@@ -102,9 +119,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Претражи"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ова апликација није инсталирана"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Апликација за ову икону није инсталирана. Можете да је уклоните или да потражите апликацију и инсталирате је ручно."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> готово"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> се преузима, завршено је <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чека на инсталирање"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Виџети за <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Листа виџета"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Листа виџета је затворена"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Додајте на почетни екран"</string>
@@ -117,11 +134,11 @@
     <string name="move_to_position" msgid="6750008980455459790">"Премести на <xliff:g id="NUMBER">%1$s</xliff:g>. позицију"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Премести на <xliff:g id="NUMBER">%1$s</xliff:g>. позицију у омиљеним"</string>
     <string name="item_moved" msgid="4606538322571412879">"Ставка је премештена"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Додај у директоријум: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Додај у директоријум у коме је <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Ставка је додата у директоријум"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Направите директоријум са: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Директоријум је направљен"</string>
+    <string name="add_to_folder" msgid="9040534766770853243">"Додај у фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Додај у фолдер у коме је <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="added_to_folder" msgid="4793259502305558003">"Ставка је додата у фолдер"</string>
+    <string name="create_folder_with" msgid="4050141361160214248">"Направите фолдер са: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="folder_created" msgid="6409794597405184510">"Фолдер је направљен"</string>
     <string name="action_move_to_workspace" msgid="1603837886334246317">"Премести на почетни екран"</string>
     <string name="action_resize" msgid="1802976324781771067">"Промени величину"</string>
     <string name="action_increase_width" msgid="8773715375078513326">"Повећај ширину"</string>
@@ -142,8 +159,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Даље"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Важи"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Пословни профил је паузиран"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Пословне апликације не могу да вам шаљу обавештења, користе батерију нити приступају локацији"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Пословни профил је паузиран. Пословне апликације не могу да вам шаљу обавештења, користе батерију нити приступају локацији"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Пословне апликације не могу да вам шаљу обавештења, користе батерију нити приступају локацији"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Пословни профил је паузиран. Пословне апликације не могу да вам шаљу обавештења, користе батерију нити приступају локацији"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Пословне апликације су означене значком и ИТ администратор може да их види"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Важи"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Паузирај пословне апликације"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Укључи"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтер"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Паузирајте пословне апликације и обавештења"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Није успело: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index b8beb9b..aad4e4e 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Den hämtade appen inaktiverades i säkert läge"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets är inaktiverade i felsäkert läge"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Genvägen är inte tillgänglig"</string>
-    <string name="home_screen" msgid="806512411299847073">"Startskärm"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Anpassade åtgärder"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Tryck länge om du vill flytta en widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryck två gånger och håll kvar om du vill ta upp en widget eller använda anpassade åtgärder."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bred gånger %2$d hög"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Placera manuellt genom att trycka länge"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Lägg till automatiskt"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Rensa texten från sökrutan"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Det finns inga tillgängliga widgetar"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Inga sökresultat"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Privata widgetar"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Arbete"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Konversationer"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Sök efter appar"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Läser in appar …"</string>
     <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="notifications_header" msgid="1404149926117359025">"Aviseringar"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tryck länge om du vill ta upp en genväg."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tryck snabbt två gånger och håll kvar om du vill ta upp en genväg eller använda anpassade åtgärder."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Det finns inte plats för mer på den här startskärmen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritfältet är fullt"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Applista"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Listan Personliga appar"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Listan Jobbappar"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Startskärm"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Ta bort"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstallera"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Mappen har bytt namn till <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> objekt"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller fler objekt"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgetar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Format och bakgrunder"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Startinställningar"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Sök"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Appen är inte installerad"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen för den här ikonen har inte installerats. Du kan ta bort den eller söka efter appen och installera den manuellt."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeras. <xliff:g id="PROGRESS">%2$s</xliff:g> har slutförts"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> laddas ned, <xliff:g id="PROGRESS">%2$s</xliff:g> klart"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> väntar på installation"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgetar"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widgetlista"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widgetslistan har stängts"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Lägg till på startskärmen"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Nästa"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Jobbprofilen är pausad"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Jobbappar kan inte skicka aviseringar, använda batteriet eller komma åt din plats"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Jobbprofilen är pausad. Jobbappar kan inte skicka aviseringar, använda batteriet eller komma åt din plats"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Jobbappar kan inte skicka aviseringar, använda batteriet eller komma åt din plats"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Jobbprofilen är pausad. Jobbappar kan inte skicka aviseringar, använda batteriet eller komma åt din plats"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Jobbappar är märkta och synliga för IT-administratören"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Pausa jobbappar"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aktivera"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Pausa jobbappar och jobbaviseringar"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Misslyckades: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index b91ed0a..3ff05e1 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Programu iliyopakuliwa imezimwa katika Hali Salama"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Wijeti zimezimwa katika hali ya Usalama"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Hakuna njia ya mkato"</string>
-    <string name="home_screen" msgid="806512411299847073">"Skrini ya kwanza"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Vitendo maalum"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Gusa na ushikilie ili kuteua wijeti."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Gusa mara mbili na ushikilie ile uchague wijeti au utumie vitendo maalum."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Upana wa %1$d na kimo cha %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Gusa na ushikilie ili uweke mwenyewe"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Ongeza kiotomatiki"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Futa maandishi kwenye kisanduku cha kutafutia"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Hamna wijeti zinazopatikana"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Hamna matokeo ya utafutaji"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Binafsi"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Kazini"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Mazungumzo"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tafuta programu"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Inapakia programu..."</string>
     <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="notifications_header" msgid="1404149926117359025">"Arifa"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Gusa na ushikilie ili uchague njia ya mkato."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Gusa mara mbili na ushikilie ili uchague njia ya mkato au utumie vitendo maalum."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Hakuna nafasi katika skrini hii ya Mwanzo."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Hakuna nafasi zaidi katika treya ya Vipendeleo"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Orodha ya programu"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Orodha ya programu za binafsi"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Orodha ya programu za kazini"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Mwanzo"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Ondoa"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sakinua"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Maelezo ya programu"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Folda imebadilishwa jina kuwa <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folda: <xliff:g id="NAME">%1$s</xliff:g>, vipengee <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folda: <xliff:g id="NAME">%1$s</xliff:g>, vipengee <xliff:g id="SIZE">%2$d</xliff:g> au zaidi"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Wijeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mitindo na mandhari"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Mipangilio ya mwanzo"</string>
@@ -101,12 +116,12 @@
     <string name="abandoned_search" msgid="891119232568284442">"Tafuta"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Programu hii haijasakinishwa"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Programu ya aikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Inasakinisha <xliff:g id="NAME">%1$s</xliff:g>, imekamilika <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> inapakuliwa, <xliff:g id="PROGRESS">%2$s</xliff:g> imekamilika"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> inasubiri kusakinisha"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Wijeti za <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Orodha ya wijeti"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Orodha ya wijeti imefungwa"</string>
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"Ongeza kwenye Skrini ya Kwanza"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Weka kwenye Skrini ya Kwanza"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Hamishia kipengee hapa"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Kipengee kimeongezwa kwenye skrini ya kwanza"</string>
     <string name="item_removed" msgid="851119963877842327">"Kipengee kimeondolewa"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Endelea"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Nimeelewa"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Wasifu wa kazini umesimamishwa"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Programu za kazini haziwezi kukutumia arifa, kutumia betri yako au kufikia maelezo ya mahali ulipo"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Wasifu wa kazini umesimamishwa. Programu za kazini haziwezi kukutumia arifa, kutumia betri yako au kufikia maelezo ya mahali ulipo"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Programu za kazini haziwezi kukutumia arifa, kutumia betri yako au kufikia maelezo ya mahali ulipo"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Wasifu wa kazini umesimamishwa. Programu za kazini haziwezi kukutumia arifa, kutumia betri yako au kufikia maelezo ya mahali ulipo"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Programu za kazini zina beji na zinaonekana kwa msimamizi wako wa TEHAMA"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Nimeelewa"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Simamisha programu za kazini"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Washa"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Kichujio"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Simamisha arifa na programu za kazini"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Hitilafu: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sw340dp/dimens.xml b/res/values-sw340dp/dimens.xml
index c9f2981..33b06f5 100644
--- a/res/values-sw340dp/dimens.xml
+++ b/res/values-sw340dp/dimens.xml
@@ -19,6 +19,5 @@
 
     <!-- Drag padding to add to the bottom of drop targets -->
     <dimen name="drop_target_drag_padding">20dp</dimen>
-    <dimen name="drop_target_text_size">16sp</dimen>
 
 </resources>
\ No newline at end of file
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
deleted file mode 100644
index 09bdaaf..0000000
--- a/res/values-sw600dp/config.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<resources>
-    <bool name="allow_rotation">true</bool>
-</resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index ead666c..47a88f2 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -3,7 +3,7 @@
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
+     You may obtain a copy of the License at`
 
           http://www.apache.org/licenses/LICENSE-2.0
 
@@ -15,13 +15,6 @@
 -->
 
 <resources>
-<!-- All Apps -->
-    <dimen name="all_apps_background_canvas_width">850dp</dimen>
-    <dimen name="all_apps_background_canvas_height">525dp</dimen>
-
-<!-- Widget tray -->
-    <dimen name="widget_section_indent">56dp</dimen>
-
 <!-- DragController -->
     <dimen name="drag_flingToDeleteMinVelocity">-1000dp</dimen>
 </resources>
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
deleted file mode 100644
index 1f401c4..0000000
--- a/res/values-sw720dp/config.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<resources>
-    <bool name="config_largeHeap">true</bool>
-
-<!-- All Apps & Widgets -->
-    <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
-    <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
-
-<!-- Hotseat -->
-    <bool name="hotseat_transpose_layout_with_orientation">false</bool>
-</resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
deleted file mode 100644
index 691219a..0000000
--- a/res/values-sw720dp/dimens.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT 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>
-    <!-- All Apps -->
-    <dimen name="all_apps_empty_search_message_top_offset">64dp</dimen>
-    <dimen name="all_apps_empty_search_bg_top_offset">180dp</dimen>
-
-    <!-- Fast scroll -->
-    <dimen name="fastscroll_popup_width">75dp</dimen>
-    <dimen name="fastscroll_popup_height">62dp</dimen>
-    <dimen name="fastscroll_popup_padding">13dp</dimen>
-    <dimen name="fastscroll_popup_text_size">32dp</dimen>
-
-</resources>
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
deleted file mode 100644
index f322e9f..0000000
--- a/res/values-sw720dp/styles.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2008 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT 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>
-
-    <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowActionModeOverlay">true</item>
-        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
-    </style>
-
-    <!-- Workspace -->
-    <style name="DropTargetButton" parent="DropTargetButtonBase">
-        <item name="android:paddingLeft">60dp</item>
-        <item name="android:paddingRight">60dp</item>
-        <item name="android:shadowDx">0.0</item>
-        <item name="android:shadowDy">0.0</item>
-        <item name="android:shadowRadius">2.0</item>
-    </style>
-
-</resources>
\ No newline at end of file
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 2740c2a..9d565aa 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"இறக்கிய ஆப்ஸ் பாதுகாப்பு முறையில் முடக்கப்பட்டது"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"பாதுகாப்புப் பயன்முறையில் விட்ஜெட்கள் முடக்கப்பட்டுள்ளன"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ஷார்ட்கட் இல்லை"</string>
-    <string name="home_screen" msgid="806512411299847073">"முகப்புத் திரை"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"பிரத்தியேக செயல்கள்"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"விட்ஜெட்டைத் தேர்ந்தெடுக்க இருமுறை தட்டிப் பிடிக்கவும் அல்லது பிரத்தியேக செயல்களைப் பயன்படுத்தவும்."</string>
+    <string name="home_screen" msgid="5629429142036709174">"முகப்பு"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"திரைப் பிரிப்பு"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d அகலத்திற்கு %2$d உயரம்"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்"</string>
     <string name="place_automatically" msgid="8064208734425456485">"தானாகவே சேர்"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"தேடல் பெட்டியிலுள்ள உரையை அழிக்கும்"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"விட்ஜெட்டுகள் எதுவுமில்லை"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"தேடல் முடிவுகள் இல்லை"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"தனிப்பட்டவை"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"பணி"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"உரையாடல்கள்"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"பயன்பாடுகளில் தேடுக"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ஆப்ஸை ஏற்றுகிறது…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் ஆப்ஸ் இல்லை"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
     <string name="label_application" msgid="8531721983832654978">"ஆப்ஸ்"</string>
     <string name="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ஷார்ட்கட்டைச் சேர்க்க, தொட்டு பிடித்திருக்கவும்."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ஷார்ட்கட்டைச் சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் (அ) பிரத்தியேக செயல்களைப் பயன்படுத்தவும்."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"ஷார்ட்கட்டை நகர்த்தத் தொட்டுப் பிடிக்கவும்."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"ஷார்ட்கட்டை நகர்த்த இருமுறை தட்டிப் பிடிக்கவும் அல்லது பிரத்தியேகச் செயல்களைப் பயன்படுத்தவும்."</string>
     <string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"ஆப்ஸின் பட்டியல்"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"தனிப்பட்ட ஆப்ஸ் பட்டியல்"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"பணி ஆப்ஸ் பட்டியல்"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"முகப்பு"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"அகற்று"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"நிறுவல் நீக்கு"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ஆப்ஸ் தகவல்"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"கோப்புறை <xliff:g id="NAME">%1$s</xliff:g> என மறுபெயரிடப்பட்டது"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> கோப்புகள்"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> அல்லது அதற்கு அதிகமான கோப்புகள்"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"விட்ஜெட்கள்"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ஸ்டைல்கள் &amp; வால்பேப்பர்கள்"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"முகப்பு அமைப்புகள்"</string>
@@ -101,12 +116,12 @@
     <string name="abandoned_search" msgid="891119232568284442">"தேடு"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ஆப்ஸ் நிறுவப்படவில்லை"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ஐகானுக்கான ஆப்ஸ் நிறுவப்படவில்லை. இதை அகற்றலாம் அல்லது பயன்பாட்டைத் தேடி கைமுறையாக நிறுவலாம்."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> நிறுவப்படுகிறது, <xliff:g id="PROGRESS">%2$s</xliff:g> முடிந்தது"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>ஐப் பதிவிறக்குகிறது, <xliff:g id="PROGRESS">%2$s</xliff:g> முடிந்தது"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>ஐ நிறுவுவதற்காகக் காத்திருக்கிறது"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> விட்ஜெட்டுகள்"</string>
     <string name="widgets_list" msgid="796804551140113767">"விட்ஜெட்கள் பட்டியல்"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"விட்ஜெட்கள் பட்டியல் மூடப்பட்டது"</string>
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"முகப்புத் திரையில் சேர்"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"முகப்புத் திரையில் சேருங்கள்"</string>
     <string name="action_move_here" msgid="2170188780612570250">"இங்கு நகர்த்து"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"முகப்புத் திரையில் சேர்க்கப்பட்டது"</string>
     <string name="item_removed" msgid="851119963877842327">"அகற்றப்பட்டது"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"அடுத்து"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"முடிந்தது"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"பணிக் கணக்கு இடைநிறுத்தப்பட்டது"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"பணி ஆப்ஸால் அறிவிப்புகளை அனுப்பவோ பேட்டரியைப் பயன்படுத்தவோ இருப்பிடத்தை அணுகவோ முடியாது"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"பணிக் கணக்கு இடைநிறுத்தப்பட்டது. பணி ஆப்ஸால் அறிவிப்புகளை அனுப்பவோ பேட்டரியைப் பயன்படுத்தவோ இருப்பிடத்தை அணுகவோ முடியாது"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"பணி ஆப்ஸால் அறிவிப்புகளை அனுப்பவோ பேட்டரியைப் பயன்படுத்தவோ இருப்பிடத்தை அணுகவோ முடியாது"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"பணிக் கணக்கு இடைநிறுத்தப்பட்டது. பணி ஆப்ஸால் அறிவிப்புகளை அனுப்பவோ பேட்டரியைப் பயன்படுத்தவோ இருப்பிடத்தை அணுகவோ முடியாது"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"பணி ஆப்ஸ் பேட்ஜ் குறியிடப்பட்டுள்ளன. மேலும் உங்கள் IT நிர்வாகியால் அவற்றைப் பார்க்க முடியும்"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"சரி"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"பணி ஆப்ஸை இடைநிறுத்து"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"இயக்கு"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"வடிப்பான்"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"பணி தொடர்பான ஆப்ஸையும் அறிவிப்புகளையும் இடைநிறுத்தும்"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"தோல்வி: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 3f6f9a6..344f455 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"డౌన్‌లోడ్ చేసిన యాప్ సురక్షిత మోడ్‌లో నిలిపివేయబడింది"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"సురక్షిత మోడ్‌లో విడ్జెట్‌లు నిలిపివేయబడ్డాయి"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"షార్ట్‌కట్ అందుబాటులో లేదు"</string>
-    <string name="home_screen" msgid="806512411299847073">"హోమ్ స్క్రీన్"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"అనుకూల చర్యలు"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"విడ్జెట్‌ను ఎంచుకోవడానికి తాకి &amp; నొక్కి పెట్టండి."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"విడ్జెట్‌ను ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి, ఉంచండి."</string>
+    <string name="home_screen" msgid="5629429142036709174">"మొదటి ట్యాబ్"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"స్క్రీన్‌ను విభజించు"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d వెడల్పు X %2$d ఎత్తు"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"మాన్యువల్‌గా ఉంచడానికి నొక్కి, పట్టుకోండి"</string>
     <string name="place_automatically" msgid="8064208734425456485">"ఆటోమేటిక్‌గా జోడించు"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"సెర్చ్ బాక్స్ నుండి టెక్స్ట్‌ను క్లియర్ చేయి"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"విడ్జెట్‌లు అందుబాటులో లేవు"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"సెర్చ్ ఫలితాలు లేవు"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"వ్యక్తిగతం"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ఆఫీస్"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"సంభాషణలు"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"అప్లికేషన్‌లను శోధించండి"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"అప్లికేషన్‌లను లోడ్ చేస్తోంది…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"మరిన్ని యాప్‌ల కోసం వెతుకు"</string>
     <string name="label_application" msgid="8531721983832654978">"యాప్"</string>
     <string name="notifications_header" msgid="1404149926117359025">"నోటిఫికేషన్‌లు"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"షార్ట్‌కట్‌ని ఎంచుకోవడం కోసం నొక్కి, పట్టుకోండి."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"రెండుసార్లు నొక్కి, పట్టుకోవడం ద్వారా షార్ట్‌కట్‌ని ఎంచుకోండి లేదా అనుకూల చర్యలను ఉపయోగించండి."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"షార్ట్‌కట్‌ను తరలించడానికి తాకి &amp; నొక్కి ఉంచు."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"షార్ట్‌కట్‌ను తరలించడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కండి &amp; హోల్డ్ చేయండి."</string>
     <string name="out_of_space" msgid="4691004494942118364">"ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"అనువర్తనాల జాబితా"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"వ్యక్తిగత యాప్‌ల జాబితా"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"కార్యాలయ యాప్‌ల జాబితా"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"హోమ్"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"తీసివేయి"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్‌స్టాల్ చేయి"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"యాప్ సమాచారం"</string>
@@ -55,11 +71,11 @@
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్‌ను సూచించవద్దు"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"సూచనను పిన్ చేయండి"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"షార్ట్‌కట్‌లను ఇన్‌స్టాల్ చేయడం"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_read_settings" msgid="5833423719057558387">"హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_write_settings" msgid="3574213698004620587">"హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను వ్రాయడం"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను మార్చడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permdesc_write_settings" msgid="5440712911516509985">"హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను మార్చడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"ఫోన్ కాల్‌లను చేసేందుకు <xliff:g id="APP_NAME">%1$s</xliff:g>కి అనుమతి లేదు"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"విడ్జెట్‌ను లోడ్ చేయడంలో సమస్య"</string>
     <string name="gadget_setup_text" msgid="8274003207686040488">"సెటప్ చేయి"</string>
@@ -80,12 +96,11 @@
     <string name="folder_renamed" msgid="1794088362165669656">"ఫోల్డర్ పేరు <xliff:g id="NAME">%1$s</xliff:g>గా మార్చబడింది"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ఐటెమ్‌లు"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> లేదా అంతకంటే ఎక్కువ ఐటెమ్‌లు"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్‌లు"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్‌పేపర్‌లు"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"స్ట‌యిల్స్‌ &amp; వాల్‌పేపర్‌లు"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"హోమ్ సెట్టింగ్‌లు"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"హోమ్ స్క్రీన్ భ్రమణాన్ని అనుమతించండి"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"హోమ్ స్క్రీన్ రొటేషన్‌ను అనుమతించండి"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"ఫోన్‌‌ను తిప్పినప్పుడు"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"నోటిఫికేషన్ డాట్‌లు"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"ఆన్"</string>
@@ -101,12 +116,12 @@
     <string name="abandoned_search" msgid="891119232568284442">"సెర్చ్"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ఈ యాప్ ఇన్‌స్టాల్ చేయబడలేదు"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ఈ చిహ్నం యొక్క యాప్ ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ యాప్ కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g>‌ను ఇన్‌స్టాల్ చేయడం, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> డౌన్‌లోడ్ అవుతోంది, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ఇన్‌స్టాల్ కావడానికి వేచి ఉంది"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> విడ్జెట్‌లు"</string>
     <string name="widgets_list" msgid="796804551140113767">"విడ్జెట్‌ల జాబితా"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"విడ్జెట్‌ల జాబితా మూసివేయబడింది"</string>
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"హోమ్ స్క్రీన్‌కు జోడించు"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"హోమ్ స్క్రీన్‌కు జోడించండి"</string>
     <string name="action_move_here" msgid="2170188780612570250">"అంశాన్ని ఇక్కడికి తరలించు"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"అంశం హోమ్‌స్క్రీన్‌కి జోడించబడింది"</string>
     <string name="item_removed" msgid="851119963877842327">"అంశం తీసివేయబడింది"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"తర్వాత"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"అర్థమైంది"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ఆఫీస్ ప్రొఫైల్ పాజ్ చేయబడింది"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"మీకు నోటిఫికేషన్‌లు పంపడం, మీ బ్యాటరీని ఉపయోగించడం, లేదా మీ లొకేషన్‌ను యాక్సెస్ చేయడం \'వర్క్ యాప్\'‌లకు సాధ్యపడదు"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ఆఫీస్ ప్రొఫైల్ పాజ్ చేయబడింది. \'ఆఫీస్ యాప్\'‌లు మీకు నోటిఫికేషన్‌లు పంపడం, మీ బ్యాటరీని ఉపయోగించడం, లేదా మీ లొకేషన్‌ను యాక్సెస్ చేయడం చేయలేవు"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"మీకు నోటిఫికేషన్‌లు పంపడం, మీ బ్యాటరీని ఉపయోగించడం, లేదా మీ లొకేషన్‌ను యాక్సెస్ చేయడం \'వర్క్ యాప్\'‌లకు సాధ్యపడదు"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"వర్క్ ప్రొఫైల్ పాజ్ చేయబడింది. మీకు నోటిఫికేషన్‌లు పంపడం, మీ బ్యాటరీని ఉపయోగించడం, లేదా మీ లొకేషన్‌ను యాక్సెస్ చేయడం \'వర్క్ యాప్\'‌లకు సాధ్యపడదు"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"వర్క్ యాప్‌లు బ్యాడ్జ్ చేయబడ్డాయి, మీ IT అడ్మిన్‌కి కనిపిస్తాయి"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"అర్థమైంది"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"వర్క్ యాప్‌లను పాజ్ చేయి"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"ఆన్ చేయి"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ఫిల్టర్ చేయి"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ఆఫీస్ యాప్‌లు, నోటిఫికేషన్‌లను పాజ్ చేయండి"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"విఫలమైంది: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 6a9a485..ea6a7c2 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -26,34 +26,50 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"แอปที่ดาวน์โหลดถูกปิดในโหมดปลอดภัย"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"มีการปิดใช้งานวิดเจ็ตในเซฟโหมด"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"ทางลัดไม่พร้อมใช้งาน"</string>
-    <string name="home_screen" msgid="806512411299847073">"หน้าจอหลัก"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"การทำงานที่กำหนดเอง"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"แตะค้างเพื่อรับวิดเจ็ต"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"แตะ 2 ครั้งค้างไว้เพื่อเลือกวิดเจ็ตหรือใช้การกระทำที่กำหนดเอง"</string>
+    <string name="home_screen" msgid="5629429142036709174">"หน้าแรก"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"แบ่งหน้าจอ"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"กว้าง %1$d x สูง %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"แตะค้างไว้เพื่อวางด้วยตัวเอง"</string>
     <string name="place_automatically" msgid="8064208734425456485">"เพิ่มโดยอัตโนมัติ"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ล้างข้อความออกจากช่องค้นหา"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"ไม่มีวิดเจ็ตพร้อมใช้งาน"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"ไม่พบผลการค้นหา"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ส่วนตัว"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"งาน"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"การสนทนา"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ค้นหาแอป"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"กำลังโหลดแอป…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"ไม่พบแอปที่ตรงกับ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"ค้นหาแอปเพิ่มเติม"</string>
     <string name="label_application" msgid="8531721983832654978">"แอป"</string>
     <string name="notifications_header" msgid="1404149926117359025">"การแจ้งเตือน"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"แตะค้างไว้เพื่อเลือกทางลัด"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"แตะสองครั้งค้างไว้เพื่อเลือกทางลัดหรือใช้การกระทำที่กำหนดเอง"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"แตะค้างไว้เพื่อย้ายทางลัด"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"แตะสองครั้งค้างไว้เพื่อย้ายทางลัดหรือใช้การดำเนินการที่กำหนดเอง"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ไม่มีที่ว่างในหน้าจอหลักนี้"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ไม่มีพื้นที่เหลือในถาดรายการโปรด"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"รายชื่อแอป"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"รายการแอปส่วนตัว"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"รายการแอปสำหรับทำงาน"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"หน้าแรก"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"นำออก"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ถอนการติดตั้ง"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ข้อมูลแอป"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"ติดตั้ง"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"ไม่ต้องแนะนำแอป"</string>
-    <string name="pin_prediction" msgid="4196423321649756498">"ตรึงแอปที่คาดการณ์ไว้"</string>
+    <string name="pin_prediction" msgid="4196423321649756498">"ปักหมุดแอปที่คาดการณ์ไว้"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ติดตั้งทางลัด"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"อ่านการตั้งค่าและทางลัดหน้าแรกแล้ว"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"เปลี่ยนชื่อโฟลเดอร์เป็น <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> รายการ"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>, อย่างน้อย <xliff:g id="SIZE">%2$d</xliff:g> รายการ"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"วิดเจ็ต"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"รูปแบบและวอลเปเปอร์"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"การตั้งค่าหน้าแรก"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"ค้นหา"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ไม่ได้ติดตั้งแอปนี้"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ยังไม่ได้ติดตั้งแอปสำหรับไอคอนนี้ คุณสามารถนำไอคอนออก หรือค้นหาแอปดังกล่าวแล้วติดตั้งด้วยตนเอง"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"กำลังติดตั้ง <xliff:g id="NAME">%1$s</xliff:g> เสร็จแล้ว <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"กำลังดาวน์โหลด <xliff:g id="NAME">%1$s</xliff:g> เสร็จแล้ว <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> กำลังรอติดตั้ง"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"วิดเจ็ตของ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"รายการวิดเจ็ต"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ปิดรายการวิดเจ็ตแล้ว"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"เพิ่มลงในหน้าจอหลัก"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"ถัดไป"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"รับทราบ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"โปรไฟล์งานหยุดชั่วคราว"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"แอปงานจะส่งการแจ้งเตือน ใช้แบตเตอรี่ หรือเข้าถึงตำแหน่งของคุณไม่ได้"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"โปรไฟล์งานปิดชั่วคราว แอปงานจะส่งการแจ้งเตือน ใช้แบตเตอรี่ หรือเข้าถึงตำแหน่งของคุณไม่ได้"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"แอปงานจะส่งการแจ้งเตือน ใช้แบตเตอรี่ หรือเข้าถึงตำแหน่งของคุณไม่ได้"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"โปรไฟล์งานหยุดชั่วคราว แอปงานจะส่งการแจ้งเตือน ใช้แบตเตอรี่ หรือเข้าถึงตำแหน่งของคุณไม่ได้"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"แอปงานจะติดป้ายไว้และผู้ดูแลระบบไอทีจะมองเห็น"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"รับทราบ"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"หยุดแอปงานชั่วคราว"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"เปิด"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"ตัวกรอง"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"หยุดแอปงานและการแจ้งเตือนไว้ชั่วคราว"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ไม่สำเร็จ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index ac78aeb..f112ec9 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Naka-disable ang na-download na app sa Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Naka-disable ang mga widget sa Safe mode"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Hindi available ang shortcut"</string>
-    <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Mga custom na pagkilos"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Pindutin nang matagal upang kumuha ng widget."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"I-double tap nang matagal upang pumili ng widget o gumamit ng mga custom na pagkilos."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Home"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ang lapad at %2$d ang taas"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Pindutin nang matagal para manual na ilagay"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Awtomatikong idagdag"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"I-clear ang text sa box para sa paghahanap"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Walang available na widget"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Walang resulta ng paghahanap"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabaho"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Mga Pag-uusap"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Maghanap ng mga app"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Naglo-load ng mga app…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Mga Notification"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Pindutin nang matagal para kumuha ng shortcut."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"I-double tap nang matagal para kumuha ng shortcut o gumamit ng mga custom na pagkilos."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Wala nang lugar sa Home screen na ito."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Wala nang lugar sa tray ng Mga Paborito"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Listahan ng mga app"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Listahan ng mga personal na app"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Listahan ng mga app sa trabaho"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Alisin"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"I-uninstall"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Impormasyon ng app"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Pinalitan ang pangalan ng folder ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> (na) item"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o higit pang item"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Mga Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mga istilo at wallpaper"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Mga setting ng Home"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Maghanap"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Hindi naka-install ang app na ito"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Hindi naka-install ang app para sa icon na ito. Puwede mo itong alisin, o maaari mong hanapin ang app at i-install ito nang manual."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Ini-install ang <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> kumpleto"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Dina-download na ang <xliff:g id="NAME">%1$s</xliff:g>, tapos na ang <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Hinihintay nang mag-install ang <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Mga widget ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Listahan ng mga widget"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Nakasara ang listahan ng mga widget"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Idagdag sa Home screen"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Susunod"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Naka-pause ang profile sa trabaho"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Ang mga app para sa trabaho ay hindi makakapagpadala sa iyo ng mga notification, makakagamit ng battery mo, o makaka-access ng iyong lokasyon"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Naka-pause ang profile sa trabaho. Hindi makakapagpadala sa iyo ng mga notification, makakagamit ng battery mo, o makaka-access ng iyong lokasyon ang mga app para sa trabaho"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Ang mga app para sa trabaho ay hindi makakapagpadala sa iyo ng mga notification, makakagamit ng battery mo, o makaka-access ng iyong lokasyon"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Naka-pause ang profile sa trabaho. Ang mga app para sa trabaho ay hindi makakapagpadala sa iyo ng mga notification, makakagamit ng battery mo, o makaka-access ng iyong lokasyon"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"May badge at nakikita ng iyong IT admin ang mga app para sa trabaho"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"I-pause ang mga app para sa trabaho"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"I-on"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"I-pause ang mga app at notification para sa trabaho"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Hindi nagawa: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index c84e670..9936538 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -26,30 +26,46 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"İndirilen uygulama Güvenli modda devre dışı bırakıldı"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Güvenli modda widget\'lar devre dışı"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Kısayol kullanılamıyor"</string>
-    <string name="home_screen" msgid="806512411299847073">"Ana ekran"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Özel işlemler"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Widget seçmek için dokunun ve basılı tutun."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Bir widget\'ı seçmek veya özel işlemleri kullanmak için iki kez dokunun ve basılı tutun."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"genişlik: %1$d, yükseklik: %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Manuel olarak yerleştirmek için dokunun ve basılı tutun"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Otomatik olarak ekle"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Arama kutusundaki metni temizle"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Kullanılabilir widget yok"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Arama sonucu yok"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Kişisel"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"İş"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Görüşmeler"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Uygulamalarda ara"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Uygulamalar yükleniyor…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Bildirimler"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Kısayol seçmek için dokunun ve basılı tutun."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Bir kısayolu seçmek veya özel işlemleri kullanmak için iki kez dokunun ve basılı tutun."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Bu Ana ekranda yer kalmadı."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoriler tepsisinde başka yer kalmadı"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Uygulamalar listesi"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Kişisel uygulamalar listesi"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"İş uygulamaları listesi"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Ana ekran"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Kaldır"</string>
-    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Yüklemeyi kaldır"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Kaldır"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Uygulama bilgileri"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Yükle"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Uygulama önerme"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Klasörün adı <xliff:g id="NAME">%1$s</xliff:g> olarak değiştirildi"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> öğe"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> veya daha fazla öğe"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widget\'lar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiller ve duvar kağıtları"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ana ekran ayarları"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Ara"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Bu uygulama yüklü değil"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Bu simgenin uygulaması yüklü değil. Uygulamayı kaldırabilir veya arayıp manuel olarak yükleyebilirsiniz."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> yükleniyor, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> indiriliyor, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> uygulaması yüklenmek için bekliyor"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widget\'ları"</string>
     <string name="widgets_list" msgid="796804551140113767">"Widget listesi"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Widget listesi kapalı"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Ana ekrana ekle"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"İleri"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Anladım"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"İş profili duraklatıldı"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"İş uygulamaları size bildirim gönderemez, pilinizi kullanamaz veya konum bilginize erişemez"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"İş profili duraklatıldı. İş uygulamaları size bildirim gönderemez, pilinizi kullanamaz veya konum bilginize erişemez"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"İş uygulamaları size bildirim gönderemez, pilinizi kullanamaz veya konum bilginize erişemez"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"İş profili duraklatıldı. İş uygulamaları size bildirim gönderemez, pilinizi kullanamaz veya konum bilginize erişemez"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"İş uygulamaları rozetle işaretlenmiş olup BT yöneticisi tarafından görülebilir"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Anladım"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"İş uygulamalarını duraklat"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Aç"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Filtre"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"İş uygulamalarını ve bildirimlerini duraklatın"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Başarısız: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 57e7c5a..87ae581 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -26,28 +26,48 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Завантажений додаток вимкнено в безпечному режимі"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"У безпечному режимі віджети вимкнено"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Ярлик недоступний"</string>
-    <string name="home_screen" msgid="806512411299847073">"Головний екран"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Спеціальні дії"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Натисніть і утримуйте, щоб вибрати віджет."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Двічі натисніть і утримуйте, щоб вибрати віджет, або виконайте іншу дію."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Головний екран"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Розділити екран"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина – %1$d, висота – %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Натисніть і утримуйте, щоб додати вручну"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Додати автоматично"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Очистити текст у полі пошуку"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Немає доступних віджетів"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Нічого не знайдено"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Особисті"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Робочі"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Розмови"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Пошук додатків"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Завантаження додатків…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Немає додатків для запиту \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Шукати ще додатки"</string>
     <string name="label_application" msgid="8531721983832654978">"Додаток"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Сповіщення"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Натисніть і втримуйте, щоб вибрати ярлик."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Двічі натисніть і втримуйте, щоб вибрати ярлик, або виконайте іншу дію."</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"Натисніть і втримуйте, щоб перемістити ярлик."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"Двічі натисніть і втримуйте ярлик, щоб перемістити його або виконати інші дії."</string>
     <string name="out_of_space" msgid="4691004494942118364">"На цьому головному екрані більше немає місця."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"В області \"Вибране\" немає місця"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Список додатків"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Список особистих додатків"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Список робочих додатків"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Головний екран"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Видалити"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Видалити додаток"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Про додаток"</string>
@@ -82,7 +102,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Папку перейменовано на <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g> або більше"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Віджети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стиль і фон"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Налаштування головного екрана"</string>
@@ -103,9 +122,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Шукати"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Цей додаток не встановлено"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Додаток для цього значка не встановлено. Можна видалити значок або знайти додаток і встановити його вручну."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> встановлюється, виконано <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> завантажується, <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> очікує на завантаження"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Віджети додатка <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Список віджетів"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Список віджектів закрито"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Додати на головний екран"</string>
@@ -143,8 +162,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Далі"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Робочий профіль призупинено"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Робочі додатки не можуть надсилати сповіщення, споживати заряд акумулятора й використовувати геодані"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Робочий профіль призупинено. Робочі додатки не можуть надсилати сповіщення, споживати заряд акумулятора й використовувати геодані"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Робочі додатки не можуть надсилати сповіщення, використовувати заряд акумулятора й отримувати доступ до геоданих"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Робочий профіль призупинено. Робочі додатки не можуть надсилати сповіщення, використовувати заряд акумулятора й отримувати доступ до геоданих"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Робочі додатки відповідно позначені й видимі системному адміністратору"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Зрозуміло"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Призупинити робочі додатки"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Увімкнути"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Фільтр"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Призупинити робочі додатки й сповіщення"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Не вдалося <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 881ae53..2ee66b0 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ڈاؤن لوڈ کردہ ایپ کو محفوظ وضع میں غیر فعال کر دیا گیا"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"ویجیٹس کو محفوظ وضع میں غیر فعال کر دیا گیا"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"شارٹ کٹ دستیاب نہیں ہے"</string>
-    <string name="home_screen" msgid="806512411299847073">"ہوم اسکرین"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"حسب ضرورت کارروائیاں"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"کوئی ویجیٹ منتخب کرنے کیلئے ٹچ کریں اور پکڑے رہیں۔"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"کوئی ویجٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور پکڑے رکھیں۔"</string>
+    <string name="home_screen" msgid="5629429142036709174">"ہوم"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"اسپلٹ اسکرین"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏%1$d چوڑا اور ‎%2$d اونچا"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"‏دستی طور پر رکھنے کیلئے ‎ٹچ کر کے دبائے رکھیں"</string>
     <string name="place_automatically" msgid="8064208734425456485">"خود کار طور پر شامل کریں"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"تلاش کے خانے سے ٹیکسٹ صاف کریں"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"کوئی ویجیٹ دستیاب نہیں ہے"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"تلاش کا کوئی نتیجہ نہیں ہے"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"ذاتی"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"دفتری ویجیٹس"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"گفتگوئیں"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ایپس تلاش کریں"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"ایپس لوڈ کی جا رہی ہیں…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" سے مماثل کوئی ایپس نہیں ملیں"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"مزید ایپس تلاش کریں"</string>
     <string name="label_application" msgid="8531721983832654978">"ایپ"</string>
     <string name="notifications_header" msgid="1404149926117359025">"اطلاعات"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ایک شارٹ کٹ منتخب کرنے کیلئے ٹچ کر کے دبائے رکھیں۔"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ایک شارٹ کٹ منتخب کرنے یا حسب ضرورت کارروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور دبائے رکھیں۔"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"شارٹ کٹ منتقل کرنے کیلیے ٹچ کریں اور پکڑ کر رکھیں۔"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"شارٹ کٹ کو منتقل کرنے یا حسب ضرورت کارروائیاں استعمال کرنے کے لیے دوبار تھپتھپائیں اور پکڑ کر رکھیں۔"</string>
     <string name="out_of_space" msgid="4691004494942118364">"اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"ایپس کی فہرست"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"ذاتی ایپس کی فہرست"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"دفتری ایپس کی فہرست"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"ہوم"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"ہٹائیں"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"اَن انسٹال کریں"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ایپ کی معلومات"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"فولڈر کا نام تبدیل کر کے <xliff:g id="NAME">%1$s</xliff:g> کر دیا گیا"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> آئٹمز"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> یا مزید آئٹمز"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"طرزیں اور وال پیپر"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ہوم ترتیبات"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"تلاش کریں"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"یہ ایپ انسٹال کردہ نہیں ہے"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"اس آئیکن کیلئے ایپ انسٹال کردہ نہیں ہے۔ آپ اسے ہٹا سکتے ہیں یا ایپ کو تلاش کر سکتے اور دستی طور پر اسے انسٹال کر سکتے ہیں۔"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> انسٹال کی جا رہی ہے، <xliff:g id="PROGRESS">%2$s</xliff:g> مکمل ہو گئی"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ڈاؤن لوڈ ہو رہا ہے، <xliff:g id="PROGRESS">%2$s</xliff:g> مکمل ہو گیا"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> انسٹال ہونے کا انتظار کر رہی ہے"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ویجیٹس"</string>
     <string name="widgets_list" msgid="796804551140113767">"ویجیٹس کی فہرست"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"ویجیٹس کی فہرست بند کر دی گئی"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ہوم اسکرین میں شامل کریں"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"اگلا"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"سمجھ آ گئی"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"دفتری پروفائل روک دی گئی ہے"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"ورک ایپس آپ کو اطلاعات نہیں بھیج سکتیں، آپ کی بیٹری استعمال، یا آپ کے مقام تک رسائی حاصل نہیں کر سکتی ہیں"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"دفتری پروفائل موقوف کر دی گئی ہے۔ ورک ایپس آپ کو اطلاعت نہیں بھیج سکتیں، آپ کی بیٹری کا استعمال، یا آپ کے مقام تک رسائی حاصل نہیں کر سکتی ہیں"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"ورک ایپس آپ کو اطلاعات نہیں بھیج سکتیں، آپ کی بیٹری استعمال یا آپ کے مقام تک رسائی حاصل نہیں کر سکتی ہیں"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"دفتری پروفائل موقوف کر دی گئی ہے۔ ورک ایپس آپ کو اطلاعات نہیں بھیج سکتیں، آپ کی بیٹری استعمال یا آپ کے مقام تک رسائی حاصل نہیں کر سکتی ہیں"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"‏ورک ایپس پر بَیج لگایا ہے اور آپ کا IT منتظم انہیں دیکھ سکتا ہے"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"سمجھ آ گئی"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"ورک ایپس موقوف کریں"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"آن کریں"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"فلٹر"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"ورک ایپس اور اطلاعات کو روکیں"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ناکام ہو گيا: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 1532124..d31aea9 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Yuklab olingan ilova xavfsiz rejimda o‘chirib qo‘yildi"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Xavfsiz rejimda vidjetlar o‘chirib qo‘yilgan"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Tezkor tugmadan foydalanib bo‘lmaydi"</string>
-    <string name="home_screen" msgid="806512411299847073">"Bosh ekran"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Maxsus amallar"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidjetni tanlash uchun bosib turing."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ikki marta bosib va bosib turgan holatda vidjetni tanlang yoki maxsus amaldan foydalaning."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Eni %1$d, bo‘yi %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Joylash uchun bosib turing"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Avtomatik chiqarish"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Qidiruv maydoni matnini tozalash"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Hech qanday vidjet mavjud emas"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Hech narsa topilmadi"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Shaxsiy"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Ish"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Suhbatlar"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Ilovalarni qidirish"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Ilovalar yuklanmoqda…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Bildirishnomalar"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Yorliqni tanlab olish uchun bosib turing."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Ikki marta bosib va bosib turgan holatda yorliqni tanlang yoki maxsus amaldan foydalaning."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Uy ekranida bitta ham xona yo‘q."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Ilovalar ro‘yxati"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Shaxsiy ilovalar ro‘yxati"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Ishga oid ilovalar ro‘yxati"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Bosh sahifa"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Olib tashlash"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirib tashlash"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Ilova haqida"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Jild nomi <xliff:g id="NAME">%1$s</xliff:g>ga o‘zgartirildi"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Jild: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> fayllar"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Jild: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> va undan ortiq fayllar"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mavzu va fon rasmlari"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Bosh ekran sozlamalari"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Qidirish"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ushbu ilova o‘rnatilmagan"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ilova o‘rnatilmagan. Belgini o‘chirib tashlashingiz yoki ilovani topib, uni qo‘lda o‘rnatishingiz mumkin."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> oʻrnatlmoqda, <xliff:g id="PROGRESS">%2$s</xliff:g> yakunlandi"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> yuklab olinmoqda, <xliff:g id="PROGRESS">%2$s</xliff:g> bajarildi"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi o‘rnatilishi kutilmoqda"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> vidjetlari"</string>
     <string name="widgets_list" msgid="796804551140113767">"Vidjetlar ro‘yxati"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Vidjetlar ro‘yxati yopildi"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Bosh ekranga chiqarish"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Keyingisi"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Ish profili pauzada"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Ishga oid ilovalar batareya sarfi haqida bildirishnomalar yubora olmaydi va joylashuv axborotidan foydalana olmaydi"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Ish profili pauzada. Ishga oid ilovalar batareya sarfi haqida bildirishnomalar yubora olmaydi va joylashuv axborotidan foydalana olmaydi"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Ishga oid ilovalar batareya sarfi haqida bildirishnomalar yubora olmaydi va joylashuv axborotidan foydalana olmaydi"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Ish profili pauzada. Ishga oid ilovalar batareya sarfi haqida bildirishnomalar yubora olmaydi va joylashuv axborotidan foydalana olmaydi"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Ishga oid ilovalar qadalgan va IT administratoringizga koʻrinadi"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"OK"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Ishga oid ilovalarni pauza qilish"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Yoqish"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Saralash"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Ishga oid ilova va bildirishnomalarni pauza qilish"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Xato: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-v22/styles.xml b/res/values-v22/styles.xml
deleted file mode 100644
index f86db7a..0000000
--- a/res/values-v22/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
-*/
--->
-
-<resources>
-
-    <style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
-        <item name="widgetsTheme">@style/WidgetContainerTheme</item>
-    </style>
-
-</resources>
\ No newline at end of file
diff --git a/res/values-v26/styles.xml b/res/values-v26/styles.xml
deleted file mode 100644
index d2f0802..0000000
--- a/res/values-v26/styles.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-<resources>
-    <!-- Theme for the widget container. -->
-    <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="android:colorPrimaryDark">#E8EAED</item>
-        <item name="android:textColorSecondary">?android:attr/textColorPrimary</item>
-        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
-    </style>
-    <style name="WidgetContainerTheme.Dark" parent="AppTheme.Dark">
-        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
-        <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
-    </style>
-
-</resources>
diff --git a/go/res/values/dimens.xml b/res/values-v28/dimens.xml
similarity index 81%
copy from go/res/values/dimens.xml
copy to res/values-v28/dimens.xml
index f1b1053..3f118cd 100644
--- a/go/res/values/dimens.xml
+++ b/res/values-v28/dimens.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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.
@@ -15,6 +15,5 @@
 -->
 
 <resources>
-    <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+    <dimen name="dialogCornerRadius">?android:attr/dialogCornerRadius</dimen>
+</resources>
diff --git a/res/values-v28/styles.xml b/res/values-v28/styles.xml
deleted file mode 100644
index 7df9ce5..0000000
--- a/res/values-v28/styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* 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>
-    <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" >
-        <item name="android:textFontWeight">400</item>
-    </style>
-</resources>
diff --git a/res/values-v29/styles.xml b/res/values-v29/styles.xml
index 7590594..5370b79 100644
--- a/res/values-v29/styles.xml
+++ b/res/values-v29/styles.xml
@@ -26,7 +26,6 @@
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowShowWallpaper">true</item>
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
         <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
         <item name="android:enforceStatusBarContrast">false</item>
         <item name="android:enforceNavigationBarContrast">false</item>
diff --git a/res/values-v30/styles.xml b/res/values-v30/styles.xml
index 71740a9..ec5c113 100644
--- a/res/values-v30/styles.xml
+++ b/res/values-v30/styles.xml
@@ -26,7 +26,6 @@
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowShowWallpaper">true</item>
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
         <item name="android:windowLayoutInDisplayCutoutMode">always</item>
         <item name="android:enforceStatusBarContrast">false</item>
         <item name="android:enforceNavigationBarContrast">false</item>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
new file mode 100644
index 0000000..d73ee57
--- /dev/null
+++ b/res/values-v31/colors.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+<resources  xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="popup_color_primary_light">@android:color/system_accent2_50</color>
+    <color name="popup_color_secondary_light">@android:color/system_neutral2_100</color>
+    <color name="popup_color_tertiary_light">@android:color/system_neutral2_300</color>
+    <color name="popup_color_neutral_dark">@android:color/system_neutral1_1000</color>
+    <color name="popup_color_primary_dark">@android:color/system_neutral2_800</color>
+    <color name="popup_color_secondary_dark">@android:color/system_neutral1_900</color>
+    <color name="popup_color_tertiary_dark">@android:color/system_neutral2_700</color>
+
+    <color name="popup_notification_dot_light">@android:color/system_accent1_100</color>
+    <color name="popup_notification_dot_dark">@android:color/system_accent2_600</color>
+
+    <color name="workspace_text_color_light">@android:color/system_neutral1_0</color>
+    <color name="workspace_text_color_dark">@android:color/system_neutral1_1000</color>
+
+    <color name="folder_hint_text_color_light">@android:color/system_neutral1_50</color>
+    <color name="folder_hint_text_color_dark">@android:color/system_neutral2_700</color>
+
+    <color name="text_color_primary_dark">@android:color/system_neutral1_50</color>
+    <color name="text_color_secondary_dark">@android:color/system_neutral2_200</color>
+    <color name="text_color_tertiary_dark">@android:color/system_neutral2_400</color>
+
+    <color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
+
+    <color name="folder_dot_color">@android:color/system_accent2_50</color>
+
+    <color name="workspace_accent_color_light">@android:color/system_accent1_100</color>
+    <color name="workspace_accent_color_dark">@android:color/system_accent2_600</color>
+</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index c4f8304..318d453 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ứng dụng đã tải xuống bị tắt ở chế độ An toàn"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Tiện ích bị vô hiệu hóa ở chế độ an toàn"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Lối tắt không khả dụng"</string>
-    <string name="home_screen" msgid="806512411299847073">"Màn hình chính"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Tác vụ tùy chỉnh"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Chạm và giữ để chọn tiện ích."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Nhấn đúp và giữ để chọn tiện ích hoặc sử dụng tác vụ tùy chỉnh."</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="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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Rộng %1$d x cao %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Chạm và giữ để thêm theo cách thủ công"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Tự động thêm"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Xóa văn bản khỏi hộp tìm kiếm"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Không có tiện ích nào"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Không có kết quả tìm kiếm nào"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Cá nhân"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Công việc"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Cuộc trò chuyện"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Tìm kiếm ứng dụng"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Đang tải ứng dụng…"</string>
     <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="notifications_header" msgid="1404149926117359025">"Thông báo"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Chạm và giữ để chọn lối tắt."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Nhấn đúp và giữ để chọn lối tắt hoặc sử dụng hành động tùy chỉnh."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Không còn chỗ trên Màn hình chính này."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Không còn chỗ trong khay Mục yêu thích"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Danh sách ứng dụng"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Danh sách ứng dụng cá nhân"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Danh sách ứng dụng công việc"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Màn hình chính"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Xóa"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Gỡ cài đặt"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Thông tin ứng dụng"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Đã đổi tên thư mục thành <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục trở lên"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Tiện ích"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Kiểu và hình nền"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Cài đặt màn hình chính"</string>
@@ -93,7 +108,7 @@
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Cần quyền truy cập thông báo"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Để hiển thị Dấu chấm thông báo, hãy bật thông báo ứng dụng cho <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Thay đổi cài đặt"</string>
-    <string name="notification_dots_service_title" msgid="4284221181793592871">"Hiển thị dấu chấm thông báo"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Hiện dấu chấm thông báo"</string>
     <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Thêm biểu tượng ứng dụng vào Màn hình chính"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Cho ứng dụng mới"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
@@ -101,16 +116,16 @@
     <string name="abandoned_search" msgid="891119232568284442">"Tìm kiếm"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Ứng dụng này chưa được cài đặt"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ứng dụng cho biểu tượng này chưa được cài đặt. Bạn có thể xóa ứng dụng hoặc tìm kiếm và cài đặt ứng dụng theo cách thủ công."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"Đang cài đặt <xliff:g id="NAME">%1$s</xliff:g>, hoàn tất <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Đang tải xuống <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> hoàn tất"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Đang chờ cài đặt <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Tiện ích của <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Danh sách tiện ích"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Đã đóng danh sách tiện ích"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Thêm vào màn hình chính"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Di chuyển mục vào đây"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Đã thêm mục vào màn hình chính"</string>
     <string name="item_removed" msgid="851119963877842327">"Đã xóa mục"</string>
-    <string name="undo" msgid="4151576204245173321">"Hoàn tác"</string>
+    <string name="undo" msgid="4151576204245173321">"Hủy"</string>
     <string name="action_move" msgid="4339390619886385032">"Di chuyển mục"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Di chuyển đến hàng <xliff:g id="NUMBER_0">%1$s</xliff:g> cột <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Di chuyển tới vị trí <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Tiếp theo"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Hồ sơ công việc của bạn đã bị tạm dừng"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Các ứng dụng công việc không thể gửi thông báo cho bạn, sử dụng pin hoặc truy cập thông tin vị trí của bạn"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Hồ sơ công việc của bạn đã bị tạm dừng. Các ứng dụng công việc không thể: gửi thông báo cho bạn, sử dụng pin hoặc xem dữ liệu vị trí"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Các ứng dụng công việc không thể gửi thông báo cho bạn, sử dụng pin hoặc truy cập thông tin vị trí của bạn"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Hồ sơ công việc đã bị tạm dừng. Các ứng dụng công việc không thể gửi thông báo cho bạn, sử dụng pin hoặc truy cập thông tin vị trí của bạn"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Các ứng dụng công việc được gắn huy hiệu và quản trị viên CNTT sẽ nhìn thấy các ứng dụng này"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Tôi hiểu"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Tạm dừng các ứng dụng công việc"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Bật"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Bộ lọc"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Tạm dừng các ứng dụng và thông báo liên quan tới công việc"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Không thực hiện được thao tác: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 095c0e3..f9628b2 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"安全模式下不允许使用下载的此应用"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"安全模式下不允许使用微件"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"无法使用快捷方式"</string>
-    <string name="home_screen" msgid="806512411299847073">"主屏幕"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"自定义操作"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"触摸并按住微件即可选择。"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"点按两次并按住微件即可选择微件,您也可以使用自定义操作。"</string>
+    <string name="home_screen" msgid="5629429142036709174">"主屏幕"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分屏"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"宽 %1$d,高 %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"轻触并按住即可手动放置"</string>
     <string name="place_automatically" msgid="8064208734425456485">"自动添加"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"清除搜索框中的文字"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"没有可用的微件"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"无搜索结果"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"个人"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"对话"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"搜索应用"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"正在加载应用…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"未找到与“<xliff:g id="QUERY">%1$s</xliff:g>”相符的应用"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜索更多应用"</string>
     <string name="label_application" msgid="8531721983832654978">"应用"</string>
     <string name="notifications_header" msgid="1404149926117359025">"通知"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"触摸并按住快捷方式即可选择快捷方式。"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"点按两次并按住快捷方式即可选择快捷方式,您也可以使用自定义操作。"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"轻触并按住快捷方式即可移动该快捷方式。"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"点按两次并按住快捷方式即可移动该快捷方式或使用自定义操作。"</string>
     <string name="out_of_space" msgid="4691004494942118364">"此主屏幕上已没有空间。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"收藏栏已满"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"应用列表"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"个人应用列表"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"工作应用列表"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"主屏幕"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"卸载"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"应用信息"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"已将文件夹重命名为“<xliff:g id="NAME">%1$s</xliff:g>”"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个项目"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个或更多项目"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"微件"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"样式和壁纸"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"主屏幕设置"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"搜索"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"未安装此应用"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"未安装此图标对应的应用。您可以移除此图标,也可以尝试搜索相应的应用并手动安装。"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"正在安装<xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"正在下载<xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>正在等待安装"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>微件"</string>
     <string name="widgets_list" msgid="796804551140113767">"微件列表"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"微件列表已关闭"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"添加到主屏幕"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"继续"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"工作资料已被暂停"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"现在,工作应用无法向您发送通知、不能耗用电池电量,也无法获取您的位置信息"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"工作资料已被暂停。现在,工作应用无法向您发送通知、不能耗用电池电量,也无法获取您的位置信息"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"现在,工作应用无法向您发送通知、不能耗用电池电量,也无法获取您的位置信息"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"工作资料已暂停使用。现在,工作应用无法向您发送通知、不能耗用电池电量,也无法获取您的位置信息"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"工作应用带有标志,您的 IT 管理员可以看到工作应用"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"知道了"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"暂停工作应用"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"开启"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"过滤器"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"暂停工作应用及其通知"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"失败:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index f497c58..8929aa8 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"在安全模式中無法使用「已下載的應用程式」功能"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"在安全模式中無法使用小工具"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"沒有可用的捷徑"</string>
-    <string name="home_screen" msgid="806512411299847073">"主畫面"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"自訂操作"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"輕觸並按住小工具即可選取。"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"連扲兩下,然後扲住,就可以新增小工具,或者執行自訂操作。"</string>
+    <string name="home_screen" msgid="5629429142036709174">"主畫面"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割螢幕"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d 闊,%2$d 高"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"按住即可手動新增"</string>
     <string name="place_automatically" msgid="8064208734425456485">"自動新增"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"清除搜尋框中的文字"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"沒有可用的小工具"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"沒有相符的搜尋結果"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"個人"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"對話"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"搜尋應用程式"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"正在載入應用程式…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"找不到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜尋更多應用程式"</string>
     <string name="label_application" msgid="8531721983832654978">"應用程式"</string>
     <string name="notifications_header" msgid="1404149926117359025">"通知"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"按住捷徑即可選取。"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"連㩒兩下之後繼續㩒住,就可以揀選捷徑或者用自訂嘅操作。"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"輕觸並按住即可移動捷徑。"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"㩒兩下之後㩒住,就可以郁捷徑或者用自訂操作。"</string>
     <string name="out_of_space" msgid="4691004494942118364">"主畫面已無空間。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"我的收藏寄存區沒有足夠空間"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"應用程式清單"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"個人應用程式清單"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"工作應用程式清單"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"主畫面"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資料"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"資料夾已重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個項目"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個或以上的項目"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"樣式和桌布"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"主畫面設定"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"尚未安裝這個應用程式"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"正在安裝「<xliff:g id="NAME">%1$s</xliff:g>」(已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"正在下載 <xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"正在等待安裝 <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>小工具"</string>
     <string name="widgets_list" msgid="796804551140113767">"小工具清單"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"已經關閉嘅小工具清單"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"新增至主畫面"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"繼續"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"工作設定檔已暫停使用"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"工作應用程式無法向您傳送通知、使用電池或存取位置"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"工作設定檔已暫停。工作應用程式無法向您傳送通知、使用電池或存取位置"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"工作應用程式無法向您傳送通知、使用電池或存取位置"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"工作設定檔已暫停。工作應用程式無法向您傳送通知、使用電池或存取位置"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"IT 管理員可看到工作應用程式和相關標誌"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"知道了"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"暫停工作應用程式"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"開啟"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"篩選器"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"暫停工作應用程式和通知"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"操作失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index d735715..c6f933c 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -26,28 +26,44 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"在安全模式中無法使用「已下載的應用程式」功能"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"在安全模式下無法使用小工具"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"目前無法使用捷徑"</string>
-    <string name="home_screen" msgid="806512411299847073">"主螢幕"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"自訂動作"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"輕觸並按住小工具即可選取。"</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"輕觸兩下並按住小工具即可選取,你也可以使用自訂動作。"</string>
+    <string name="home_screen" msgid="5629429142036709174">"主畫面"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割畫面"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"寬度為 %1$d,高度為 %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"按住圖示即可手動新增"</string>
     <string name="place_automatically" msgid="8064208734425456485">"自動新增"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"清除搜尋框中的文字"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"沒有可用的小工具"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"找不到相符的搜尋結果"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"個人"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"對話"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"搜尋應用程式"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"正在載入應用程式…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"找不到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜尋更多應用程式"</string>
     <string name="label_application" msgid="8531721983832654978">"應用程式"</string>
     <string name="notifications_header" msgid="1404149926117359025">"通知"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"按住捷徑即可選取。"</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"輕觸兩下並按住捷徑即可選取,你也可以使用自訂動作。"</string>
-    <string name="out_of_space" msgid="4691004494942118364">"這個主螢幕已無空間。"</string>
+    <string name="long_press_shortcut_to_add" msgid="5405328730817637737">"按住即可移動捷徑。"</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="2199537273817090740">"輕觸兩下並按住即可移動捷徑或使用自訂操作。"</string>
+    <string name="out_of_space" msgid="4691004494942118364">"這個主畫面已無空間。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"「我的最愛」匣已無可用空間"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"應用程式清單"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"個人應用程式清單"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"辦公應用程式清單"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"主螢幕"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資訊"</string>
@@ -56,9 +72,9 @@
     <string name="pin_prediction" msgid="4196423321649756498">"固定預測的應用程式"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式自動新增捷徑。"</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"讀取主螢幕的設定和捷徑"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"讀取主畫面的設定和捷徑"</string>
     <string name="permdesc_read_settings" msgid="5833423719057558387">"允許應用程式讀取主畫面中的設定和捷徑。"</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"寫入主螢幕設定和捷徑"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"寫入主畫面設定和捷徑"</string>
     <string name="permdesc_write_settings" msgid="5440712911516509985">"允許應用程式變更主畫面中的設定和捷徑。"</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> 無法撥打電話"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"載入小工具時發生問題"</string>
@@ -71,7 +87,7 @@
       <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="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_scroll_format" msgid="8458889198184077399">"主畫面:第 %1$d 頁,共 %2$d 頁"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"新的主畫面頁面"</string>
     <string name="folder_opened" msgid="94695026776264709">"資料夾已開啟 (<xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"輕觸即可關閉資料夾"</string>
@@ -80,12 +96,11 @@
     <string name="folder_renamed" msgid="1794088362165669656">"已將資料夾重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個項目"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個以上的項目"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"樣式和桌布"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"主螢幕設定"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"主畫面設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"允許旋轉主螢幕"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"允許旋轉主畫面"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
     <string name="notification_dots_title" msgid="9062440428204120317">"通知圓點"</string>
     <string name="notification_dots_desc_on" msgid="1679848116452218908">"開啟"</string>
@@ -94,16 +109,16 @@
     <string name="msg_missing_notification_access" msgid="281113995110910548">"如要顯示通知圓點,請開啟「<xliff:g id="NAME">%1$s</xliff:g>」的應用程式通知功能"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"變更設定"</string>
     <string name="notification_dots_service_title" msgid="4284221181793592871">"顯示通知圓點"</string>
-    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"將應用程式圖示新增到主畫面"</string>
+    <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"將應用程式圖示加到主畫面"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"適用於新安裝的應用程式"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"尚未安裝這個應用程式"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"你尚未安裝這個圖示代表的應用程式。你可以移除這個圖示,也可以搜尋該應用程式並手動安裝。"</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"正在安裝「<xliff:g id="NAME">%1$s</xliff:g>」(已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"正在下載「<xliff:g id="NAME">%1$s</xliff:g>」,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"正在等待安裝「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"「<xliff:g id="NAME">%1$s</xliff:g>」小工具"</string>
     <string name="widgets_list" msgid="796804551140113767">"小工具清單"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"已關閉小工具清單"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"新增至主畫面"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"繼續"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"我知道了"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"工作資料夾已暫停"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"工作應用程式無法傳送通知給你、不能使用電池電量,也無法存取你的位置資訊"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"工作資料夾已暫停。工作應用程式無法傳送通知給你、不能使用電池電量,也無法存取你的位置資訊"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"工作應用程式將無法傳送通知,也無法存取你的位置資訊。你還可以省下這類應用程式消耗的電量"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"工作資料夾已暫停。工作應用程式將無法傳送通知,也無法存取你的位置資訊。你還可以省下這類應用程式消耗的電量"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"你的 IT 管理員可以看見工作應用程式和相關標記"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"我知道了"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"暫停工作應用程式"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"開啟"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"篩選器"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"暫停工作應用程式和通知"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index d9dc210..8296853 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -26,31 +26,47 @@
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Uhlelo lokusebenza olulandiwe lukhutshaziwe kumodi ephephile"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Amawijethi akhutshaziwe kwimodi yokuphepha"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"Isinqamuleli asitholakali"</string>
-    <string name="home_screen" msgid="806512411299847073">"Isikrini sasekhaya"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"Izenzo zangokwezifiso"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Thinta uphinde ubambe ukuze uphakamise iwijethi."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Thepha kabili bese uyabamba ukuze uthathe iwijethi noma sebenzisa izenzo ezingokwezifiso."</string>
+    <string name="home_screen" msgid="5629429142036709174">"Ikhaya"</string>
+    <string name="recent_task_option_split_screen" msgid="6690461455618725183">"Hlukanisa isikrini"</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>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ububanzi ngokungu-%2$d ukuya phezulu"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Thinta futhi ubambe ukuze ubeke ngokwenza"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Engeza ngokuzenzakalelayo"</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_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>
+    <string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Sula umbhalo ovela ebhokisini lokusesha"</string>
+    <string name="no_widgets_available" msgid="9140948620298620513">"Awekho amawijethi atholakalayo"</string>
+    <string name="no_search_results" msgid="6518732304311458580">"Ayikho imiphumela yosesho"</string>
+    <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Okomuntu siqu"</string>
+    <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Umsebenzi"</string>
+    <string name="widget_category_conversations" msgid="8894438636213590446">"Izingxoxo"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Sesha izinhlelo zokusebenza"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Ilayisha izinhlelo zokusebenza..."</string>
     <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="notifications_header" msgid="1404149926117359025">"Izaziso"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Thinta futhi ubambe ukuze ukhethe isinqamuleli."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Thepha kabili uphinde ubambe ukuze uphakamise isinqamuleli noma usebenzise izenzo zangokwezifiso."</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>
     <string name="out_of_space" msgid="4691004494942118364">"Asisekho isikhala kulesi sikrini Sasekhaya."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Asisekho isikhala kwitreyi lezintandokazi"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Uhlu lwezinhlelo zokusebenza"</string>
     <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Uhlu lwezinhlelo zokusebenza zomuntu siqu"</string>
     <string name="all_apps_button_work_label" msgid="7270707118948892488">"Uhlu lwezinhlelo zokusebenza zomsebenzi"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Ikhaya"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Susa"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Khipha"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Ulwazi lohlelo lokusebenza"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Ulwazi nge-app"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Faka"</string>
     <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ungaphakamisi uhlelo lokusebenza"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"Ukubikezela Iphinikhodi"</string>
@@ -80,7 +96,6 @@
     <string name="folder_renamed" msgid="1794088362165669656">"Ifolda iqanjwe kabusha ngo-<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format_exact" msgid="8626242716117004803">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> izinto"</string>
     <string name="folder_name_format_overflow" msgid="4270108890534995199">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> noma izinto eziningi"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Amawijethi"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Izitayela nezithombe zangemuva"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Izilungiselelo zasekhaya"</string>
@@ -101,9 +116,9 @@
     <string name="abandoned_search" msgid="891119232568284442">"Sesha"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Lolu hlelo lokusebenza alifakiwe"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Uhlelo lokusebenza lalesi sithonjana alufakiwe. Ungalisusa, noma sesha uhlelo lokusebenza bese uzifakela lona ngokuzenzela."</string>
+    <string name="app_installing_title" msgid="5864044122733792085">"I-<xliff:g id="NAME">%1$s</xliff:g> iyafakwa, seyiqede <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"I-<xliff:g id="NAME">%1$s</xliff:g> iyalandwa, <xliff:g id="PROGRESS">%2$s</xliff:g> kuqediwe"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ilinde ukufakwa"</string>
-    <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> amawijethi"</string>
     <string name="widgets_list" msgid="796804551140113767">"Uhlu lwamawijethi"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Uhlu lwamawijethi luvaliwe"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Faka kusikrini sasekhaya"</string>
@@ -141,8 +156,13 @@
     <string name="work_profile_edu_next" msgid="8783418929296503629">"Okulandelayo"</string>
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ngiyezwa"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"Iphrofayela yomsebenzi iphunyuziwe"</string>
-    <string name="work_apps_paused_body" msgid="2197210494568696054">"Izinhlelo zokusebenza zomsebenzi azikwazi ukukuthumela izaziso, ukusebenzisa ibhethri lakho, noma ukufinyelela indawo yakho"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Iphrofayela yomsebenzi iphunyuziwe. Izinhlelo zokusebenza zomsebenzi azikwazi ukukuthumela izaziso, ukusebenzisa ibhethri yakho, noma ukufinyelela indawo yakho"</string>
+    <string name="work_apps_paused_body" msgid="4209084728264328628">"Ama-app omsebenzi awakwazi ukukuthumela izaziso, ukusebenzisa ibhethri lakho, noma ukufinyelela indawo yakho"</string>
+    <string name="work_apps_paused_content_description" msgid="4473292417145736203">"Iphrofayela yomsebenzi iphunyuziwe. Ama-app omsebenzi awakwazi ukukuthumela izaziso, ukusebenzisa ibhethri lakho, noma ukufinyelela indawo yakho"</string>
+    <string name="work_apps_paused_edu_banner" msgid="8872412121608402058">"Ama-app omsebenzi anebheji futhi ayabonakala kumphathi wakho we-IT"</string>
+    <string name="work_apps_paused_edu_accept" msgid="6377476824357318532">"Ngiyezwa"</string>
+    <string name="work_apps_pause_btn_text" msgid="4669288269140620646">"Misa ama-app omsebenzi"</string>
+    <string name="work_apps_enable_btn_text" msgid="82111102541971856">"Vula"</string>
+    <string name="developer_options_filter_hint" msgid="5896817443635989056">"Hlunga"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"Phumuza izinhlelo zokusebenza zomsebenzi nezaziso"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"Yehlulekile: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 26e6cba..00cf31c 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /* Copyright 2008, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,23 +19,27 @@
 
     <!-- Attributes used for launcher theme -->
     <attr name="allAppsScrimColor" format="color" />
-    <attr name="allAppsInterimScrimAlpha" format="integer" />
+    <attr name="allappsHeaderProtectionColor" format="color" />
     <attr name="allAppsNavBarScrimColor" format="color" />
     <attr name="allAppsTheme" format="reference" />
     <attr name="popupColorPrimary" format="color" />
     <attr name="popupColorSecondary" format="color" />
     <attr name="popupColorTertiary" format="color" />
+    <attr name="popupShadeFirst" format="color" />
+    <attr name="popupShadeSecond" format="color" />
+    <attr name="popupShadeThird" format="color" />
     <attr name="isMainColorDark" format="boolean" />
     <attr name="isWorkspaceDarkText" format="boolean" />
     <attr name="workspaceTextColor" format="color" />
     <attr name="workspaceShadowColor" format="color" />
-    <attr name="workspaceAmbientShadowColor" format="color"/>
+    <attr name="workspaceAmbientShadowColor" format="color" />
     <attr name="workspaceKeyShadowColor" format="color" />
     <attr name="workspaceStatusBarScrim" format="reference" />
     <attr name="widgetsTheme" format="reference" />
-    <attr name="loadingIconColor" format="color" />
-    <attr name="iconOnlyShortcutColor" format="color"/>
-    <attr name="eduHalfSheetBGColor" format="color"/>
+    <attr name="iconOnlyShortcutColor" format="color" />
+    <attr name="eduHalfSheetBGColor" format="color" />
+    <attr name="overviewScrimColor" format="color" />
+    <attr name="popupNotificationDotColor" format="color" />
 
     <attr name="folderDotColor" format="color" />
     <attr name="folderFillColor" format="color" />
@@ -44,8 +47,10 @@
     <attr name="folderIconBorderColor" format="color" />
     <attr name="folderTextColor" format="color" />
     <attr name="folderHintColor" format="color" />
+    <attr name="isFolderDarkText" format="boolean" />
     <attr name="workProfileOverlayTextColor" format="color" />
-    <attr name="disabledIconAlpha" format="float" />
+    <attr name="workspaceAccentColor" format="color" />
+    <attr name="dropTargetHoverTextColor" format="color" />
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
@@ -57,6 +62,9 @@
             <enum name="folder" value="2" />
             <enum name="widget_section" value="3" />
             <enum name="shortcut_popup" value="4" />
+            <enum name="taskbar" value="5" />
+            <enum name="search_result_tall" value="6" />
+            <enum name="search_result_small" value="7" />
         </attr>
         <attr name="centerVertically" format="boolean" />
     </declare-styleable>
@@ -68,12 +76,19 @@
         <attr name="folderDotColor" />
     </declare-styleable>
 
+    <declare-styleable name="SearchResultSuggestion">
+        <attr name="customIcon" format="reference" />
+        <attr name="matchTextInsetWithQuery" format="boolean" />
+    </declare-styleable>
+
+
     <declare-styleable name="ShadowInfo">
         <attr name="ambientShadowColor" format="color" />
         <attr name="ambientShadowBlur" format="dimension" />
         <attr name="keyShadowColor" format="color" />
         <attr name="keyShadowBlur" format="dimension" />
-        <attr name="keyShadowOffset" format="dimension" />
+        <attr name="keyShadowOffsetX" format="dimension" />
+        <attr name="keyShadowOffsetY" format="dimension" />
     </declare-styleable>
 
     <!-- PagedView specific attributes. These attributes are used to customize
@@ -119,13 +134,34 @@
         <!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
         <attr name="numFolderRows" format="integer" />
         <attr name="numFolderColumns" format="integer" />
-        <!-- numHotseatIcons defaults to numColumns, if not specified -->
-        <attr name="numHotseatIcons" format="integer" />
-        <attr name="dbFile" format="string" />
         <!-- numAllAppsColumns defaults to numColumns, if not specified -->
         <attr name="numAllAppsColumns" format="integer" />
+        <!-- Number of columns to use when extending the all-apps size,
+         defaults to 2 * numAllAppsColumns -->
+        <attr name="numExtendedAllAppsColumns" format="integer" />
+
+        <!-- numHotseatIcons defaults to numColumns, if not specified -->
+        <attr name="numHotseatIcons" format="integer" />
+        <!-- Number of icons to use when extending the hotseat size,
+         defaults to 2 * numHotseatIcons -->
+        <attr name="numExtendedHotseatIcons" format="integer" />
+
+        <attr name="dbFile" format="string" />
         <attr name="defaultLayoutId" format="reference" />
         <attr name="demoModeLayoutId" format="reference" />
+        <attr name="isScalable" format="boolean" />
+        <attr name="devicePaddingId" format="reference" />
+
+    </declare-styleable>
+
+    <declare-styleable name="DevicePadding">
+        <attr name="maxEmptySpace" format="dimension" />
+    </declare-styleable>
+
+    <declare-styleable name="DevicePaddingFormula">
+        <attr name="a" format="float|dimension" />
+        <attr name="b" format="float|dimension" />
+        <attr name="c" format="float|dimension" />
     </declare-styleable>
 
     <declare-styleable name="ProfileDisplayOption">
@@ -133,18 +169,31 @@
         <attr name="minWidthDps" format="float" />
         <attr name="minHeightDps" format="float" />
 
+        <!-- These min cell values are only used if GridDisplayOption#isScalable is true-->
+        <attr name="minCellHeightDps" format="float" />
+        <attr name="minCellWidthDps" format="float" />
+
+        <attr name="borderSpacingDps" format="float" />
+
         <attr name="iconImageSize" format="float" />
         <!-- landscapeIconSize defaults to iconSize, if not specified -->
         <attr name="landscapeIconSize" format="float" />
         <attr name="iconTextSize" format="float" />
-        <!-- If true, this display option is used to determine the default grid -->
-        <attr name="canBeDefault" format="boolean" />
+        <!-- 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>
 
         <!-- 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" />
+
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
@@ -159,15 +208,27 @@
         <attr name="android:src" />
         <attr name="android:shadowColor" />
         <attr name="android:elevation" />
-        <attr name="darkTintColor" format="color"/>
+        <attr name="darkTintColor" format="color" />
     </declare-styleable>
 
     <declare-styleable name="RecyclerViewFastScroller">
         <attr name="canThumbDetach" format="boolean" />
     </declare-styleable>
 
+    <declare-styleable name="LoggablePref">
+        <attr name="android:key" />
+        <attr name="android:defaultValue" />
+        <!-- Ground truth of this Pref integer can be found in StatsLogManager -->
+        <attr name="logIdOn" format="integer" />
+        <attr name="logIdOff" format="integer" />
+    </declare-styleable>
+
     <declare-styleable name="PreviewFragment">
         <attr name="android:name" />
         <attr name="android:id" />
     </declare-styleable>
+
+    <declare-styleable name="WidgetsListRowHeader">
+        <attr name="appIconSize" format="dimension" />
+    </declare-styleable>
 </resources>
diff --git a/res/values/bools.xml b/res/values/bools.xml
deleted file mode 100644
index bc2c678..0000000
--- a/res/values/bools.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources>
-    <bool name="notification_dots_enabled">false</bool>
-
-    <bool name="enable_install_shortcut_api">true</bool>
-</resources>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 043ad9a..1b68fb6 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -17,7 +17,8 @@
 ** limitations under the License.
 */
 -->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <!-- The color tints to apply to the text and drag view when hovering
          over the delete target or the info target -->
     <color name="delete_target_hover_tint">#FFC1C1C1</color>
@@ -35,11 +36,51 @@
 
     <color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
 
-    <color name="all_apps_bg_hand_fill">#E5E5E5</color>
-    <color name="all_apps_bg_hand_fill_dark">#9AA0A6</color>
-
     <color name="gesture_tutorial_ripple_color">#A0C2F9</color> <!-- Light Blue -->
     <color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
-    <color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
-    <color name="gesture_tutorial_primary_color">#1A73E8</color> <!-- 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 -->
+    <color name="popup_color_neutral_dark">#3C4043</color> <!-- Gray 800 -->
+    <color name="popup_color_primary_dark">#3C4043</color> <!-- Gray 800 -->
+    <color name="popup_color_secondary_dark">#202124</color>
+    <color name="popup_color_tertiary_dark">#757575</color> <!-- Gray 600 -->
+
+    <color name="popup_shade_first_light">#F9F9F9</color>
+    <color name="popup_shade_second_light">#F1F1F1</color>
+    <color name="popup_shade_third_light">#E2E2E2</color>
+    <color name="popup_shade_first_dark">#303030</color>
+    <color name="popup_shade_second_dark">#262626</color>
+    <color name="popup_shade_third_dark">#1B1B1B</color>
+
+    <color name="popup_notification_dot_light">#FFF</color>
+    <color name="popup_notification_dot_dark">#757575</color>
+
+    <color name="workspace_text_color_light">#FFF</color>
+    <color name="workspace_text_color_dark">#FF000000</color>
+
+    <color name="folder_hint_text_color_light">#FFF</color>
+    <color name="folder_hint_text_color_dark">#FF000000</color>
+
+    <color name="folder_background_light">#F9F9F9</color>
+    <color name="folder_background_dark">#464746</color>
+
+    <color name="folder_dot_color">?attr/colorPrimary</color>
+
+    <color name="text_color_primary_dark">#FFFFFFFF</color>
+    <color name="text_color_secondary_dark">#FFFFFFFF</color>
+    <color name="text_color_tertiary_dark">#CCFFFFFF</color>
+
+    <color name="wallpaper_popup_scrim">?android:attr/colorAccent</color>
+    <color name="wallpaper_scrim_color">#0D878787</color>
+
+    <color name="workspace_accent_color_light">#ff8df5e3</color>
+    <color name="workspace_accent_color_dark">#ff3d665f</color>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 75fcc90..04c359e 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,7 +1,6 @@
 <resources>
     <!-- Miscellaneous -->
     <bool name="config_largeHeap">false</bool>
-    <bool name="allow_rotation">false</bool>
 
     <integer name="extracted_color_gradient_alpha">153</integer>
 
@@ -24,7 +23,7 @@
 
     <!-- AllApps & Launcher transitions -->
     <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
-    <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
+    <integer name="config_workspaceSpringLoadShrinkPercentage">85</integer>
 
     <!-- The duration of the animation from search hint to text entry -->
     <integer name="config_searchHintAnimationDuration">50</integer>
@@ -32,13 +31,16 @@
     <!-- View tag key used to store SpringAnimation data. -->
     <item type="id" name="spring_animation_tag" />
 
+    <!-- View tag key used to determine if we should fade in the child views.. -->
+    <string name="popup_container_iterate_children" translatable="false">popup_container_iterate_children</string>
+
     <!-- Workspace -->
     <!-- The duration (in ms) of the fade animation on the object outlines, used when
          we are dragging objects around on the home screen. -->
-    <integer name="config_dragOutlineFadeTime">900</integer>
+    <integer name="config_dragOutlineFadeTime">500</integer>
 
     <!-- The alpha value at which to show the most recent drop visualization outline. -->
-    <integer name="config_dragOutlineMaxAlpha">128</integer>
+    <integer name="config_dragOutlineMaxAlpha">255</integer>
 
     <!-- Parameters controlling the animation for when an item is dropped on the home screen,
          and it animates from its old position to the new one. -->
@@ -55,21 +57,15 @@
     <!-- The duration of the caret animation -->
     <integer name="config_caretAnimationDuration">200</integer>
 
-    <!-- Hotseat -->
-    <bool name="hotseat_transpose_layout_with_orientation">true</bool>
-
     <!-- Various classes overriden by projects/build flavors. -->
-    <string name="app_filter_class" translatable="false"></string>
-    <string name="user_event_dispatcher_class" translatable="false"></string>
     <string name="folder_name_provider_class" translatable="false"></string>
     <string name="stats_log_manager_class" translatable="false"></string>
-    <string name="app_transition_manager_class" translatable="false"></string>
     <string name="instant_app_resolver_class" translatable="false"></string>
     <string name="main_process_initializer_class" translatable="false"></string>
     <string name="app_launch_tracker_class" translatable="false"></string>
     <string name="test_information_handler_class" translatable="false"></string>
     <string name="launcher_activity_logic_class" translatable="false"></string>
-    <string name="prediction_model_class" translatable="false"></string>
+    <string name="model_delegate_class" translatable="false"></string>
 
     <!-- View ID to use for QSB widget -->
     <item type="id" name="qsb_widget" />
@@ -80,18 +76,15 @@
     <!-- View IDs to store item highlight information -->
     <item type="id" name="view_unhighlight_background" />
 
+    <!-- view ID used to restore work tab state -->
+    <item type="id" name="work_tab_state_id" />
+
     <!-- Menu id for feature flags -->
     <item type="id" name="menu_apply_flags" />
 
-    <!-- Popup items -->
-    <integer name="config_popupOpenCloseDuration">150</integer>
-    <integer name="config_popupArrowOpenCloseDuration">40</integer>
-    <integer name="config_removeNotificationViewDuration">300</integer>
-
     <!-- Default packages -->
     <string name="wallpaper_picker_package" translatable="false"></string>
-    <string name="calendar_component_name" translatable="false"></string>
-    <string name="clock_component_name" translatable="false"></string>
+    <string name="local_colors_extraction_class" translatable="false"></string>
 
     <!-- Accessibility actions -->
     <item type="id" name="action_remove" />
@@ -113,6 +106,7 @@
     <!-- QSB IDs. DO not change -->
     <item type="id" name="search_container_workspace" />
     <item type="id" name="search_container_all_apps" />
+    <item type="id" name="search_container_hotseat" />
 
     <!-- Recents -->
     <item type="id" name="overview_panel"/>
@@ -137,9 +131,46 @@
     <item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
 
     <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>
+
+    <item name="swipe_up_trans_y_damping" type="dimen" format="float">0.45</item>
+    <item name="swipe_up_trans_y_stiffness" type="dimen" format="float">200</item>
+
     <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>
@@ -154,37 +185,37 @@
     <dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
 
     <array name="dynamic_resources">
-        <item>@dimen/all_apps_spring_damping_ratio</item>
-        <item>@dimen/all_apps_spring_stiffness</item>
+        <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/dismiss_task_trans_y_damping_ratio</item>
-        <item>@dimen/dismiss_task_trans_y_stiffness</item>
+        <item>@dimen/c1_a</item>
+        <item>@dimen/c1_b</item>
+        <item>@dimen/c1_c</item>
+        <item>@dimen/c1_d</item>
 
-        <item>@dimen/dismiss_task_trans_x_damping_ratio</item>
-        <item>@dimen/dismiss_task_trans_x_stiffness</item>
+        <item>@dimen/mp_x</item>
+        <item>@dimen/mp_y</item>
 
-        <item>@dimen/horizontal_spring_damping_ratio</item>
-        <item>@dimen/horizontal_spring_stiffness</item>
-
-        <item>@dimen/swipe_up_rect_scale_damping_ratio</item>
-        <item>@dimen/swipe_up_rect_scale_stiffness</item>
-
-        <item>@dimen/swipe_up_rect_xy_fling_friction</item>
-        <item>@dimen/swipe_up_rect_xy_damping_ratio</item>
-        <item>@dimen/swipe_up_rect_xy_stiffness</item>
-
-        <item>@dimen/staggered_damping_ratio</item>
-        <item>@dimen/staggered_stiffness</item>
-        <item>@dimen/unlock_staggered_velocity_dp_per_s</item>
-
-        <item>@dimen/swipe_up_fling_min_visible_change</item>
-        <item>@dimen/swipe_up_y_overshoot</item>
-
-        <item>@dimen/hint_scale_damping_ratio</item>
-        <item>@dimen/hint_scale_stiffness</item>
-        <item>@dimen/hint_scale_velocity_dp_per_s</item>
+        <item>@dimen/c2_a</item>
+        <item>@dimen/c2_b</item>
+        <item>@dimen/c2_c</item>
+        <item>@dimen/c2_d</item>
     </array>
 
-    <string-array name="live_wallpapers_remove_sysui_scrims">
-    </string-array>
+    <string-array name="filtered_components" ></string-array>
+
+    <!-- Name of the class used to generate colors from the wallpaper colors. Must be implementing the LauncherAppWidgetHostView.ColorGenerator interface. -->
+    <string name="color_generator_class" translatable="false"/>
+
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 947e635..f434644 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,13 +20,17 @@
 
     <!-- Dynamic Grid -->
     <dimen name="dynamic_grid_edge_margin">8dp</dimen>
-    <dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
+    <dimen name="dynamic_grid_left_right_margin">8dp</dimen>
+    <dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen>
     <!-- Minimum space between workspace and hotseat in spring loaded mode -->
     <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
 
+    <dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
     <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>
@@ -35,26 +39,30 @@
     <dimen name="dynamic_grid_hotseat_extra_vertical_size">34dp</dimen>
     <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 -->
     <dimen name="workspace_page_indicator_height">24dp</dimen>
     <dimen name="workspace_page_indicator_line_height">1dp</dimen>
     <dimen name="workspace_page_indicator_overlap_workspace">0dp</dimen>
 
-    <!-- Hotseat/all-apps scrim -->
-    <dimen name="all_apps_scrim_blur">4dp</dimen>
-    <dimen name="vertical_drag_handle_width">18dp</dimen>
-    <dimen name="vertical_drag_handle_height">6dp</dimen>
-    <dimen name="vertical_drag_handle_elevation">1dp</dimen>
-    <dimen name="vertical_drag_handle_touch_size">48dp</dimen>
-    <dimen name="vertical_drag_handle_padding_in_vertical_bar_layout">16dp</dimen>
-
 <!-- Drop target bar -->
-    <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
+    <dimen name="dynamic_grid_drop_target_size">52dp</dimen>
     <dimen name="drop_target_vertical_gap">20dp</dimen>
 
 <!-- App Widget resize frame -->
     <dimen name="widget_handle_margin">13dp</dimen>
     <dimen name="resize_frame_background_padding">24dp</dimen>
+    <dimen name="resize_frame_margin">22dp</dimen>
+
+    <!-- App widget reconfigure button -->
+    <dimen name="widget_reconfigure_button_corner_radius">14dp</dimen>
+    <dimen name="widget_reconfigure_button_padding">6dp</dimen>
+    <dimen name="widget_reconfigure_button_margin">32dp</dimen>
+    <dimen name="widget_reconfigure_button_size">36dp</dimen>
+    <dimen name="widget_reconfigure_tip_top_margin">16dp</dimen>
 
 <!-- Fast scroll -->
     <dimen name="fastscroll_track_min_width">6dp</dimen>
@@ -78,24 +86,30 @@
     <dimen name="fastscroll_end_margin">-26dp</dimen>
 
     <!-- All Apps -->
+    <dimen name="all_apps_open_vertical_translate">320dp</dimen>
     <dimen name="all_apps_search_bar_field_height">48dp</dimen>
     <dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
     <dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
     <dimen name="all_apps_background_canvas_width">700dp</dimen>
     <dimen name="all_apps_background_canvas_height">475dp</dimen>
-    <dimen name="all_apps_header_tab_height">50dp</dimen>
+    <dimen name="all_apps_header_pill_height">48dp</dimen>
+    <dimen name="all_apps_header_pill_corner_radius">12dp</dimen>
+    <dimen name="all_apps_header_tab_height">48dp</dimen>
     <dimen name="all_apps_tabs_indicator_height">2dp</dimen>
     <dimen name="all_apps_header_top_padding">36dp</dimen>
+    <dimen name="all_apps_header_bottom_padding">6dp</dimen>
     <dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
     <dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
-    <dimen name="all_apps_tabs_side_padding">12dp</dimen>
-    <dimen name="all_apps_divider_height">1dp</dimen>
+    <dimen name="all_apps_tabs_button_horizontal_padding">4dp</dimen>
+    <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_tip_bottom_margin">8dp</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_arrow_width">10dp</dimen>
 
 <!-- Search bar in All Apps -->
     <dimen name="all_apps_header_max_elevation">3dp</dimen>
@@ -104,12 +118,47 @@
 
     <dimen name="all_apps_divider_margin_vertical">8dp</dimen>
 
+<!-- Floating action button inside work tab to toggle work profile -->
+    <dimen name="work_fab_height">56dp</dimen>
+    <dimen name="work_fab_radius">28dp</dimen>
+    <dimen name="work_card_padding_horizontal">24dp</dimen>
+    <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>
 
-<!-- Widget tray -->
-    <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
-    <dimen name="widget_preview_label_horizontal_padding">16dp</dimen>
+    <!-- rounded button shown inside card views, and snack bars  -->
+    <dimen name="padded_rounded_button_height">48dp</dimen>
+    <dimen name="rounded_button_height">32dp</dimen>
+    <dimen name="rounded_button_radius">16dp</dimen>
+    <dimen name="rounded_button_padding">8dp</dimen>
+
+
+    <!-- Widget tray -->
+    <dimen name="widget_cell_vertical_padding">8dp</dimen>
+    <dimen name="widget_cell_horizontal_padding">16dp</dimen>
+    <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_apps_tabs_vertical_padding">6dp</dimen>
+
+    <dimen name="recommended_widgets_table_vertical_padding">8dp</dimen>
+
+    <!-- Bottom margin for the search and recommended widgets container without work profile -->
+    <dimen name="search_and_recommended_widgets_container_bottom_margin">16dp</dimen>
+    <!-- Bottom margin for the search and recommended widgets container with work profile -->
+    <dimen name="search_and_recommended_widgets_container_small_bottom_margin">10dp</dimen>
+
+    <dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
+    <dimen name="widget_list_content_corner_radius">4dp</dimen>
+
+    <dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
+    <dimen name="widget_list_entry_spacing">2dp</dimen>
+    <dimen name="widget_list_horizontal_margin">16dp</dimen>
 
     <dimen name="widget_preview_shadow_blur">0.5dp</dimen>
     <dimen name="widget_preview_key_shadow_distance">1dp</dimen>
@@ -121,21 +170,32 @@
     <dimen name="widget_section_icon_size">40dp</dimen>
     <dimen name="widget_section_vertical_padding">8dp</dimen>
     <dimen name="widget_section_horizontal_padding">16dp</dimen>
-    <dimen name="widget_section_indent">0dp</dimen>
 
     <dimen name="widget_row_padding">8dp</dimen>
     <dimen name="widget_row_divider">2dp</dimen>
 
+    <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>
     <dimen name="shortcut_preview_padding_top">0dp</dimen>
 
+<!-- Pin widget dialog -->
+    <dimen name="pin_widget_button_padding_horizontal">8dp</dimen>
+    <dimen name="pin_widget_button_padding_vertical">4dp</dimen>
+    <dimen name="pin_widget_button_inset_horizontal">4dp</dimen>
+    <dimen name="pin_widget_button_inset_vertical">6dp</dimen>
+
 <!-- Dragging -->
     <!-- Drag padding to add to the bottom of drop targets -->
     <dimen name="drop_target_drag_padding">14dp</dimen>
-    <dimen name="drop_target_text_size">14sp</dimen>
+    <dimen name="drop_target_text_size">16sp</dimen>
     <dimen name="drop_target_shadow_elevation">2dp</dimen>
+    <dimen name="drop_target_bar_margin_horizontal">4dp</dimen>
 
     <!-- the distance an icon must be dragged before button drop targets accept it -->
     <dimen name="drag_distanceThreshold">30dp</dimen>
@@ -146,17 +206,20 @@
 
     <dimen name="drag_flingToDeleteMinVelocity">-1500dp</dimen>
 
-    <dimen name="spring_loaded_panel_border">1dp</dimen>
+    <dimen name="spring_loaded_panel_border">2dp</dimen>
+    <dimen name="keyboard_drag_stroke_width">4dp</dimen>
 
 <!-- Folders -->
     <dimen name="page_indicator_dot_size">8dp</dimen>
 
     <dimen name="folder_cell_x_padding">9dp</dimen>
     <dimen name="folder_cell_y_padding">6dp</dimen>
-    <dimen name="folder_child_text_size">13sp</dimen>
-    <dimen name="folder_label_padding_top">4dp</dimen>
-    <dimen name="folder_label_padding_bottom">12dp</dimen>
-    <dimen name="folder_label_text_size">14sp</dimen>
+    <!-- label text size = workspace text size multiplied by this scale -->
+    <dimen name="folder_label_text_scale">1.14</dimen>
+    <dimen name="folder_label_height">48dp</dimen>
+
+    <dimen name="folder_content_padding_left_right">8dp</dimen>
+    <dimen name="folder_content_padding_top">16dp</dimen>
 
 <!-- Sizes for managed profile badges -->
     <dimen name="profile_badge_size">24dp</dimen>
@@ -174,63 +237,53 @@
     <dimen name="pending_widget_elevation">2dp</dimen>
 
 <!-- Deep shortcuts -->
-    <dimen name="deep_shortcuts_elevation">9dp</dimen>
-    <dimen name="bg_popup_item_width">220dp</dimen>
+    <dimen name="deep_shortcuts_elevation">2dp</dimen>
+    <dimen name="bg_popup_padding">2dp</dimen>
+    <dimen name="bg_popup_item_width">216dp</dimen>
     <dimen name="bg_popup_item_height">56dp</dimen>
-    <dimen name="bg_popup_item_condensed_height">48dp</dimen>
     <dimen name="pre_drag_view_scale">6dp</dimen>
     <!-- an icon with shortcuts must be dragged this far before the container is removed. -->
     <dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
-    <dimen name="deep_shortcut_icon_size">36dp</dimen>
-    <dimen name="deep_shortcut_drawable_padding">8dp</dimen>
+    <dimen name="deep_shortcut_icon_size">32dp</dimen>
+    <dimen name="popup_margin">2dp</dimen>
+    <dimen name="popup_single_item_radius">100dp</dimen>
+    <dimen name="popup_smaller_radius">4dp</dimen>
+    <dimen name="deep_shortcut_drawable_padding">12dp</dimen>
     <dimen name="deep_shortcut_drag_handle_size">16dp</dimen>
     <dimen name="popup_padding_start">10dp</dimen>
     <dimen name="popup_padding_end">16dp</dimen>
     <dimen name="popup_vertical_padding">4dp</dimen>
-    <dimen name="popup_arrow_width">10dp</dimen>
-    <dimen name="popup_arrow_height">8dp</dimen>
-    <dimen name="popup_arrow_vertical_offset">-2dp</dimen>
+    <dimen name="popup_arrow_width">12dp</dimen>
+    <dimen name="popup_arrow_height">10dp</dimen>
+    <dimen name="popup_arrow_vertical_offset">-1dp</dimen>
     <!-- popup_padding_start + deep_shortcut_icon_size / 2 -->
-    <dimen name="popup_arrow_horizontal_center_start">28dp</dimen>
-    <!-- popup_padding_end + deep_shortcut_drag_handle_size / 2 -->
-    <dimen name="popup_arrow_horizontal_center_end">24dp</dimen>
+    <dimen name="popup_arrow_horizontal_center_offset">26dp</dimen>
     <dimen name="popup_arrow_corner_radius">2dp</dimen>
     <!-- popup_padding_start + icon_size + 10dp -->
-    <dimen name="deep_shortcuts_text_padding_start">56dp</dimen>
-    <!-- popup_item_width - deep_shortcuts_text_padding_start -->
-    <dimen name="deep_shortcuts_divider_width">164dp</dimen>
+    <dimen name="deep_shortcuts_text_padding_start">52dp</dimen>
     <dimen name="system_shortcut_icon_size">24dp</dimen>
-    <!-- popup_arrow_center_start - system_shortcut_icon_size / 2 -->
+    <!-- popup_arrow_horizontal_center_offset - system_shortcut_icon_size / 2 -->
     <dimen name="system_shortcut_margin_start">16dp</dimen>
-    <dimen name="system_shortcut_header_height">48dp</dimen>
+    <dimen name="system_shortcut_header_height">56dp</dimen>
     <dimen name="system_shortcut_header_icon_touch_size">48dp</dimen>
     <!-- (touch_size - icon_size) / 2 -->
     <dimen name="system_shortcut_header_icon_padding">12dp</dimen>
 
 <!-- Notifications -->
     <dimen name="bg_round_rect_radius">8dp</dimen>
-    <dimen name="notification_padding_start">16dp</dimen>
-    <dimen name="notification_padding_end">12dp</dimen>
-    <!-- notification_padding_end + (icon_size - footer_icon_size) / 2 -->
-    <dimen name="notification_footer_icon_row_padding">15dp</dimen>
-    <dimen name="notification_header_height">36dp</dimen>
-    <dimen name="notification_main_height">84dp</dimen>
-    <dimen name="notification_footer_height">32dp</dimen>
-    <!-- How much space to keep as padding for the last notification when the footer collapses -->
-    <dimen name="notification_empty_footer_height">6dp</dimen>
-    <dimen name="notification_header_text_size">13sp</dimen>
+    <dimen name="notification_padding">16dp</dimen>
+    <dimen name="notification_padding_top">18dp</dimen>
+    <dimen name="notification_header_text_size">14sp</dimen>
     <dimen name="notification_header_count_text_size">12sp</dimen>
-    <dimen name="notification_main_title_size">16sp</dimen>
+    <dimen name="notification_main_title_size">14sp</dimen>
     <dimen name="notification_main_text_size">14sp</dimen>
-    <dimen name="notification_icon_size">24dp</dimen>
-    <dimen name="notification_footer_icon_size">18dp</dimen>
-    <!-- notification_icon_size + notification_padding_end + 16dp padding between icon and text -->
-    <dimen name="notification_main_text_padding_end">52dp</dimen>
+    <dimen name="notification_circle_icon_size">24dp</dimen>
+    <dimen name="notification_icon_size">32dp</dimen>
+    <!-- Space between edge and icon and icon and text -->
+    <dimen name="notification_icon_padding">12dp</dimen>
+    <!-- notification_icon_padding + notification_icon_size + notification_icon_padding -->
+    <dimen name="notification_main_text_padding_start">56dp</dimen>
     <dimen name="horizontal_ellipsis_size">18dp</dimen>
-    <!-- arrow_horizontal_offset_start - (ellipsis_size - arrow_width) / 2 -->
-    <dimen name="horizontal_ellipsis_offset">19dp</dimen>
-    <dimen name="popup_item_divider_height">0.5dp</dimen>
-    <dimen name="swipe_helper_falsing_threshold">70dp</dimen>
 
 <!-- Overview -->
     <dimen name="options_menu_icon_size">24dp</dimen>
@@ -247,10 +300,35 @@
     <dimen name="snackbar_min_text_size">12sp</dimen>
     <dimen name="snackbar_max_text_size">14sp</dimen>
 
+<!-- Developer Options -->
+    <dimen name="developer_options_filter_margins">10dp</dimen>
+
 <!-- Theming related -->
     <dimen name="default_dialog_corner_radius">8dp</dimen>
+    <dimen name="dialogCornerRadius">@dimen/default_dialog_corner_radius</dimen>
 
     <!-- Onboarding bottomsheet related -->
     <dimen name="bottom_sheet_edu_padding">24dp</dimen>
 
+<!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
+    <dimen name="taskbar_size">0dp</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 -->
+    <dimen name="task_thumbnail_icon_size">0dp</dimen>
+    <dimen name="task_thumbnail_icon_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>
+
+<!-- Workspace grid visualization parameters -->
+    <dimen name="grid_visualization_rounding_radius">22dp</dimen>
+    <dimen name="grid_visualization_cell_spacing">6dp</dimen>
+
+<!-- Search results related parameters -->
+    <dimen name="search_row_icon_size">48dp</dimen>
+    <dimen name="search_row_small_icon_size">32dp</dimen>
+    <dimen name="padded_rounded_button_padding">8dp</dimen>
 </resources>
diff --git a/res/values/drawables.xml b/res/values/drawables.xml
deleted file mode 100644
index 7d63142..0000000
--- a/res/values/drawables.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <drawable name="ic_setup_shadow">@drawable/ic_setting</drawable>
-    <drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
-    <drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
-    <drawable name="ic_block_shadow">@drawable/ic_block_no_shadow</drawable>
-    <drawable name="all_apps_arrow_shadow">@drawable/drag_handle_indicator_no_shadow</drawable>
-</resources>
\ No newline at end of file
diff --git a/res/values/id.xml b/res/values/id.xml
new file mode 100644
index 0000000..1709c59
--- /dev/null
+++ b/res/values/id.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<resources>
+    <item type="id" name="apps_list_view_work" />
+    <item type="id" name="tag_widget_entry" />
+    <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" />
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 935bb40..d1774e5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -36,24 +36,92 @@
     <!-- Message shown when a shortcut is not available. It could have been temporarily disabled and may start working again after some time. -->
     <string name="shortcut_not_available">Shortcut isn\'t available</string>
     <!-- User visible name for the launcher/home screen. [CHAR_LIMIT=30] -->
-    <string name="home_screen">Home screen</string>
-    <!-- Label for showing custom action list of a shortcut or widget. [CHAR_LIMIT=30] -->
-    <string name="custom_actions">Custom actions</string>
+    <string name="home_screen">Home</string>
+
+    <!-- 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>
 
     <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
-    <string name="long_press_widget_to_add">Touch &amp; hold to pick up a widget.</string>
+    <string name="long_press_widget_to_add">Touch &amp; hold to move a widget.</string>
     <!-- Accessibility spoken hint message in widget picker, which allows user to add a widget. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] -->
-    <string name="long_accessible_way_to_add">Double-tap &amp; hold to pick up a widget or use custom actions.</string>
+    <string name="long_accessible_way_to_add">Double-tap &amp; hold to move a widget or use custom actions.</string>
     <!-- The format string for the dimensions of a widget in the drawer -->
     <!-- There is a special version of this format string for Farsi -->
     <string name="widget_dims_format">%1$d \u00d7 %2$d</string>
     <!-- Accessibility spoken message format for the dimensions of a widget in the drawer -->
     <string name="widget_accessible_dims_format">%1$d wide by %2$d high</string>
-    <!-- Message to tell the user to press and hold a widget/icon to add it  -->
-    <string name="add_item_request_drag_hint">Touch &amp; hold to place manually</string>
-    <!-- Button label to automatically add icon on home screen [CHAR_LIMIT=50] -->
-    <string name="place_automatically">Add automatically</string>
+    <!-- Spoken text for a screen reader. The placeholder text is the widget name.
+         [CHAR_LIMIT=none]-->
+    <string name="widget_preview_context_description"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget</string>
+    <!-- Message to tell the user to press and hold a widget/icon to add it to the home screen.
+         [CHAR LIMIT=NONE]  -->
+    <string name="add_item_request_drag_hint">Touch &amp; hold the widget to move it around the Home screen</string>
+    <!-- Button label to automatically add a widget to home screen [CHAR_LIMIT=50] -->
+    <string name="add_to_home_screen">Add to Home screen</string>
+    <!-- Accessibility spoken message announced when a widget gets added to the home screen using a
+         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>
+    <!-- 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>
+    <!-- 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>
+
+    <!-- Text for both the tile of a popup view, which shows all available widgets installed on
+         the device, and the text of a button, which opens this popup view. [CHAR LIMIT=30]-->
+    <string name="widget_button_text">Widgets</string>
+    <!-- Search bar text shown in the popup view showing all available widgets installed on the
+         device. [CHAR_LIMIT=50] -->
+    <string name="widgets_full_sheet_search_bar_hint">Search</string>
+    <!-- Spoken text for screen readers. This text lets a user know that the button is used to clear
+         the text that the user entered in the search box. [CHAR_LIMIT=none] -->
+    <string name="widgets_full_sheet_cancel_button_description">Clear text from search box</string>
+    <!-- Text shown when there are no widgets or shortcuts that can be added to the device.
+         [CHAR_LIMIT=none] -->
+    <string name="no_widgets_available">Widgets and shortcuts aren\'t available</string>
+    <!-- Text shown when there are no matching widget or shortcut search results for user's search
+         query. [CHAR_LIMIT=none] -->
+    <string name="no_search_results">No widgets or shortcuts found</string>
+    <!-- Tab label. A user can tap this tab to access their personal widgets. [CHAR_LIMIT=25] -->
+    <string name="widgets_full_sheet_personal_tab">Personal</string>
+    <!-- Tab label. A user can tap this tab to access their work widgets. [CHAR_LIMIT=25] -->
+    <string name="widgets_full_sheet_work_tab">Work</string>
+
+    <!-- A widget category label for grouping widgets related to conversations. [CHAR_LIMIT=30] -->
+    <string name="widget_category_conversations">Conversations</string>
+
+    <!-- Title of a dialog. This dialog lets a user know how they can use widgets on their phone.
+         [CHAR_LIMIT=NONE] -->
+    <string name="widget_education_header">Useful info at your fingertips</string>
+    <!-- Dialog text. This dialog lets a user know how they can use widgets on their phone.
+         [CHAR_LIMIT=NONE] -->
+    <string name="widget_education_content">To get info without opening apps, you can add widgets to your Home screen</string>
+
+    <!-- Text on an educational tip on widget informing users that they can change widget settings.
+         [CHAR_LIMIT=NONE] -->
+    <string name="reconfigurable_widget_education_tip">Tap to change widget settings</string>
+
+    <!-- Text on the button that closes the education dialog about widgets. [CHAR_LIMIT=50] -->
+    <string name="widget_education_close_button">Got it</string>
+
+    <!-- Spoken text for screen readers. This text is for an icon that lets the user change a
+         widget's settings. [CHAR_LIMIT=50] -->
+    <string name="widget_reconfigure_button_content_description">Change widget settings</string>
 
     <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
@@ -73,13 +141,13 @@
 
     <!-- Drag and drop -->
     <!-- Message to tell the user to press and hold on a shortcut to add it [CHAR_LIMIT=50] -->
-    <string name="long_press_shortcut_to_add">Touch &amp; hold to pick up a shortcut.</string>
+    <string name="long_press_shortcut_to_add">Touch &amp; hold to move a shortcut.</string>
     <!-- Accessibility spoken hint message in deep shortcut menu, which allows user to add a shortcut. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=200] -->
-    <string name="long_accessible_way_to_add_shortcut">Double-tap &amp; hold to pick up a shortcut or use custom actions.</string>
+    <string name="long_accessible_way_to_add_shortcut">Double-tap &amp; hold to move a shortcut or use custom actions.</string>
 
     <skip />
-    <!-- Error message when user has filled a home screen -->
-    <string name="out_of_space">No more room on this Home screen.</string>
+    <!-- Error message when a user can't add more apps, widgets, or shortcuts to a Home screen. -->
+    <string name="out_of_space">No room on this Home screen</string>
     <!-- Error message when user has filled the hotseat -->
     <string name="hotseat_out_of_space">No more room in the Favorites tray</string>
 
@@ -88,10 +156,6 @@
     <string name="all_apps_button_personal_label">Personal apps list</string>
     <string name="all_apps_button_work_label">Work apps list</string>
 
-    <!-- Label for button in all applications label to go back home (to the workspace / desktop)
-         for accessibilty (spoken when the button gets focus). -->
-    <string name="all_apps_home_button_label">Home</string>
-
     <!-- Label for remove drop target (from the homescreen only).
          May appear next to uninstall_drop_target_label [CHAR_LIMIT=20] -->
     <string name="remove_drop_target_label">Remove</string>
@@ -132,11 +196,11 @@
     <!-- Widgets: -->
     <skip />    
 
-    <!-- Text to show user in place of a gadget when we can't display it properly -->
-    <string name="gadget_error_text">Problem loading widget</string>
+    <!-- Error text that lets a user know that the widget can't load. -->
+    <string name="gadget_error_text">Can\'t load widget</string>
 
-    <!-- Text to show user in place of a gadget when it is not yet initialized. -->
-    <string name="gadget_setup_text">Setup</string>
+    <!-- Instructional text to encourage a user to finish setting up the widget. -->
+    <string name="gadget_setup_text">Tap to finish setup</string>
 
     <!-- Text to inform the user that they can't uninstall a system application -->
     <string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
@@ -178,13 +242,11 @@
     <string name="folder_name_format_overflow">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> or more items</string>
 
     <!-- Strings for the customization mode -->
-    <!-- Text for widget add button -->
-    <string name="widget_button_text">Widgets</string>
-    <!-- Text for wallpaper change button -->
+    <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
     <string name="wallpaper_button_text">Wallpapers</string>
-    <!-- Text for wallpaper change button -->
-    <string name="styles_wallpaper_button_text">Styles &amp; wallpapers</string>
-    <!-- Text for settings button [CHAR LIMIT=20]-->
+    <!-- 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]-->
     <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>
@@ -228,14 +290,14 @@
     <string name="abandoned_promise_explanation">The app for this icon isn\'t installed.
         You can remove it, or search for the app and install it manually.
     </string>
+    <!-- Title for an app which is being installed. -->
+    <string name="app_installing_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> installing, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
     <!-- Title for an app which is being downloaded. -->
     <string name="app_downloading_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> downloading, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
     <!-- Title for an app whose download has been started. -->
     <string name="app_waiting_download_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> waiting to install</string>
 
     <!-- Strings for widgets & more in the popup container/bottom sheet -->
-    <!-- Title for a bottom sheet that shows widgets for a particular app -->
-    <string name="widgets_bottom_sheet_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> widgets</string>
 
     <!-- Accessibility title for the popup containing a list of widgets. [CHAR_LIMIT=50] -->
     <string name="widgets_list">Widgets list</string>
@@ -332,26 +394,29 @@
 
     <!-- This string is in the work profile tab when a user has All Apps open on their phone. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective.-->
     <string name="work_profile_toggle_label">Work profile</string>
-    <!--- User onboarding title for personal apps -->
-    <string name="work_profile_edu_personal_apps">Personal data is separate &amp; hidden from work apps</string>
     <!--- User onboarding title for work profile apps -->
-    <string name="work_profile_edu_work_apps">Work apps &amp; data are visible to your IT admin</string>
-    <!-- Action label to proceed to the next work profile edu section-->
-    <string name="work_profile_edu_next">Next</string>
+    <string name="work_profile_edu_work_apps">Work apps are badged and visible to your IT admin</string>
     <!-- Action label to finish work profile edu-->
     <string name="work_profile_edu_accept">Got it</string>
 
     <!--- heading shown when user opens work apps tab while work apps are paused -->
-    <string name="work_apps_paused_title">Work profile is paused</string>
+    <string name="work_apps_paused_title">Work apps are paused</string>
     <!--- body shown when user opens work apps tab while work apps are paused -->
-    <string name="work_apps_paused_body">Work apps can\'t send you notifications, use your battery, or access your location</string>
+    <string name="work_apps_paused_body">Your work apps can’t send you notifications, use your battery, or access your location</string>
     <!-- content description for paused work apps list -->
-    <string name="work_apps_paused_content_description">Work profile is paused. Work apps can\’t send you notifications, use your battery, or access your location</string>
+    <string name="work_apps_paused_content_description">Work apps are off. Your work apps can’t send you notifications, use your battery, or access your location</string>
+    <!-- string shown in educational banner about work profile -->
+    <string name="work_apps_paused_edu_banner">Work apps are badged and visible to your IT admin</string>
+    <!-- button string shown to dismiss work tab education -->
+    <string name="work_apps_paused_edu_accept">Got it</string>
 
+    <!-- button string shown pause work profile -->
+    <string name="work_apps_pause_btn_text">Turn off work apps</string>
+    <!-- button string shown enable work profile -->
+    <string name="work_apps_enable_btn_text">Turn on work apps</string>
 
-
-    <!-- A tip shown pointing at work toggle -->
-    <string name="work_switch_tip">Pause work apps and notifications</string>
+    <!-- A hint shown in launcher settings develop options filter box -->
+    <string name="developer_options_filter_hint">Filter</string>
 
     <!-- Failed action error message: e.g. Failed: Pause -->
     <string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 25f21f3..d30b80c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -26,36 +26,43 @@
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowShowWallpaper">true</item>
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
     </style>
 
     <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
         <item name="android:textColorSecondary">#DE000000</item>
-        <item name="allAppsScrimColor">#FFFFFFFF</item>
-        <item name="allAppsInterimScrimAlpha">46</item>
+        <item name="allAppsScrimColor">?android:attr/colorBackgroundFloating</item>
+        <item name="allappsHeaderProtectionColor">@color/popup_color_tertiary_light</item>
         <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
         <item name="allAppsTheme">@style/AllAppsTheme</item>
-        <item name="popupColorPrimary">#FFF</item>
-        <item name="popupColorSecondary">#F1F3F4</item>
-        <item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
+        <item name="popupColorPrimary">@color/popup_color_primary_light</item>
+        <item name="popupColorSecondary">@color/popup_color_secondary_light</item>
+        <item name="popupColorTertiary">@color/popup_color_tertiary_light</item>
+        <item name="popupShadeFirst">@color/popup_shade_first_light</item>
+        <item name="popupShadeSecond">@color/popup_shade_second_light</item>
+        <item name="popupShadeThird">@color/popup_shade_third_light</item>
+        <item name="popupNotificationDotColor">@color/popup_notification_dot_light</item>
         <item name="isMainColorDark">false</item>
         <item name="isWorkspaceDarkText">false</item>
-        <item name="workspaceTextColor">@android:color/white</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_light</item>
         <item name="workspaceShadowColor">#B0000000</item>
-        <item name="workspaceAmbientShadowColor">#33000000</item>
-        <item name="workspaceKeyShadowColor">#44000000</item>
+        <item name="workspaceAmbientShadowColor">#40000000</item>
+        <item name="workspaceKeyShadowColor">#89000000</item>
         <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
-        <item name="folderDotColor">?android:attr/colorPrimary</item>
-        <item name="folderFillColor">#CDFFFFFF</item>
+        <item name="folderDotColor">@color/folder_dot_color</item>
+        <item name="folderFillColor">@color/folder_background_light</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
-        <item name="folderTextColor">#FF212121</item>
-        <item name="folderHintColor">#89616161</item>
+        <item name="folderTextColor">@color/workspace_text_color_dark</item>
+        <item name="isFolderDarkText">true</item>
+        <item name="folderHintColor">@color/folder_hint_text_color_dark</item>
         <item name="loadingIconColor">#CCFFFFFF</item>
         <item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
         <item name="workProfileOverlayTextColor">#FF212121</item>
         <item name="eduHalfSheetBGColor">?android:attr/colorAccent</item>
         <item name="disabledIconAlpha">.54</item>
+        <item name="workspaceAccentColor">@color/workspace_accent_color_light</item>
+        <item name="dropTargetHoverTextColor">@color/workspace_text_color_dark</item>
+        <item name="overviewScrimColor">@color/overview_scrim</item>
 
         <item name="android:windowTranslucentStatus">false</item>
         <item name="android:windowTranslucentNavigation">false</item>
@@ -67,70 +74,67 @@
     </style>
 
     <style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
-        <item name="folderFillColor">#FF3C4043</item> <!-- 100% GM2 800 -->
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
         <item name="disabledIconAlpha">.254</item>
 
     </style>
 
     <style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
-        <item name="workspaceTextColor">#FF212121</item>
-        <item name="allAppsInterimScrimAlpha">128</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
         <item name="workspaceKeyShadowColor">@android:color/transparent</item>
         <item name="isWorkspaceDarkText">true</item>
         <item name="workspaceStatusBarScrim">@null</item>
-        <item name="folderDotColor">#FF464646</item>
-        <item name="folderFillColor">#CDFFFFFF</item>
-        <item name="folderIconBorderColor">#FF80868B</item>
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
+        <item name="workspaceAccentColor">@color/workspace_accent_color_dark</item>
+        <item name="dropTargetHoverTextColor">@color/workspace_text_color_light</item>
     </style>
 
     <style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
-        <item name="android:textColorPrimary">#FFFFFFFF</item>
-        <item name="android:textColorSecondary">#FFFFFFFF</item>
-        <item name="android:textColorTertiary">#CCFFFFFF</item>
+        <item name="android:textColorPrimary">@color/text_color_primary_dark</item>
+        <item name="android:textColorSecondary">@color/text_color_secondary_dark</item>
+        <item name="android:textColorTertiary">@color/text_color_tertiary_dark</item>
         <item name="android:textColorHint">#A0FFFFFF</item>
-        <item name="android:colorControlHighlight">#A0FFFFFF</item>
+        <item name="android:colorControlHighlight">#19FFFFFF</item>
         <item name="android:colorPrimary">#FF212121</item>
-        <item name="allAppsScrimColor">#FF000000</item>
-        <item name="allAppsInterimScrimAlpha">102</item>
+        <item name="allAppsScrimColor">?android:attr/colorBackgroundFloating</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
         <item name="allAppsTheme">@style/AllAppsTheme.Dark</item>
-        <item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
-        <item name="popupColorSecondary">#202124</item>
-        <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
+        <item name="popupColorPrimary">@color/popup_color_primary_dark</item>
+        <item name="popupColorSecondary">@color/popup_color_secondary_dark</item>
+        <item name="popupColorTertiary">@color/popup_color_tertiary_dark</item>
+        <item name="popupNotificationDotColor">@color/popup_notification_dot_dark</item>
+        <item name="popupShadeFirst">@color/popup_shade_first_dark</item>
+        <item name="popupShadeSecond">@color/popup_shade_second_dark</item>
+        <item name="popupShadeThird">@color/popup_shade_third_dark</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
-        <item name="folderDotColor">#FF464646</item>
-        <item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
-        <item name="folderIconBorderColor">#FF80868B</item>
-        <item name="folderTextColor">@android:color/white</item>
-        <item name="folderHintColor">#89CCCCCC</item>
+        <item name="folderDotColor">@color/folder_dot_color</item>
+        <item name="folderFillColor">@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>
+        <item name="folderHintColor">@color/folder_hint_text_color_light</item>
         <item name="isMainColorDark">true</item>
         <item name="loadingIconColor">#99FFFFFF</item>
         <item name="iconOnlyShortcutColor">#B3FFFFFF</item>
         <item name="workProfileOverlayTextColor">@android:color/white</item>
         <item name="eduHalfSheetBGColor">#DD000000</item>
+        <item name="overviewScrimColor">@color/overview_scrim_dark</item>
     </style>
 
     <style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
-        <item name="folderFillColor">#FF3C4043</item> <!-- 100% GM2 800 -->
-        <item name="folderTextColor">@android:color/white</item>
         <item name="disabledIconAlpha">.54</item>
     </style>
 
     <style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
-        <item name="android:colorControlHighlight">#75212121</item>
-        <item name="allAppsInterimScrimAlpha">25</item>
-        <item name="folderFillColor">#CDFFFFFF</item>
-        <item name="folderTextColor">?attr/workspaceTextColor</item>
-        <item name="workspaceTextColor">#FF212121</item>
+        <item name="android:colorControlHighlight">#19212121</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
         <item name="workspaceKeyShadowColor">@android:color/transparent</item>
         <item name="isWorkspaceDarkText">true</item>
         <item name="workspaceStatusBarScrim">@null</item>
+        <item name="workspaceAccentColor">@color/workspace_accent_color_dark</item>
+        <item name="dropTargetHoverTextColor">@color/workspace_text_color_light</item>
     </style>
 
     <!-- A derivative project can extend these themes to customize the application theme without
@@ -143,12 +147,19 @@
     <style name="AppTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark.DarkMainColor" />
     <style name="AppTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark.DarkText" />
 
-    <style name="AppItemActivityTheme" parent="@android:style/Theme.Material.Light.Dialog.Alert">
-        <item name="widgetsTheme">@style/WidgetContainerTheme</item>
+    <style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
+        <item name="android:navigationBarColor">?android:colorPrimaryDark</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="preferenceTheme">@style/HomeSettingsPreferenceTheme</item>
     </style>
 
-    <style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="android:navigationBarColor">@android:color/transparent</item>
+    <style name="HomeSettingsPreferenceTheme" parent="@style/PreferenceThemeOverlay.v14.Material">
+        <item name="preferenceFragmentCompatStyle">@style/HomeSettingsFragmentCompatStyle</item>
+    </style>
+
+    <style name="HomeSettingsFragmentCompatStyle" parent="@style/PreferenceFragment.Material">
+        <item name="android:layout">@layout/home_settings</item>
     </style>
 
     <!--
@@ -162,14 +173,16 @@
         <item name="android:textColorSecondary">?attr/workspaceTextColor</item>
     </style>
 
-    <!-- Theme for the widget container. Overridden on API 26. -->
+    <!-- Theme for the widget container. -->
     <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="android:colorEdgeEffect">?android:attr/textColorSecondaryInverse</item>
-        <item name="android:textColorPrimary">?android:attr/textColorPrimaryInverse</item>
-        <item name="android:textColorSecondary">?android:attr/textColorSecondaryInverse</item>
+        <item name="android:colorPrimaryDark">#E8EAED</item>
+        <item name="android:textColorSecondary">?android:attr/textColorPrimary</item>
+        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
     </style>
-
-    <style name="WidgetContainerTheme.Dark" />
+    <style name="WidgetContainerTheme.Dark" parent="AppTheme.Dark">
+        <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+        <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
+    </style>
 
     <style name="FastScrollerPopup" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
         <item name="android:layout_width">wrap_content</item>
@@ -181,7 +194,7 @@
         <item name="android:alpha">0</item>
         <item name="android:elevation">3dp</item>
         <item name="android:saveEnabled">false</item>
-        <item name="android:textColor">@android:color/white</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
         <item name="android:includeFontPadding">false</item>
         <item name="android:importantForAccessibility">no</item>
     </style>
@@ -194,8 +207,9 @@
         <item name="disabledIconAlpha">.54</item>
     </style>
 
+    <style name="BaseIconRoot" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
 
-    <style name="BaseIconUnBounded" parent="@android:style/TextAppearance.DeviceDefault">
+    <style name="BaseIconUnBounded" parent="BaseIconRoot">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center</item>
@@ -212,15 +226,30 @@
         <item name="android:lines">1</item>
     </style>
 
+    <!-- Base theme for AllApps BubbleTextViews -->
+    <style name="BaseIcon.AllApps" parent="BaseIcon">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:stateListAnimator">@animator/all_apps_fastscroll_icon_anim</item>
+        <item name="android:paddingLeft">@dimen/dynamic_grid_cell_padding_x</item>
+        <item name="android:paddingRight">@dimen/dynamic_grid_cell_padding_x</item>
+    </style>
+
+
     <!-- Icon displayed on the workspace -->
-    <style name="BaseIcon.Workspace" >
+    <style name="BaseIcon.Workspace.Shadows" parent="BaseIcon">
         <item name="android:shadowRadius">2.0</item>
         <item name="android:shadowColor">?attr/workspaceShadowColor</item>
         <item name="ambientShadowColor">?attr/workspaceAmbientShadowColor</item>
-        <item name="ambientShadowBlur">2.5dp</item>
+        <item name="ambientShadowBlur">1.5dp</item>
         <item name="keyShadowColor">?attr/workspaceKeyShadowColor</item>
-        <item name="keyShadowBlur">1dp</item>
-        <item name="keyShadowOffset">.5dp</item>
+        <item name="keyShadowBlur">.5dp</item>
+        <item name="keyShadowOffsetX">.5dp</item>
+        <item name="keyShadowOffsetY">.5dp</item>
+    </style>
+
+    <!-- Intentionally empty so we can override -->
+    <style name="BaseIcon.Workspace" parent="BaseIcon.Workspace.Shadows">
     </style>
 
     <!-- Theme for the popup container -->
@@ -230,34 +259,57 @@
 
     <!-- Drop targets -->
     <style name="DropTargetButtonBase" parent="@android:style/TextAppearance.DeviceDefault">
-        <item name="android:drawablePadding">7.5dp</item>
-        <item name="android:paddingLeft">16dp</item>
-        <item name="android:paddingRight">16dp</item>
-        <item name="android:textColor">?attr/workspaceTextColor</item>
+        <item name="android:drawablePadding">8dp</item>
+        <item name="android:padding">14dp</item>
+        <item name="android:textColor">@color/drop_target_text</item>
         <item name="android:textSize">@dimen/drop_target_text_size</item>
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">end</item>
-        <item name="android:shadowColor">?attr/workspaceShadowColor</item>
-        <item name="android:shadowDx">0.0</item>
-        <item name="android:shadowDy">1.0</item>
-        <item name="android:shadowRadius">4.0</item>
+        <item name="android:background">@drawable/drop_target_background</item>
     </style>
 
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
     <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
+
     <style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
 
     <style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
 
     <style name="AllAppsEmptySearchBackground">
         <item name="android:colorPrimary">#E0E0E0</item>
-        <item name="android:colorControlHighlight">#BDBDBD</item>
+        <item name="android:colorControlHighlight">#19BDBDBD</item>
         <item name="android:colorForeground">@color/all_apps_bg_hand_fill</item>
     </style>
     <style name="AllAppsEmptySearchBackground.Dark">
         <item name="android:colorPrimary">#9AA0A6</item>
-        <item name="android:colorControlHighlight">#DFE1E5</item>
+        <item name="android:colorControlHighlight">#19DFE1E5</item>
         <item name="android:colorForeground">@color/all_apps_bg_hand_fill_dark</item>
     </style>
+
+    <style name="Button.TopRounded.Bordered" parent="@android:style/Widget.Material.Button">
+        <item name="android:background">@drawable/button_top_rounded_bordered_ripple</item>
+        <item name="android:stateListAnimator">@null</item>
+    </style>
+
+    <style name="Button.BottomRounded.Colored" parent="@android:style/Widget.Material.Button">
+        <item name="android:background">@drawable/button_bottom_rounded_colored_ripple</item>
+        <item name="android:stateListAnimator">@null</item>
+    </style>
+
+    <style name="Button.Rounded.Colored" parent="@android:style/Widget.Material.Button">
+        <item name="android:background">@drawable/button_rounded_colored_ripple</item>
+        <item name="android:stateListAnimator">@null</item>
+    </style>
+
+    <style name="Button.FullRounded.Colored" parent="@android:style/Widget.Material.Button">
+        <item name="android:background">@drawable/full_rounded_colored_ripple</item>
+        <item name="android:stateListAnimator">@null</item>
+    </style>
+
+    <style name="AddItemActivityTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
+        <item name="widgetsTheme">@style/WidgetContainerTheme</item>
+        <item name="android:windowLightStatusBar">true</item>
+        <item name="android:windowTranslucentStatus">true</item>
+    </style>
 </resources>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 1c99dfc..256999c 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -116,6 +116,14 @@
             launcher:canBeDefault="true" />
 
         <display-option
+            launcher:name="Large Phone Split Display"
+            launcher:minWidthDps="406"
+            launcher:minHeightDps="694"
+            launcher:iconImageSize="56"
+            launcher:iconTextSize="14.4"
+            launcher:canBeDefault="split_display" />
+
+        <display-option
             launcher:name="Shorter Stubby"
             launcher:minWidthDps="255"
             launcher:minHeightDps="400"
diff --git a/res/xml/dynamic_resources.xml b/res/xml/dynamic_resources.xml
index f5d2628..3a3e239 100644
--- a/res/xml/dynamic_resources.xml
+++ b/res/xml/dynamic_resources.xml
@@ -4,6 +4,6 @@
     <entry id="@color/delete_target_hover_tint" />
     <entry id="@color/delete_target_hover_tint" />
     <entry id="@color/delete_target_hover_tint" />
+    <entry id="@color/wallpaper_scrim_color" />
 
-
-</DynamicResources>
\ No newline at end of file
+</DynamicResources>
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 3455cb8..90de498 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -15,40 +15,40 @@
 -->
 
 <androidx.preference.PreferenceScreen
-    xmlns:android="http://schemas.android.com/apk/res/android">
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto">
 
     <com.android.launcher3.settings.NotificationDotsPreference
         android:key="pref_icon_badging"
         android:title="@string/notification_dots_title"
         android:persistent="false"
-        android:widgetLayout="@layout/notification_pref_warning" >
-        <intent android:action="android.settings.NOTIFICATION_SETTINGS">
-            <!-- This extra highlights the "Allow notification dots" field in Notification settings -->
-            <extra
-                android:name=":settings:fragment_args_key"
-                android:value="notification_badging" />
-        </intent>
-    </com.android.launcher3.settings.NotificationDotsPreference>
+        android:widgetLayout="@layout/notification_pref_warning" />
 
+    <!--
+      LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED(613)
+      LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_DISABLED(614)
+    -->
     <SwitchPreference
         android:key="pref_add_icon_to_home"
         android:title="@string/auto_add_shortcuts_label"
         android:summary="@string/auto_add_shortcuts_description"
         android:defaultValue="true"
-        android:persistent="true" />
+        android:persistent="true"
+        launcher:logIdOn="613"
+        launcher:logIdOff="614" />
 
+    <!--
+      LAUNCHER_HOME_SCREEN_ROTATION_ENABLED(615)
+      LAUNCHER_HOME_SCREEN_ROTATION_DISABLED(616)
+    -->
     <SwitchPreference
         android:key="pref_allowRotation"
         android:title="@string/allow_rotation_title"
         android:summary="@string/allow_rotation_desc"
-        android:defaultValue="@bool/allow_rotation"
-        android:persistent="true" />
-
-    <SwitchPreference
-        android:key="pref_grid_options"
-        android:title="Enable grid options"
         android:defaultValue="false"
-        android:persistent="true" />
+        android:persistent="true"
+        launcher:logIdOn="615"
+        launcher:logIdOff="616" />
 
     <androidx.preference.PreferenceScreen
         android:key="pref_developer_options"
diff --git a/res/xml/size_limits_80x104.xml b/res/xml/size_limits_80x104.xml
new file mode 100644
index 0000000..4178664
--- /dev/null
+++ b/res/xml/size_limits_80x104.xml
@@ -0,0 +1,114 @@
+<?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/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
new file mode 100644
index 0000000..9ed26ff
--- /dev/null
+++ b/robolectric_tests/Android.bp
@@ -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.
+
+//
+// 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/Android.mk b/robolectric_tests/Android.mk
deleted file mode 100644
index 79c26c3..0000000
--- a/robolectric_tests/Android.mk
+++ /dev/null
@@ -1,68 +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.
-
-#############################################
-# Launcher Robolectric test target.         #
-#############################################
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := LauncherRoboTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-LOCAL_SDK_VERSION := system_current
-LOCAL_SRC_FILES := \
-	$(call all-java-files-under, src) \
-	$(call all-java-files-under, ../tests/src_common)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.runner \
-    androidx.test.rules \
-    mockito-robolectric-prebuilt \
-    truth-prebuilt
-LOCAL_JAVA_LIBRARIES := \
-    platform-robolectric-4.3.1-prebuilt
-
-LOCAL_JAVA_RESOURCE_DIRS := resources config
-
-LOCAL_INSTRUMENTATION_FOR := Launcher3
-LOCAL_MODULE_TAGS := optional
-
-# Generate test_config.properties
-include external/robolectric-shadows/gen_test_config.mk
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-############################################
-# Target to run the previous target.       #
-############################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := RunLauncherRoboTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-LOCAL_SDK_VERSION := system_current
-LOCAL_JAVA_LIBRARIES := LauncherRoboTests
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_TEST_PACKAGE := Launcher3
-LOCAL_INSTRUMENT_SOURCE_DIRS := packages/apps/Launcher3/src
-
-LOCAL_ROBOTEST_TIMEOUT := 36000
-
-include prebuilts/misc/common/robolectric/4.3.1/run_robotests.mk
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/resources/robolectric.properties
similarity index 69%
rename from robolectric_tests/config/robolectric.properties
rename to robolectric_tests/resources/robolectric.properties
index a8e0cb3..abb6968 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/resources/robolectric.properties
@@ -1,19 +1,15 @@
-sdk=29
+sdk=30
+
 shadows= \
-    com.android.launcher3.shadows.LShadowApplicationPackageManager \
     com.android.launcher3.shadows.LShadowAppPredictionManager \
     com.android.launcher3.shadows.LShadowAppWidgetManager \
     com.android.launcher3.shadows.LShadowBackupManager \
-    com.android.launcher3.shadows.LShadowBitmap \
     com.android.launcher3.shadows.LShadowDisplay \
     com.android.launcher3.shadows.LShadowLauncherApps \
-    com.android.launcher3.shadows.LShadowTypeface \
-    com.android.launcher3.shadows.LShadowUserManager \
-    com.android.launcher3.shadows.LShadowWallpaperManager \
     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
\ No newline at end of file
+application=com.android.launcher3.util.LauncherTestApplication
diff --git a/robolectric_tests/resources/widgets_predication_update_task_data.txt b/robolectric_tests/resources/widgets_predication_update_task_data.txt
new file mode 100644
index 0000000..941d195
--- /dev/null
+++ b/robolectric_tests/resources/widgets_predication_update_task_data.txt
@@ -0,0 +1,24 @@
+# Model data used by WidgetsPredictionUpdateTasksTest
+
+classMap s com.android.launcher3.model.data.WorkspaceItemInfo
+classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
+
+# Items for the BgDataModel
+
+# App shortcuts
+bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
+bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
+bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
+bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
+
+# Promise icons for app3
+bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
+bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
+bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
+
+# Promise icon for app4
+bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
+
+# Widget
+bgItem w providerName=app4/provider1 id=9
+bgItem w providerName=app5/provider1 id=10
\ No newline at end of file
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index b7ba106..2a94d9b 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -25,16 +25,20 @@
 
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.Executors;
 
 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)
 public final class FolderNameProviderTest {
     private Context mContext;
     private WorkspaceItemInfo mItem1;
@@ -58,18 +62,20 @@
     }
 
     @Test
-    public void getSuggestedFolderName_workAssignedToEnd() {
+    public void getSuggestedFolderName_workAssignedToEnd() throws Exception {
         ArrayList<WorkspaceItemInfo> list = new ArrayList<>();
         list.add(mItem1);
         list.add(mItem2);
         FolderNameInfos nameInfos = new FolderNameInfos();
-        new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+        Executors.MODEL_EXECUTOR.submit(() ->
+                new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos)).get();
         assertEquals("Work", nameInfos.getLabels()[0]);
 
         nameInfos.setLabel(0, "candidate1", 1.0f);
         nameInfos.setLabel(1, "candidate2", 1.0f);
         nameInfos.setLabel(2, "candidate3", 1.0f);
-        new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+        Executors.MODEL_EXECUTOR.submit(() ->
+                new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos)).get();
         assertEquals("Work", nameInfos.getLabels()[3]);
         assertTrue(nameInfos.hasSuggestions());
         assertTrue(nameInfos.hasPrimary());
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
index c892618..01b23ba 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -9,8 +9,8 @@
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.util.Scheduler;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
 
 import java.io.File;
 import java.io.PrintWriter;
@@ -21,11 +21,10 @@
  * Tests for {@link FileLog}
  */
 @RunWith(RobolectricTestRunner.class)
+@LooperMode(Mode.PAUSED)
 public class FileLogTest {
 
     private File mTempDir;
-    private boolean mTestActive;
-
     @Before
     public void setUp() {
         int count = 0;
@@ -35,14 +34,6 @@
         } while (!mTempDir.mkdir());
 
         FileLog.setDir(mTempDir);
-
-        mTestActive = true;
-        Scheduler scheduler = Shadows.shadowOf(FileLog.getHandler().getLooper()).getScheduler();
-        new Thread(() -> {
-            while (mTestActive) {
-                scheduler.advanceToLastPostedRunnable();
-            }
-        }).start();
     }
 
     @After
@@ -52,8 +43,6 @@
             new File(mTempDir, "log-" + i).delete();
         }
         mTempDir.delete();
-
-        mTestActive = false;
     }
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 90313ab..34a8025 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -35,13 +35,13 @@
 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 com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.shadows.LShadowBackupManager;
-import com.android.launcher3.shadows.LShadowUserManager;
 import com.android.launcher3.util.LauncherModelHelper;
 
 import org.junit.Before;
@@ -51,6 +51,7 @@
 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.
@@ -64,17 +65,11 @@
     private static final long OLD_WORK_PROFILE_ID = 11;
     private static final int WORK_PROFILE_ID = 10;
 
-    private static final int SYSTEM_USER = 0;
-    private static final int FLAG_SYSTEM = 0x00000800;
-    private static final int FLAG_PROFILE = 0x00001000;
-
-    private LShadowUserManager mUserManager;
+    private ShadowUserManager mUserManager;
     private BackupManager mBackupManager;
     private LauncherModelHelper mModelHelper;
     private SQLiteDatabase mDb;
     private InvariantDeviceProfile mIdp;
-    private UserHandle mMainProfileUser;
-    private UserHandle mWorkProfileUser;
 
     @Before
     public void setUp() {
@@ -90,17 +85,15 @@
         final UserManager userManager = RuntimeEnvironment.application.getSystemService(
                 UserManager.class);
         mUserManager = Shadow.extract(userManager);
-        // sign in to primary user
-        mMainProfileUser = mUserManager.addUser(SYSTEM_USER, "me", FLAG_SYSTEM);
         // sign in to work profile
-        mWorkProfileUser = mUserManager.addUser(WORK_PROFILE_ID, "work", FLAG_PROFILE);
+        mUserManager.addUser(WORK_PROFILE_ID, "work", ShadowUserManager.FLAG_MANAGED_PROFILE);
     }
 
     private void setupBackupManager() {
         mBackupManager = new BackupManager(RuntimeEnvironment.application);
         final LShadowBackupManager bm = Shadow.extract(mBackupManager);
-        bm.addProfile(MY_OLD_PROFILE_ID, mMainProfileUser);
-        bm.addProfile(OLD_WORK_PROFILE_ID, mWorkProfileUser);
+        bm.addProfile(MY_OLD_PROFILE_ID, Process.myUserHandle());
+        bm.addProfile(OLD_WORK_PROFILE_ID, UserHandle.of(WORK_PROFILE_ID));
     }
 
     @Test
@@ -134,7 +127,7 @@
                 { APP_ICON, SHORTCUT, SHORTCUT, NO__ICON},
             }}, 2, OLD_WORK_PROFILE_ID);
         // simulates the creation of backup upon restore
-        new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numHotseatIcons,
+        new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numDatabaseHotseatIcons,
                 mIdp.numColumns, mIdp.numRows).doBackup(
                         MY_OLD_PROFILE_ID, GridBackupTable.OPTION_REQUIRES_SANITIZATION);
         // reset favorites table
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 5610b0e..9ac3fe7 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -53,10 +53,10 @@
         mModelHelper = new LauncherModelHelper();
         mModelHelper.initializeData("/cache_data_updated_task_data.txt");
 
-        // Add dummy entries in the cache to simulate update
+        // Add placeholder entries in the cache to simulate update
         Context context = RuntimeEnvironment.application;
         IconCache iconCache = LauncherAppState.getInstance(context).getIconCache();
-        CachingLogic<ItemInfo> dummyLogic = new CachingLogic<ItemInfo>() {
+        CachingLogic<ItemInfo> placeholderLogic = new CachingLogic<ItemInfo>() {
             @Override
             public ComponentName getComponent(ItemInfo info) {
                 return info.getTargetComponent();
@@ -81,7 +81,7 @@
 
         UserManager um = context.getSystemService(UserManager.class);
         for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
-            iconCache.addIconToDBAndMemCache(info, dummyLogic, new PackageInfo(),
+            iconCache.addIconToDBAndMemCache(info, placeholderLogic, new PackageInfo(),
                     um.getSerialNumberForUser(info.user), true);
         }
     }
diff --git a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index bbbe21e..be03c7d 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -165,7 +165,7 @@
             @Override
             public void onOpen(SQLiteDatabase db) { }
         };
-        // Insert dummy data
+        // Insert mock data
         for (int i = 0; i < 10; i++) {
             ContentValues values = new ContentValues();
             values.put(Favorites._ID, i);
diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 8f3a83e..655237d 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -109,7 +109,7 @@
     public void testCustomProfileLoaded_with_widget() throws Exception {
         String pendingAppPkg = "com.test.pending";
 
-        // Add a dummy session info so that the widget exists
+        // Add a placeholder session info so that the widget exists
         SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
         params.setAppPackageName(pendingAppPkg);
 
@@ -120,7 +120,7 @@
         setField(sessionInfo, "appIcon", BitmapInfo.LOW_RES_ICON);
 
         writeLayoutAndLoad(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
-                .putWidget(pendingAppPkg, "DummyWidget", 2, 2));
+                .putWidget(pendingAppPkg, "PlaceholderWidget", 2, 2));
 
         // Verify widget
         assertEquals(1, mModelHelper.getBgDataModel().appWidgets.size());
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index 8e00dcb..d544a0b 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -64,7 +64,7 @@
                 mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0),
         };
 
-        mIdp.numHotseatIcons = 3;
+        mIdp.numDatabaseHotseatIcons = 3;
         new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
                 .migrateHotseat();
         // First item is dropped as it has the least weight.
@@ -81,7 +81,7 @@
                 mModelHelper.addItem(10, 4, HOTSEAT, 0, 0),
         };
 
-        mIdp.numHotseatIcons = 3;
+        mIdp.numDatabaseHotseatIcons = 3;
         new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
                 .migrateHotseat();
         // First item is dropped as it has the least weight.
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
index cca333c..8e49fae 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
@@ -120,7 +120,7 @@
         };
         mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7);
 
-        mIdp.numHotseatIcons = 4;
+        mIdp.numDatabaseHotseatIcons = 4;
         mIdp.numColumns = 4;
         mIdp.numRows = 4;
         GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
@@ -128,16 +128,16 @@
                 srcHotseatItems.length);
         GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
                 LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
-                mIdp.numHotseatIcons);
+                mIdp.numDatabaseHotseatIcons);
         GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
-                destReader, mIdp.numHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
         task.migrate();
 
         // 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);
-        assertEquals(c.getCount(), mIdp.numHotseatIcons);
+        assertEquals(c.getCount(), mIdp.numDatabaseHotseatIcons);
         int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
         int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
         c.moveToNext();
@@ -186,5 +186,101 @@
         assertTrue(c.getString(intentIndex).contains(testPackage8));
         assertEquals(c.getInt(cellXIndex), 0);
         assertEquals(c.getInt(cellYIndex), 2);
+
+        c.close();
+    }
+
+    @Test
+    public void migrateToLargerHotseat() {
+        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),
+                mModelHelper.addItem(APP_ICON, 2, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+                mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
+        };
+
+        int numSrcDatabaseHotseatIcons = srcHotseatItems.length;
+        mIdp.numDatabaseHotseatIcons = 6;
+        mIdp.numColumns = 4;
+        mIdp.numRows = 4;
+        GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages,
+                numSrcDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
+                mIdp.numDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+        task.migrate();
+
+        // 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);
+        assertEquals(c.getCount(), numSrcDatabaseHotseatIcons);
+        int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
+        int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
+        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();
+        assertEquals(c.getInt(screenIndex), 3);
+        assertTrue(c.getString(intentIndex).contains(testPackage4));
+
+        c.close();
+    }
+
+    @Test
+    public void migrateFromLargerHotseat() {
+        int[] srcHotseatItems = {
+                mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+                -1,
+                mModelHelper.addItem(SHORTCUT, 2, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+                mModelHelper.addItem(APP_ICON, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+                mModelHelper.addItem(SHORTCUT, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
+                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);
+        GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+                LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages,
+                mIdp.numDatabaseHotseatIcons);
+        GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
+                destReader, mIdp.numDatabaseHotseatIcons, new Point(mIdp.numColumns, mIdp.numRows));
+        task.migrate();
+
+        // 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);
+        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), 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();
+        assertEquals(c.getInt(screenIndex), 3);
+        assertTrue(c.getString(intentIndex).contains(testPackage4));
+
+        c.close();
     }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 3a252dc..800311a 100644
--- a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -51,7 +51,7 @@
 
 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.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.Executors;
@@ -92,9 +92,9 @@
                 SCREEN, CELLX, CELLY, RESTORED, INTENT
         });
 
-        mLoaderCursor = new LoaderCursor(mCursor, LauncherSettings.Favorites.CONTENT_URI, mApp,
-                new UserManagerState());
-        mLoaderCursor.allUsers.put(0, Process.myUserHandle());
+        UserManagerState ums = new UserManagerState();
+        mLoaderCursor = new LoaderCursor(mCursor, Favorites.CONTENT_URI, mApp, ums);
+        ums.allUsers.put(0, Process.myUserHandle());
     }
 
     private void initCursor(int itemType, String title) {
@@ -110,7 +110,7 @@
     public void getAppShortcutInfo_dontAllowMissing_invalidComponent() {
         initCursor(ITEM_TYPE_APPLICATION, "");
         assertTrue(mLoaderCursor.moveToNext());
-        ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+        ComponentName cn = new ComponentName(mContext.getPackageName(), "placeholder-do");
         assertNull(mLoaderCursor.getAppShortcutInfo(
                 new Intent().setComponent(cn), false /* allowMissingTarget */, true));
     }
@@ -136,7 +136,7 @@
         initCursor(ITEM_TYPE_APPLICATION, "");
         assertTrue(mLoaderCursor.moveToNext());
 
-        ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+        ComponentName cn = new ComponentName(mContext.getPackageName(), "placeholder-do");
         WorkspaceItemInfo info = Executors.MODEL_EXECUTOR.submit(() ->
                 mLoaderCursor.getAppShortcutInfo(
                         new Intent().setComponent(cn), true  /* allowMissingTarget */, true))
@@ -160,7 +160,7 @@
     public void checkItemPlacement_outsideBounds() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Item outside screen bounds are not placed
         assertFalse(mLoaderCursor.checkItemPlacement(
@@ -171,7 +171,7 @@
     public void checkItemPlacement_overlappingItems() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Overlapping mItems are not placed
         assertTrue(mLoaderCursor.checkItemPlacement(
@@ -197,7 +197,7 @@
     public void checkItemPlacement_hotseat() {
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
+        mIDP.numDatabaseHotseatIcons = 3;
 
         // Hotseat mItems are only placed based on screenId
         assertTrue(mLoaderCursor.checkItemPlacement(
diff --git a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
index 87fe3c0..a2abfd5 100644
--- a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3.model;
 
-import static com.android.launcher3.util.Executors.createAndStartNewForegroundLooper;
+import static com.android.launcher3.util.Executors.createAndStartNewLooper;
 import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
 
 import static org.junit.Assert.assertEquals;
@@ -30,6 +30,7 @@
 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;
@@ -43,8 +44,8 @@
 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 org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -73,8 +74,9 @@
 
         // 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(createAndStartNewForegroundLooper("tempMain"));
-        ReflectionHelpers.setField(mModelHelper.getModel(), "mMainExecutor", mTempMainExecutor);
+        mTempMainExecutor = new LooperExecutor(createAndStartNewLooper("tempMain"));
+        ShadowLooperExecutor sle = Shadow.extract(Executors.MAIN_EXECUTOR);
+        sle.setHandler(mTempMainExecutor.getHandler());
     }
 
     @Test
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index e43df21..412ace0 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -67,7 +67,7 @@
         for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
             if (info instanceof WorkspaceItemInfo) {
                 assertEquals(updates.contains(info.id) ? progress: 0,
-                        ((WorkspaceItemInfo) info).getInstallProgress());
+                        ((WorkspaceItemInfo) info).getProgressLevel());
             } else {
                 assertEquals(updates.contains(info.id) ? progress: -1,
                         ((LauncherAppWidgetInfo) info).installProgress);
diff --git a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index ee73b82..4184d33 100644
--- a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -44,7 +44,7 @@
     @Test
     public void testMigrateProfileId() throws Exception {
         SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
-        // Add some dummy data
+        // Add some mock data
         for (int i = 0; i < 5; i++) {
             ContentValues values = new ContentValues();
             values.put(Favorites._ID, i);
@@ -64,7 +64,7 @@
     @Test
     public void testChangeDefaultColumn() throws Exception {
         SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
-        // Add some dummy data
+        // Add some mock data
         for (int i = 0; i < 5; i++) {
             ContentValues values = new ContentValues();
             values.put(Favorites._ID, i);
diff --git a/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java b/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
index 0ca5ce6..e3694ae 100644
--- a/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
+++ b/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
@@ -21,7 +21,6 @@
 
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
 
 import android.content.Context;
 import android.os.UserManager;
@@ -30,8 +29,6 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.allapps.AllAppsPagedView;
 import com.android.launcher3.allapps.AllAppsRecyclerView;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.shadows.ShadowOverrides;
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.LauncherModelHelper;
 
@@ -62,20 +59,15 @@
     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);
-        ShadowOverrides.setProvider(UserEventDispatcher.class,
-                c -> mock(UserEventDispatcher.class));
         Settings.Global.putFloat(mTargetContext.getContentResolver(),
                 Settings.Global.WINDOW_ANIMATION_SCALE, 0);
 
         mModelHelper.installApp(TEST_PACKAGE);
-        mLayoutBuilder = new LauncherLayoutBuilder();
     }
 
     @Test
@@ -91,7 +83,7 @@
     public void testAllAppsList_workProfile() throws Exception {
         ShadowUserManager sum = Shadow.extract(mTargetContext.getSystemService(UserManager.class));
         sum.addUser(SYSTEM_USER, "me", FLAG_SYSTEM);
-        sum.addUser(WORK_PROFILE_ID, "work", FLAG_PROFILE);
+        sum.addProfile(SYSTEM_USER, WORK_PROFILE_ID, "work", FLAG_PROFILE);
 
         SecondaryDisplayLauncher launcher = loadLauncher();
         launcher.showAppDrawer(true);
diff --git a/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java b/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java
new file mode 100644
index 0000000..3271812
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.settings;
+
+import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem;
+import static androidx.test.espresso.intent.Intents.intended;
+import static androidx.test.espresso.intent.matcher.BundleMatchers.hasEntry;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT;
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARGS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.equalTo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.espresso.intent.Intents;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.launcher3.R;
+import com.android.systemui.shared.plugins.PluginPrefs;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SettingsActivityTest {
+
+    private Context mApplicationContext;
+
+    @Before
+    public void setUp() {
+        mApplicationContext = ApplicationProvider.getApplicationContext();
+        Intents.init();
+    }
+
+    @After
+    public void tearDown() {
+        Intents.release();
+    }
+
+    @Test
+    public void testSettings_aboutTap_launchesActivity() {
+        ActivityScenario.launch(SettingsActivity.class);
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItem(hasDescendant(withText("About")), click()));
+
+        intended(allOf(
+                hasComponent(SettingsActivity.class.getName()),
+                hasExtra(
+                        equalTo(EXTRA_FRAGMENT_ARGS),
+                        hasEntry(ARG_PREFERENCE_ROOT, "about_screen"))));
+    }
+
+    @Test
+    public void testSettings_developerOptionsTap_launchesActivityWithFragment() {
+        PluginPrefs.setHasPlugins(mApplicationContext);
+        ActivityScenario.launch(SettingsActivity.class);
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItem(hasDescendant(withText("Developer Options")), click()));
+
+        intended(allOf(
+                hasComponent(SettingsActivity.class.getName()),
+                hasExtra(EXTRA_FRAGMENT, DeveloperOptionsFragment.class.getName())));
+    }
+
+    @Test
+    public void testSettings_aboutScreenIntent() {
+        Bundle fragmentArgs = new Bundle();
+        fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");
+
+        Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
+                .putExtra(EXTRA_FRAGMENT_ARGS, fragmentArgs);
+        ActivityScenario.launch(intent);
+
+        onView(withText("About")).check(matches(isDisplayed()));
+        onView(withText("Version")).check(matches(isDisplayed()));
+        onView(withContentDescription("Navigate up")).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testSettings_developerOptionsFragmentIntent() {
+        Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
+                .putExtra(EXTRA_FRAGMENT, DeveloperOptionsFragment.class.getName());
+        ActivityScenario.launch(intent);
+
+        onView(withText("Developer Options")).check(matches(isDisplayed()));
+        onView(withId(R.id.filter_box)).check(matches(isDisplayed()));
+        onView(withContentDescription("Navigate up")).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testSettings_intentWithUnknownFragment() {
+        String fragmentClass = PreferenceFragmentCompat.class.getName();
+        Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
+                .putExtra(EXTRA_FRAGMENT, fragmentClass);
+
+        try {
+            ActivityScenario.launch(intent);
+            Assert.fail("Should have thrown an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage()).contains(fragmentClass);
+        }
+    }
+
+    @Test
+    public void testSettings_backButtonFinishesActivity() {
+        Bundle fragmentArgs = new Bundle();
+        fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");
+        Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
+                .putExtra(EXTRA_FRAGMENT_ARGS, fragmentArgs);
+        ActivityScenario<SettingsActivity> scenario = ActivityScenario.launch(intent);
+
+        onView(withContentDescription("Navigate up")).perform(click());
+        scenario.onActivity(activity -> assertThat(activity.isFinishing()).isTrue());
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java
deleted file mode 100644
index da8b919..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java
+++ /dev/null
@@ -1,36 +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.os.Process;
-import android.os.UserHandle;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowApplicationPackageManager;
-
-/**
- * Shadow for {@link ShadowApplicationPackageManager} which create mock predictors
- */
-@Implements(className = "android.app.ApplicationPackageManager")
-public class LShadowApplicationPackageManager extends ShadowApplicationPackageManager {
-
-    @Implementation
-    public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) {
-        return Process.myUserHandle().equals(user) ? label : "Work " + label;
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java
deleted file mode 100644
index abd90bb..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java
+++ /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.
- */
-
-package com.android.launcher3.shadows;
-
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowBitmap;
-
-/**
- * Extension of {@link ShadowBitmap} with missing shadow methods
- */
-@Implements(value = Bitmap.class)
-public class LShadowBitmap extends ShadowBitmap {
-
-    @Implementation
-    protected Bitmap extractAlpha(Paint paint, int[] offsetXY) {
-        return extractAlpha();
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java
deleted file mode 100644
index 0e7c1de..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.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 android.graphics.Typeface;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowTypeface;
-
-/**
- * Extension of {@link ShadowTypeface} with missing shadow methods
- */
-@Implements(Typeface.class)
-public class LShadowTypeface extends ShadowTypeface {
-
-    @Implementation
-    public static Typeface create(Typeface family, int weight, boolean italic) {
-        int style = italic ? Typeface.ITALIC : Typeface.NORMAL;
-        if (weight >= 400) {
-            style |= Typeface.BOLD;
-        }
-        return create(family, style);
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
deleted file mode 100644
index edf8edb..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
+++ /dev/null
@@ -1,53 +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.os.UserHandle;
-import android.os.UserManager;
-import android.util.SparseBooleanArray;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowUserManager;
-
-/**
- * Extension of {@link ShadowUserManager} with missing shadow methods
- */
-@Implements(value = UserManager.class)
-public class LShadowUserManager extends ShadowUserManager {
-
-    private final SparseBooleanArray mQuietUsers = new SparseBooleanArray();
-    private final SparseBooleanArray mLockedUsers = new SparseBooleanArray();
-
-    @Implementation
-    protected boolean isQuietModeEnabled(UserHandle userHandle) {
-        return mQuietUsers.get(userHandle.hashCode());
-    }
-
-    public void setQuietModeEnabled(UserHandle userHandle, boolean enabled) {
-        mQuietUsers.put(userHandle.hashCode(), enabled);
-    }
-
-    @Implementation
-    protected boolean isUserUnlocked(UserHandle userHandle) {
-        return !mLockedUsers.get(userHandle.hashCode());
-    }
-
-    public void setUserLocked(UserHandle userHandle, boolean enabled) {
-        mLockedUsers.put(userHandle.hashCode(), enabled);
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java
deleted file mode 100644
index d60251c..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java
+++ /dev/null
@@ -1,56 +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.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowApplication;
-import org.robolectric.shadows.ShadowUserManager;
-import org.robolectric.shadows.ShadowWallpaperManager;
-
-/**
- * Extension of {@link ShadowUserManager} with missing shadow methods
- */
-@Implements(WallpaperManager.class)
-public class LShadowWallpaperManager extends ShadowWallpaperManager {
-
-    @Implementation
-    protected static WallpaperManager getInstance(Context context) {
-        return context.getSystemService(WallpaperManager.class);
-    }
-
-    /**
-     * Remove this once the fix for
-     * https://github.com/robolectric/robolectric/issues/5285
-     * is available
-     */
-    public static void initializeMock() {
-        WallpaperManager wm = mock(WallpaperManager.class);
-        ShadowApplication shadowApplication = Shadows.shadowOf(RuntimeEnvironment.application);
-        shadowApplication.setSystemService(Context.WALLPAPER_SERVICE, wm);
-        doReturn(0).when(wm).getDesiredMinimumWidth();
-        doReturn(0).when(wm).getDesiredMinimumHeight();
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
index 344f532..b58e4b7 100644
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
+++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
@@ -18,11 +18,15 @@
 
 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.
@@ -30,6 +34,9 @@
 @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
      */
@@ -40,4 +47,16 @@
     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
index a3b7dc7..57eda7e 100644
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
+++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
@@ -23,6 +23,8 @@
 
 import android.os.Handler;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.util.LooperExecutor;
 
 import org.robolectric.annotation.Implementation;
@@ -37,8 +39,13 @@
 
     @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()) {
@@ -49,4 +56,8 @@
         }
         return directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
     }
+
+    public void setHandler(@Nullable Handler handler) {
+        mOverriddenHandler = handler;
+    }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java b/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java
new file mode 100644
index 0000000..17d0ac1
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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
index d330d10..ea75548 100644
--- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
+++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
@@ -20,7 +20,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.mockito.Mockito.mock;
 
 import android.content.Context;
 import android.os.SystemClock;
@@ -36,12 +35,10 @@
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.FolderPagedView;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.shadows.ShadowOverrides;
 import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder;
 import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -57,6 +54,7 @@
  */
 @RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
+@org.junit.Ignore
 public class LauncherUIScrollTest {
 
     private Context mTargetContext;
@@ -70,8 +68,6 @@
         mModelHelper = new LauncherModelHelper();
         mTargetContext = RuntimeEnvironment.application;
         mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
-        ShadowOverrides.setProvider(UserEventDispatcher.class,
-                c -> mock(UserEventDispatcher.class));
 
         Settings.Global.putFloat(mTargetContext.getContentResolver(),
                 Settings.Global.WINDOW_ANIMATION_SCALE, 0);
@@ -165,7 +161,7 @@
 
     private static MotionEvent createScrollEvent(int scroll) {
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE
-                .get(RuntimeEnvironment.application).portraitProfile;
+                .get(RuntimeEnvironment.application).supportedProfiles.get(0);
 
         final PointerProperties[] pointerProperties = new PointerProperties[1];
         pointerProperties[0] = new PointerProperties();
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 0388087..846e201 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -48,10 +48,12 @@
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.shadows.ShadowLooperExecutor;
 
 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;
@@ -81,7 +83,7 @@
     public static final int NO__ICON = -1;
     public static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
 
-    // Authority for providing a dummy default-workspace-layout data.
+    // Authority for providing a test default-workspace-layout data.
     private static final String TEST_PROVIDER_AUTHORITY =
             LauncherModelHelper.class.getName().toLowerCase();
     private static final int DEFAULT_BITMAP_SIZE = 10;
@@ -252,7 +254,7 @@
     }
 
     /**
-     * Adds a dummy item in the DB.
+     * Adds a mock item in the DB.
      * @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
      *             folder (where the type represents the number of items in the folder).
      */
@@ -310,7 +312,7 @@
     }
 
     /**
-     * Initializes the DB with dummy elements to represent the provided grid structure.
+     * Initializes the DB with mock elements to represent the provided grid structure.
      * @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
      *                  type definitions. The first dimension represents the screens and the next
      *                  two represent the workspace grid.
@@ -347,20 +349,20 @@
     }
 
     /**
-     * Sets up a dummy provider to load the provided layout by default, next time the layout loads
+     * Sets up a mock provider to load the provided layout by default, next time the layout loads
      */
     public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
             throws Exception {
         Context context = RuntimeEnvironment.application;
         InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
-        idp.numRows = idp.numColumns = idp.numHotseatIcons = DEFAULT_GRID_SIZE;
+        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);
 
         shadowOf(context.getPackageManager())
-                .addProviderIfNotPresent(new ComponentName("com.test", "Dummy")).authority =
+                .addProviderIfNotPresent(new ComponentName("com.test", "Mock")).authority =
                 TEST_PROVIDER_AUTHORITY;
 
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -403,14 +405,16 @@
     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.
-        ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.UI_HELPER_EXECUTOR);
+        ShadowLooperExecutor sle = Shadow.extract(Executors.MAIN_EXECUTOR);
+        sle.setHandler(Executors.UI_HELPER_EXECUTOR.getHandler());
 
         Callbacks mockCb = mock(Callbacks.class);
         getModel().addCallbacksAndLoad(mockCb);
 
         Executors.MODEL_EXECUTOR.submit(() -> { }).get();
         Executors.UI_HELPER_EXECUTOR.submit(() -> { }).get();
-        ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.MAIN_EXECUTOR);
+
+        sle.setHandler(null);
         getModel().removeCallbacks(mockCb);
     }
 
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
index 6dd4df8..efac150 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
@@ -19,7 +19,6 @@
 
 import android.app.Application;
 
-import com.android.launcher3.shadows.LShadowWallpaperManager;
 import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
 import com.android.launcher3.shadows.ShadowOverrides;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -37,9 +36,6 @@
 
         // Disable plugins
         PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
-
-        // Initialize mock wallpaper manager
-        LShadowWallpaperManager.initializeMock();
     }
 
     @Override
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
index f019a20..fdddab4 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
@@ -18,6 +18,8 @@
 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;
@@ -46,10 +48,7 @@
      */
     public static String getLauncherClassName() {
         Context context = RuntimeEnvironment.application;
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setPackage(context.getPackageName())
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        Intent homeIntent = createHomeIntent().setPackage(context.getPackageName());
 
         List<ResolveInfo> launchers = context.getPackageManager()
                 .queryIntentActivities(homeIntent, 0);
diff --git a/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java b/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java
new file mode 100644
index 0000000..1090d1e
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/CachingWidgetPreviewLoaderTest.java
@@ -0,0 +1,409 @@
+/*
+ * 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/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
new file mode 100644
index 0000000..a6f892c
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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.Mockito.doAnswer;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+
+import org.junit.Before;
+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)
+public final class LauncherAppWidgetProviderInfoTest {
+
+    private static final int CELL_SIZE = 50;
+    private static final int NUM_OF_COLS = 4;
+    private static final int NUM_OF_ROWS = 5;
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void initSpans_minWidthSmallerThanCellWidth_shouldInitializeSpansToOne() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 20;
+        info.minHeight = 20;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.spanX).isEqualTo(1);
+        assertThat(info.spanY).isEqualTo(1);
+    }
+
+    @Test
+    public void initSpans_minWidthLargerThanCellWidth_shouldInitializeSpans() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 80;
+        info.minHeight = 80;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.spanX).isEqualTo(2);
+        assertThat(info.spanY).isEqualTo(2);
+    }
+
+    @Test
+    public void
+            initSpans_minWidthLargerThanGridColumns_shouldInitializeSpansToAtMostTheGridColumns() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = CELL_SIZE * (NUM_OF_COLS + 1);
+        info.minHeight = 20;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.spanX).isEqualTo(NUM_OF_COLS);
+        assertThat(info.spanY).isEqualTo(1);
+    }
+
+    @Test
+    public void initSpans_minHeightLargerThanGridRows_shouldInitializeSpansToAtMostTheGridRows() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 20;
+        info.minHeight = 50 * (NUM_OF_ROWS + 1);
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.spanX).isEqualTo(1);
+        assertThat(info.spanY).isEqualTo(NUM_OF_ROWS);
+    }
+
+    @Test
+    public void initSpans_minResizeWidthUnspecified_shouldInitializeMinSpansToOne() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(1);
+        assertThat(info.minSpanY).isEqualTo(1);
+    }
+
+    @Test
+    public void initSpans_minResizeWidthSmallerThanCellWidth_shouldInitializeMinSpansToOne() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 100;
+        info.minHeight = 100;
+        info.minResizeWidth = 20;
+        info.minResizeHeight = 20;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(1);
+        assertThat(info.minSpanY).isEqualTo(1);
+    }
+
+    @Test
+    public void initSpans_minResizeWidthLargerThanCellWidth_shouldInitializeMinSpans() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 100;
+        info.minHeight = 100;
+        info.minResizeWidth = 80;
+        info.minResizeHeight = 80;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(2);
+        assertThat(info.minSpanY).isEqualTo(2);
+    }
+
+    @Test
+    public void initSpans_minResizeWidthWithCellSpacingAndWidgetInset_shouldInitializeMinSpans() {
+        InvariantDeviceProfile idp = createIDP();
+        DeviceProfile dp = idp.supportedProfiles.get(0);
+        Rect padding = new Rect();
+        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;
+        Mockito.when(dp.shouldInsetWidgets()).thenReturn(true);
+
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = CELL_SIZE * 3;
+        info.minHeight = CELL_SIZE * 3;
+        info.minResizeWidth = CELL_SIZE * 2 + maxPadding;
+        info.minResizeHeight = CELL_SIZE * 2 + maxPadding;
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(2);
+        assertThat(info.minSpanY).isEqualTo(2);
+    }
+
+    @Test
+    public void initSpans_minResizeWidthWithCellSpacingAndNoWidgetInset_shouldInitializeMinSpans() {
+        InvariantDeviceProfile idp = createIDP();
+        DeviceProfile dp = idp.supportedProfiles.get(0);
+        Rect padding = new Rect();
+        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;
+        Mockito.when(dp.shouldInsetWidgets()).thenReturn(false);
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = CELL_SIZE * 3;
+        info.minHeight = CELL_SIZE * 3;
+        info.minResizeWidth = CELL_SIZE * 2 + maxPadding;
+        info.minResizeHeight = CELL_SIZE * 2 + maxPadding;
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(3);
+        assertThat(info.minSpanY).isEqualTo(3);
+    }
+
+    @Test
+    public void
+            initSpans_minResizeWidthHeightLargerThanMinWidth_shouldUseMinWidthHeightAsMinSpans() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 20;
+        info.minHeight = 20;
+        info.minResizeWidth = 80;
+        info.minResizeHeight = 80;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(1);
+        assertThat(info.minSpanY).isEqualTo(1);
+    }
+
+    @Test
+    public void isMinSizeFulfilled_minWidthAndHeightWithinGridSize_shouldReturnTrue() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 80;
+        info.minHeight = 80;
+        info.minResizeWidth = 50;
+        info.minResizeHeight = 50;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.isMinSizeFulfilled()).isTrue();
+    }
+
+    @Test
+    public void
+            isMinSizeFulfilled_minWidthAndMinResizeWidthExceededGridColumns_shouldReturnFalse() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = CELL_SIZE * (NUM_OF_COLS + 2);
+        info.minHeight = 80;
+        info.minResizeWidth = CELL_SIZE * (NUM_OF_COLS + 1);
+        info.minResizeHeight = 50;
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.isMinSizeFulfilled()).isFalse();
+    }
+
+    @Test
+    public void isMinSizeFulfilled_minHeightAndMinResizeHeightExceededGridRows_shouldReturnFalse() {
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minWidth = 80;
+        info.minHeight = CELL_SIZE * (NUM_OF_ROWS + 2);
+        info.minResizeWidth = 50;
+        info.minResizeHeight = CELL_SIZE * (NUM_OF_ROWS + 1);
+        InvariantDeviceProfile idp = createIDP();
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.isMinSizeFulfilled()).isFalse();
+    }
+
+    private InvariantDeviceProfile createIDP() {
+        DeviceProfile profile = Mockito.mock(DeviceProfile.class);
+        doAnswer(i -> {
+            ((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE);
+            return null;
+        }).when(profile).getCellSize(any(Point.class));
+        Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
+
+        InvariantDeviceProfile idp = new InvariantDeviceProfile();
+        List<DeviceProfile> supportedProfiles = new ArrayList<>(idp.supportedProfiles);
+        supportedProfiles.add(profile);
+        idp.supportedProfiles = Collections.unmodifiableList(supportedProfiles);
+        idp.numColumns = NUM_OF_COLS;
+        idp.numRows = NUM_OF_ROWS;
+        return idp;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
deleted file mode 100644
index 84c65b1..0000000
--- a/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.widget;
-
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.view.LayoutInflater;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.data.PackageItemInfo;
-
-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.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-@RunWith(RobolectricTestRunner.class)
-public class WidgetsListAdapterTest {
-
-    @Mock private LayoutInflater mMockLayoutInflater;
-    @Mock private WidgetPreviewLoader mMockWidgetCache;
-    @Mock private RecyclerView.AdapterDataObserver mListener;
-    @Mock private IconCache mIconCache;
-
-    private WidgetsListAdapter mAdapter;
-    private InvariantDeviceProfile mTestProfile;
-    private Context mContext;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
-        mTestProfile = new InvariantDeviceProfile();
-        mTestProfile.numRows = 5;
-        mTestProfile.numColumns = 5;
-        mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
-                mIconCache, null, null);
-        mAdapter.registerAdapterDataObserver(mListener);
-    }
-
-    @Test
-    public void test_notifyDataSetChanged() throws Exception {
-        mAdapter.setWidgets(generateSampleMap(1));
-        verify(mListener, times(1)).onChanged();
-    }
-
-    @Test
-    public void test_notifyItemInserted() throws Exception {
-        mAdapter.setWidgets(generateSampleMap(1));
-        mAdapter.setWidgets(generateSampleMap(2));
-        verify(mListener, times(1)).onChanged();
-        verify(mListener, times(1)).onItemRangeInserted(eq(1), eq(1));
-    }
-
-    @Test
-    public void test_notifyItemRemoved() throws Exception {
-        mAdapter.setWidgets(generateSampleMap(2));
-        mAdapter.setWidgets(generateSampleMap(1));
-        verify(mListener, times(1)).onChanged();
-        verify(mListener, times(1)).onItemRangeRemoved(eq(1), eq(1));
-    }
-
-    @Test
-    public void testNotifyItemChanged_PackageIconDiff() throws Exception {
-        mAdapter.setWidgets(generateSampleMap(1));
-        mAdapter.setWidgets(generateSampleMap(1));
-        verify(mListener, times(1)).onChanged();
-        verify(mListener, times(1)).onItemRangeChanged(eq(0), eq(1), isNull());
-    }
-
-    @Test
-    public void testNotifyItemChanged_widgetItemInfoDiff() throws Exception {
-        // TODO: same package name but item number changed
-    }
-
-    @Test
-    public void testNotifyItemInsertedRemoved_hodgepodge() throws Exception {
-        // TODO: insert and remove combined.          curMap
-        // newMap [A, C, D]                           [A, B, E]
-        // B - C < 0, removed B from index 1          [A, E]
-        // E - C > 0, C inserted to index 1           [A, C, E]
-        // E - D > 0, D inserted to index 2           [A, C, D, E]
-        // E - null = -1, E deleted from index 3      [A, C, D]
-    }
-
-    /**
-     * Helper method to generate the sample widget model map that can be used for the tests
-     * @param num the number of WidgetItem the map should contain
-     */
-    private ArrayList<WidgetListRowEntry> generateSampleMap(int num) {
-        ArrayList<WidgetListRowEntry> result = new ArrayList<>();
-        if (num <= 0) return result;
-        ShadowPackageManager spm = shadowOf(mContext.getPackageManager());
-
-        for (int i = 0; i < num; i++) {
-            ComponentName cn = new ComponentName("com.dummy.apk" + i, "DummyWidet");
-
-            AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
-            widgetInfo.provider = cn;
-            ReflectionHelpers.setField(widgetInfo, "providerInfo", spm.addReceiverIfNotPresent(cn));
-
-            WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
-                    .fromProviderInfo(mContext, widgetInfo), mTestProfile, mIconCache);
-
-            PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
-            pInfo.title = pInfo.packageName;
-            pInfo.user = wi.user;
-            pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
-
-            result.add(new WidgetListRowEntry(pInfo, new ArrayList<>(Collections.singleton(wi))));
-        }
-
-        return result;
-    }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
new file mode 100644
index 0000000..b9f183c
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
@@ -0,0 +1,313 @@
+/*
+ * 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 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;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.UserHandle;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+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.picker.WidgetsListAdapter.WidgetListBaseRowEntryComparator;
+
+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.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsDiffReporterTest {
+    private static final String TEST_PACKAGE_PREFIX = "com.android.test";
+    private static final WidgetListBaseRowEntryComparator COMPARATOR =
+            new WidgetListBaseRowEntryComparator();
+
+    @Mock private IconCache mIconCache;
+    @Mock private RecyclerView.Adapter mAdapter;
+
+    private InvariantDeviceProfile mTestProfile;
+    private WidgetsDiffReporter mWidgetsDiffReporter;
+    private Context mContext;
+    private WidgetsListHeaderEntry mHeaderA;
+    private WidgetsListHeaderEntry mHeaderB;
+    private WidgetsListHeaderEntry mHeaderC;
+    private WidgetsListHeaderEntry mHeaderD;
+    private WidgetsListHeaderEntry mHeaderE;
+    private WidgetsListContentEntry mContentC;
+    private WidgetsListContentEntry mContentE;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTestProfile = new InvariantDeviceProfile();
+        mTestProfile.numRows = 5;
+        mTestProfile.numColumns = 5;
+
+        doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
+                .getComponent().getPackageName())
+                .when(mIconCache).getTitleNoCache(any());
+
+        mContext = RuntimeEnvironment.application;
+        mWidgetsDiffReporter = new WidgetsDiffReporter(mIconCache, mAdapter);
+        mHeaderA = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "A",
+                /* appName= */ "A", /* numOfWidgets= */ 3);
+        mHeaderB = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "B",
+                /* appName= */ "B", /* numOfWidgets= */ 3);
+        mHeaderC = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "C",
+                /* appName= */ "C", /* numOfWidgets= */ 3);
+        mContentC = createWidgetsContentEntry(TEST_PACKAGE_PREFIX + "C",
+                /* appName= */ "C", /* numOfWidgets= */ 3);
+        mHeaderD = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "D",
+                /* appName= */ "D", /* numOfWidgets= */ 3);
+        mHeaderE = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "E",
+                /* appName= */ "E", /* numOfWidgets= */ 3);
+        mContentE = createWidgetsContentEntry(TEST_PACKAGE_PREFIX + "E",
+                /* appName= */ "E", /* numOfWidgets= */ 3);
+    }
+
+    @Test
+    public void listNotChanged_shouldNotInvokeAnyCallbacks() {
+        // GIVEN the current list has app headers [A, B, C].
+        ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+                List.of(mHeaderA, mHeaderB, mHeaderC));
+
+        // WHEN computing the list difference.
+        mWidgetsDiffReporter.process(currentList, currentList, COMPARATOR);
+
+        // THEN there is no adaptor callback.
+        verifyZeroInteractions(mAdapter);
+        // THEN the current list contains the same entries.
+        assertThat(currentList).containsExactly(mHeaderA, mHeaderB, mHeaderC);
+    }
+
+    @Test
+    public void headersOnly_emptyListToNonEmpty_shouldInvokeNotifyDataSetChanged() {
+        // GIVEN the current list has app headers [A, B, C].
+        ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>();
+
+        List<WidgetsListBaseEntry> newList = List.of(
+                createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "A", "A", 3),
+                createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "B", "B", 3),
+                createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "C", "C", 3));
+
+        // WHEN computing the list difference.
+        mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+        // THEN notifyDataSetChanged is called
+        verify(mAdapter).notifyDataSetChanged();
+        // THEN the current list contains all elements from the new list.
+        assertThat(currentList).containsExactlyElementsIn(newList);
+    }
+
+    @Test
+    public void headersOnly_nonEmptyToEmptyList_shouldInvokeNotifyDataSetChanged() {
+        // GIVEN the current list has app headers [A, B, C].
+        ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+                List.of(mHeaderA, mHeaderB, mHeaderC));
+        // GIVEN the new list is empty.
+        List<WidgetsListBaseEntry> newList = List.of();
+
+        // WHEN computing the list difference.
+        mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+        // THEN notifyDataSetChanged is called.
+        verify(mAdapter).notifyDataSetChanged();
+        // THEN the current list isEmpty.
+        assertThat(currentList).isEmpty();
+    }
+
+    @Test
+    public void headersOnly_itemAddedAndRemovedInTheNewList_shouldInvokeCorrectCallbacks() {
+        // GIVEN the current list has app headers [A, B, D].
+        ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+                List.of(mHeaderA, mHeaderB, mHeaderD));
+        // GIVEN the new list has app headers [A, C, E].
+        List<WidgetsListBaseEntry> newList = List.of(mHeaderA, mHeaderC, mHeaderE);
+
+        // WHEN computing the list difference.
+        mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+        // THEN "B" is removed from position 1.
+        verify(mAdapter).notifyItemRemoved(/* position= */ 1);
+        // THEN "D" is removed from position 2.
+        verify(mAdapter).notifyItemRemoved(/* position= */ 2);
+        // THEN "C" is inserted at position 1.
+        verify(mAdapter).notifyItemInserted(/* position= */ 1);
+        // THEN "E" is inserted at position 2.
+        verify(mAdapter).notifyItemInserted(/* position= */ 2);
+        // THEN the current list contains all elements from the new list.
+        assertThat(currentList).containsExactlyElementsIn(newList);
+    }
+
+    @Test
+    public void headersContentsMix_itemAddedAndRemovedInTheNewList_shouldInvokeCorrectCallbacks() {
+        // GIVEN the current list has app headers [A, B, E content].
+        ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+                List.of(mHeaderA, mHeaderB, mContentE));
+        // GIVEN the new list has app headers [A, C content, D].
+        List<WidgetsListBaseEntry> newList = List.of(mHeaderA, mContentC, mHeaderD);
+
+        // WHEN computing the list difference.
+        mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+        // THEN "B" is removed from position 1.
+        verify(mAdapter).notifyItemRemoved(/* position= */ 1);
+        // THEN "C content" is inserted at position 1.
+        verify(mAdapter).notifyItemInserted(/* position= */ 1);
+        // THEN "D" is inserted at position 2.
+        verify(mAdapter).notifyItemInserted(/* position= */ 2);
+        // THEN "E content" is removed from position 3.
+        verify(mAdapter).notifyItemRemoved(/* position= */ 3);
+        // THEN the current list contains all elements from the new list.
+        assertThat(currentList).containsExactlyElementsIn(newList);
+    }
+
+    @Test
+    public void headersContentsMix_userInteractWithHeader_shouldInvokeCorrectCallbacks() {
+        // GIVEN the current list has app headers [A, B, E content].
+        ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+                List.of(mHeaderA, mHeaderB, mContentE));
+        // GIVEN the new list has app headers [A, B, E content] and the user has interacted with B.
+        List<WidgetsListBaseEntry> newList =
+                List.of(mHeaderA, mHeaderB.withWidgetListShown(), mContentE);
+
+        // WHEN computing the list difference.
+        mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+        // THEN notify "B" has been changed.
+        verify(mAdapter).notifyItemChanged(/* position= */ 1);
+        // THEN the current list contains all elements from the new list.
+        assertThat(currentList).containsExactlyElementsIn(newList);
+    }
+
+    @Test
+    public void headersContentsMix_headerWidgetsModified_shouldInvokeCorrectCallbacks() {
+        // GIVEN the current list has app headers [A, B, E content].
+        ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+                List.of(mHeaderA, mHeaderB, mContentE));
+        // GIVEN the new list has one of the headers widgets list modified.
+        List<WidgetsListBaseEntry> newList = List.of(
+                new WidgetsListHeaderEntry(
+                        mHeaderA.mPkgItem, mHeaderA.mTitleSectionName,
+                        mHeaderA.mWidgets.subList(0, 1)),
+                mHeaderB, mContentE);
+
+        // WHEN computing the list difference.
+        mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+        // THEN notify "A" has been changed.
+        verify(mAdapter).notifyItemChanged(/* position= */ 0);
+        // THEN the current list contains all elements from the new list.
+        assertThat(currentList).containsExactlyElementsIn(newList);
+    }
+
+    @Test
+    public void headersContentsMix_contentMaxSpanSizeModified_shouldInvokeCorrectCallbacks() {
+        // GIVEN the current list has app headers [A, B, E content].
+        ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+                List.of(mHeaderA, mHeaderB, mContentE));
+        // GIVEN the new list has max span size in "E content" modified.
+        List<WidgetsListBaseEntry> newList = List.of(
+                mHeaderA,
+                mHeaderB,
+                new WidgetsListContentEntry(
+                        mContentE.mPkgItem,
+                        mContentE.mTitleSectionName,
+                        mContentE.mWidgets,
+                        mContentE.getMaxSpanSizeInCells() + 1));
+
+        // WHEN computing the list difference.
+        mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+        // THEN notify "E content" has been changed.
+        verify(mAdapter).notifyItemChanged(/* position= */ 2);
+        // THEN the current list contains all elements from the new list.
+        assertThat(currentList).containsExactlyElementsIn(newList);
+    }
+
+
+    private WidgetsListHeaderEntry createWidgetsHeaderEntry(String packageName, String appName,
+            int numOfWidgets) {
+        List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+        PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+                widgetItems.get(0).user);
+
+        return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+    }
+
+    private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName,
+            int numOfWidgets) {
+        List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+        PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+                widgetItems.get(0).user);
+
+        return new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+    }
+
+    private PackageItemInfo createPackageItemInfo(String packageName, String appName,
+            UserHandle userHandle) {
+        PackageItemInfo pInfo = new PackageItemInfo(packageName);
+        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));
+
+            WidgetItem widgetItem = new WidgetItem(
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+                    mTestProfile, mIconCache);
+            widgetItems.add(widgetItem);
+        }
+        return widgetItems;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
new file mode 100644
index 0000000..c730fc0
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.picker;
+
+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;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Process;
+import android.os.UserHandle;
+import android.view.LayoutInflater;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+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)
+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;
+
+    private WidgetsListAdapter mAdapter;
+    private InvariantDeviceProfile mTestProfile;
+    private UserHandle mUserHandle;
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mTestProfile = new InvariantDeviceProfile();
+        mTestProfile.numRows = 5;
+        mTestProfile.numColumns = 5;
+        mUserHandle = Process.myUserHandle();
+        mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
+                mIconCache, null, null);
+        mAdapter.registerAdapterDataObserver(mListener);
+
+        doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
+                        .getComponent().getPackageName())
+                .when(mIconCache).getTitleNoCache(any());
+    }
+
+    @Test
+    public void setWidgets_shouldNotifyDataSetChanged() {
+        mAdapter.setWidgets(generateSampleMap(1));
+
+        verify(mListener).onChanged();
+    }
+
+    @Test
+    public void setWidgets_withItemInserted_shouldNotifyItemInserted() {
+        mAdapter.setWidgets(generateSampleMap(1));
+        mAdapter.setWidgets(generateSampleMap(2));
+
+        verify(mListener).onItemRangeInserted(eq(1), eq(1));
+    }
+
+    @Test
+    public void setWidgets_withItemRemoved_shouldNotifyItemRemoved() {
+        mAdapter.setWidgets(generateSampleMap(2));
+        mAdapter.setWidgets(generateSampleMap(1));
+
+        verify(mListener).onItemRangeRemoved(eq(1), eq(1));
+    }
+
+    @Test
+    public void setWidgets_appIconChanged_shouldNotifyItemChanged() {
+        mAdapter.setWidgets(generateSampleMap(1));
+        mAdapter.setWidgets(generateSampleMap(1));
+
+        verify(mListener).onItemRangeChanged(eq(0), eq(1), isNull());
+    }
+
+    @Test
+    public void headerClick_expanded_shouldNotifyItemChange() {
+        // 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].
+        mAdapter.setWidgets(generateSampleMap(3));
+
+        // WHEN com.google.test.1 header is expanded.
+        mAdapter.onHeaderClicked(/* showWidgets= */ true,
+                new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle));
+
+        // 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));
+    }
+
+    @Test
+    public void setWidgets_expandedApp_moreWidgets_shouldNotifyItemChangedWithWidgetItemInfoDiff() {
+        // GIVEN the adapter was first populated with com.google.test0 & com.google.test1. Each app
+        // has one widget.
+        ArrayList<WidgetsListBaseEntry> allEntries = generateSampleMap(2);
+        mAdapter.setWidgets(allEntries);
+        // GIVEN test com.google.test1 is expanded.
+        // Visible entries in the adapter are:
+        // [com.google.test0, com.google.test1, com.google.test1 content]
+        mAdapter.onHeaderClicked(/* showWidgets= */ true,
+                new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle));
+        Mockito.reset(mListener);
+
+        // WHEN the adapter is updated with the same list of apps but com.google.test1 has 2 widgets
+        // now.
+        WidgetsListContentEntry testPackage1ContentEntry =
+                (WidgetsListContentEntry) allEntries.get(3);
+        WidgetItem widgetItem = testPackage1ContentEntry.mWidgets.get(0);
+        WidgetsListContentEntry newTestPackage1ContentEntry = new WidgetsListContentEntry(
+                testPackage1ContentEntry.mPkgItem,
+                testPackage1ContentEntry.mTitleSectionName, List.of(widgetItem, widgetItem));
+        allEntries.set(3, newTestPackage1ContentEntry);
+        mAdapter.setWidgets(allEntries);
+
+        // THEN the onItemRangeChanged is invoked for "com.google.test1 content" at index 2.
+        verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull());
+    }
+
+    @Test
+    public void setWidgets_hodgepodge_shouldInvokeExpectedDataObserverCallbacks() {
+        // GIVEN a widgets entry list:
+        // Index:  0|   1      | 2|      3   | 4|     5    | 6|     7    | 8|     9    |
+        //        [A, A content, B, B content, C, C content, D, D content, E, E content]
+        List<WidgetsListBaseEntry> allAppsWithWidgets = generateSampleMap(5);
+        // GIVEN the current widgets list consist of [A, A content, B, B content, E, E content].
+        // GIVEN the visible widgets list consist of [A, B, E]
+        List<WidgetsListBaseEntry> currentList = List.of(
+                // A & A content
+                allAppsWithWidgets.get(0), allAppsWithWidgets.get(1),
+                // B & B content
+                allAppsWithWidgets.get(2), allAppsWithWidgets.get(3),
+                // E & E content
+                allAppsWithWidgets.get(8), allAppsWithWidgets.get(9));
+        mAdapter.setWidgets(currentList);
+
+        // WHEN the widgets list is updated to [A, A content, C, C content, D, D content].
+        // WHEN the visible widgets list is updated to [A, C, D].
+        List<WidgetsListBaseEntry> newList = List.of(
+                // A & A content
+                allAppsWithWidgets.get(0), allAppsWithWidgets.get(1),
+                // C & C content
+                allAppsWithWidgets.get(4), allAppsWithWidgets.get(5),
+                // D & D content
+                allAppsWithWidgets.get(6), allAppsWithWidgets.get(7));
+        mAdapter.setWidgets(newList);
+
+        // Computation logic                           | [Intermediate list during computation]
+        // THEN B <> C < 0, removed B from index 1     | [A, E]
+        verify(mListener).onItemRangeRemoved(/* positionStart= */ 1, /* 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 <> null = -1, E deleted from index 3 | [A, C, D]
+        verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* 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);
+        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]
+        mAdapter.onHeaderClicked(/* showWidgets= */ true,
+                new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle));
+        Mockito.reset(mListener);
+
+        // WHEN same widget entries are set again.
+        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);
+    }
+
+    /**
+     * Generates a list of sample widget entries.
+     *
+     * <p>Each sample app has 1 widget only. An app is represented by 2 entries,
+     * {@link WidgetsListHeaderEntry} & {@link WidgetsListContentEntry}. Only
+     * {@link WidgetsListHeaderEntry} is always visible in the {@link WidgetsListAdapter}.
+     * {@link WidgetsListContentEntry} is only shown upon clicking the corresponding app's
+     * {@link WidgetsListHeaderEntry}. Only at most one {@link WidgetsListContentEntry} is shown at
+     * a time.
+     *
+     * @param num the number of apps that have widgets.
+     */
+    private ArrayList<WidgetsListBaseEntry> generateSampleMap(int num) {
+        ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
+        if (num <= 0) return result;
+
+        for (int i = 0; i < num; i++) {
+            String packageName = TEST_PACKAGE_PLACEHOLDER + i;
+
+            List<WidgetItem> widgetItems = generateWidgetItems(packageName, /* numOfWidgets= */ 1);
+
+            PackageItemInfo pInfo = new PackageItemInfo(packageName);
+            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));
+            result.add(new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems));
+        }
+
+        return result;
+    }
+
+    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));
+
+            widgetItems.add(new WidgetItem(
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+                    mTestProfile, mIconCache));
+        }
+        return widgetItems;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
new file mode 100644
index 0000000..81b0c5f
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.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.widget.picker;
+
+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 android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.view.LayoutInflater;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+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.PackageUserKey;
+import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+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)
+public final class WidgetsListHeaderViewHolderBinderTest {
+    private static final String TEST_PACKAGE = "com.google.test";
+    private static final String APP_NAME = "Test app";
+
+    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;
+        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),
+                mOnHeaderClickListener,
+                new WidgetsListDrawableFactory(mTestActivity),
+                widgetsListAdapter);
+    }
+
+    @After
+    public void tearDown() {
+        mActivityController.destroy();
+    }
+
+    @Test
+    public void bindViewHolder_appWith3Widgets_shouldShowTheCorrectAppNameAndSubtitle() {
+        WidgetsListHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
+                new FrameLayout(mTestActivity));
+        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+        WidgetsListHeaderEntry entry = generateSampleAppHeader(
+                APP_NAME,
+                TEST_PACKAGE,
+                /* numOfWidgets= */ 3);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+
+        TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
+        TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
+        assertThat(appTitle.getText()).isEqualTo(APP_NAME);
+        assertThat(appSubtitle.getText()).isEqualTo("3 widgets");
+    }
+
+    @Test
+    public void bindViewHolder_shouldAttachOnHeaderClickListener() {
+        WidgetsListHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
+                new FrameLayout(mTestActivity));
+        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+        WidgetsListHeaderEntry entry = generateSampleAppHeader(
+                APP_NAME,
+                TEST_PACKAGE,
+                /* numOfWidgets= */ 3);
+
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+        widgetsListHeader.callOnClick();
+
+        verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
+                eq(new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)));
+    }
+
+    private WidgetsListHeaderEntry generateSampleAppHeader(String appName, String packageName,
+            int numOfWidgets) {
+        PackageItemInfo appInfo = new PackageItemInfo(packageName);
+        appInfo.title = appName;
+        appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+
+        return new WidgetsListHeaderEntry(appInfo,
+                /* titleSectionName= */ "",
+                generateWidgetItems(packageName, numOfWidgets));
+    }
+
+    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));
+
+            widgetItems.add(new WidgetItem(
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+                    mTestProfile, mIconCache));
+        }
+        return widgetItems;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
new file mode 100644
index 0000000..a0ba7c3
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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 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 android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.view.LayoutInflater;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+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.PackageUserKey;
+import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
+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)
+public final class WidgetsListSearchHeaderViewHolderBinderTest {
+    private static final String TEST_PACKAGE = "com.google.test";
+    private static final String APP_NAME = "Test app";
+
+    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;
+        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),
+                mOnHeaderClickListener,
+                new WidgetsListDrawableFactory(mTestActivity),
+                widgetsListAdapter);
+    }
+
+    @After
+    public void tearDown() {
+        mActivityController.destroy();
+    }
+
+    @Test
+    public void bindViewHolder_appWith3Widgets_shouldShowTheCorrectAppNameAndSubtitle() {
+        WidgetsListSearchHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
+                new FrameLayout(mTestActivity));
+        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+        WidgetsListSearchHeaderEntry entry = generateSampleSearchHeader(
+                APP_NAME,
+                TEST_PACKAGE,
+                /* numOfWidgets= */ 3);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+
+        TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
+        TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
+        assertThat(appTitle.getText()).isEqualTo(APP_NAME);
+        assertThat(appSubtitle.getText())
+                .isEqualTo(".SampleWidget0, .SampleWidget1, .SampleWidget2");
+    }
+
+    @Test
+    public void bindViewHolder_shouldAttachOnHeaderClickListener() {
+        WidgetsListSearchHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
+                new FrameLayout(mTestActivity));
+        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+        WidgetsListSearchHeaderEntry entry = generateSampleSearchHeader(
+                APP_NAME,
+                TEST_PACKAGE,
+                /* numOfWidgets= */ 3);
+
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+        widgetsListHeader.callOnClick();
+
+        verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
+                eq(new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)));
+    }
+
+    private WidgetsListSearchHeaderEntry generateSampleSearchHeader(String appName,
+            String packageName, int numOfWidgets) {
+        PackageItemInfo appInfo = new PackageItemInfo(packageName);
+        appInfo.title = appName;
+        appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+
+        return new WidgetsListSearchHeaderEntry(appInfo,
+                /* titleSectionName= */ "",
+                generateWidgetItems(packageName, numOfWidgets));
+    }
+
+    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));
+
+            widgetItems.add(new WidgetItem(
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+                    mTestProfile, mIconCache));
+        }
+        return widgetItems;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
new file mode 100644
index 0000000..8f9d132
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.os.Looper.getMainLooper;
+
+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.graphics.Bitmap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.widget.FrameLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+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.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)
+public final class WidgetsListTableViewHolderBinderTest {
+    private static final String TEST_PACKAGE = "com.google.test";
+    private static final String APP_NAME = "Test app";
+
+    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;
+    @Mock
+    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;
+        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),
+                mOnIconClickListener,
+                mOnLongClickListener,
+                new CachingWidgetPreviewLoader(mWidgetPreviewLoader),
+                new WidgetsListDrawableFactory(mTestActivity),
+                widgetsListAdapter);
+    }
+
+    @After
+    public void tearDown() {
+        mActivityController.destroy();
+    }
+
+    @Test
+    public void bindViewHolder_appWith3Widgets_shouldHave3Widgets() {
+        WidgetsRowViewHolder viewHolder = mViewHolderBinder.newViewHolder(
+                new FrameLayout(mTestActivity));
+        WidgetsListContentEntry entry = generateSampleAppWithWidgets(
+                APP_NAME,
+                TEST_PACKAGE,
+                /* numOfWidgets= */ 3);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
+        shadowOf(getMainLooper()).idle();
+
+        // 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(row.getChildCount()).isEqualTo(3);
+        // Widget 0 label is .SampleWidget0.
+        assertWidgetCellWithLabel(row.getChildAt(0), ".SampleWidget0");
+        // Widget 1 label is .SampleWidget1.
+        assertWidgetCellWithLabel(row.getChildAt(1), ".SampleWidget1");
+        // Widget 2 label is .SampleWidget2.
+        assertWidgetCellWithLabel(row.getChildAt(2), ".SampleWidget2");
+    }
+
+    private WidgetsListContentEntry generateSampleAppWithWidgets(String appName, String packageName,
+            int numOfWidgets) {
+        PackageItemInfo appInfo = new PackageItemInfo(packageName);
+        appInfo.title = appName;
+        appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+
+        return new WidgetsListContentEntry(appInfo,
+                /* titleSectionName= */ "",
+                generateWidgetItems(packageName, numOfWidgets));
+    }
+
+    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));
+
+            widgetItems.add(new WidgetItem(
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+                    mTestProfile, mIconCache));
+        }
+        return widgetItems;
+    }
+
+    private void assertWidgetCellWithLabel(View view, String label) {
+        assertThat(view).isInstanceOf(WidgetCell.class);
+        TextView widgetLabel = (TextView) view.findViewById(R.id.widget_name);
+        assertThat(widgetLabel.getText()).isEqualTo(label);
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
new file mode 100644
index 0000000..106cac0
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.model;
+
+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 com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+
+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.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(RobolectricTestRunner.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 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");
+    private final Map<ComponentName, String> mWidgetsToLabels = new HashMap();
+
+    @Mock private IconCache mIconCache;
+
+    private Context mContext;
+    private InvariantDeviceProfile mTestProfile;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mWidgetsToLabels.put(mWidget1, "Cat");
+        mWidgetsToLabels.put(mWidget2, "Dog");
+        mWidgetsToLabels.put(mWidget3, "Bird");
+
+        mContext = RuntimeEnvironment.application;
+        mTestProfile = new InvariantDeviceProfile();
+        mTestProfile.numRows = 5;
+        mTestProfile.numColumns = 5;
+
+        doAnswer(invocation -> {
+            ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+            return mWidgetsToLabels.get(componentWithLabel.getComponent());
+        }).when(mIconCache).getTitleNoCache(any());
+    }
+
+    @Test
+    public void unsortedWidgets_diffLabels_shouldSortWidgetItems() {
+        // GIVEN a list of widgets in unsorted order.
+        // Cat 2x3
+        WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 3);
+        // Dog 2x3
+        WidgetItem widgetItem2 = createWidgetItem(mWidget2, /* spanX= */ 2, /* spanY= */ 3);
+        // Bird 2x3
+        WidgetItem widgetItem3 = createWidgetItem(mWidget3, /* spanX= */ 2, /* spanY= */ 3);
+
+        // WHEN creates a WidgetsListRowEntry with the unsorted widgets.
+        WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1, widgetItem2, widgetItem3));
+
+        // THEN the widgets list is sorted by their labels alphabetically: [Bird, Cat, Dog].
+        assertThat(widgetsListRowEntry.mWidgets)
+                .containsExactly(widgetItem3, widgetItem1, widgetItem2)
+                .inOrder();
+        assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
+        assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(mPackageItemInfo1);
+    }
+
+    @Test
+    public void unsortedWidgets_sameLabels_differentSize_shouldSortWidgetItems() {
+        // GIVEN a list of widgets in unsorted order.
+        // Cat 3x3
+        WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 3, /* spanY= */ 3);
+        // Cat 1x2
+        WidgetItem widgetItem2 = createWidgetItem(mWidget1, /* spanX= */ 1, /* spanY= */ 2);
+        // Cat 2x2
+        WidgetItem widgetItem3 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 2);
+
+        // WHEN creates a WidgetsListRowEntry with the unsorted widgets.
+        WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1, widgetItem2, widgetItem3));
+
+        // THEN the widgets list is sorted by their gird sizes in an ascending order:
+        // [1x2, 2x2, 3x3].
+        assertThat(widgetsListRowEntry.mWidgets)
+                .containsExactly(widgetItem2, widgetItem3, widgetItem1)
+                .inOrder();
+        assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
+        assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(mPackageItemInfo1);
+    }
+
+    @Test
+    public void unsortedWidgets_hodgepodge_shouldSortWidgetItems() {
+        // GIVEN a list of widgets in unsorted order.
+        // Cat 3x3
+        WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 3, /* spanY= */ 3);
+        // Cat 1x2
+        WidgetItem widgetItem2 = createWidgetItem(mWidget1, /* spanX= */ 1, /* spanY= */ 2);
+        // Dog 2x2
+        WidgetItem widgetItem3 = createWidgetItem(mWidget2, /* spanX= */ 2, /* spanY= */ 2);
+        // Bird 2x2
+        WidgetItem widgetItem4 = createWidgetItem(mWidget3, /* spanX= */ 2, /* spanY= */ 2);
+
+        // WHEN creates a WidgetsListRowEntry with the unsorted widgets.
+        WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1, widgetItem2, widgetItem3, widgetItem4));
+
+        // THEN the widgets list is first sorted by labels alphabetically. Then, for widgets with
+        // same labels, they are sorted by their gird sizes in an ascending order:
+        // [Bird 2x2, Cat 1x2, Cat 3x3, Dog 2x2]
+        assertThat(widgetsListRowEntry.mWidgets)
+                .containsExactly(widgetItem4, widgetItem2, widgetItem1, widgetItem3)
+                .inOrder();
+        assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
+        assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(mPackageItemInfo1);
+    }
+
+    @Test
+    public void equals_entriesWithDifferentPackageItemInfo_returnFalse() {
+        WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry1 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry2 = new WidgetsListContentEntry(
+                mPackageItemInfo2,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 3);
+
+        assertThat(widgetsListRowEntry1.equals(widgetsListRowEntry2)).isFalse();
+    }
+
+    @Test
+    public void equals_entriesWithDifferentTitleSectionName_returnFalse() {
+        WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry1 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry2 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "S",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 3);
+
+        assertThat(widgetsListRowEntry1.equals(widgetsListRowEntry2)).isFalse();
+    }
+
+    @Test
+    public void equals_entriesWithDifferentWidgetsList_returnFalse() {
+        WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 3);
+        WidgetItem widgetItem2 = createWidgetItem(mWidget2, /* spanX= */ 2, /* spanY= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry1 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry2 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem2),
+                /* maxSpanSizeInCells= */ 3);
+
+        assertThat(widgetsListRowEntry1.equals(widgetsListRowEntry2)).isFalse();
+    }
+
+    @Test
+    public void equals_entriesWithDifferentMaxSpanSize_returnFalse() {
+        WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry1 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry2 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 2);
+
+        assertThat(widgetsListRowEntry1.equals(widgetsListRowEntry2)).isFalse();
+    }
+
+    @Test
+    public void equals_entriesWithSameContents_returnTrue() {
+        WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry1 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 3);
+        WidgetsListContentEntry widgetsListRowEntry2 = new WidgetsListContentEntry(
+                mPackageItemInfo1,
+                /* titleSectionName= */ "T",
+                List.of(widgetItem1),
+                /* maxSpanSizeInCells= */ 3);
+
+        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));
+
+        LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
+                LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo);
+        launcherAppWidgetProviderInfo.spanX = spanX;
+        launcherAppWidgetProviderInfo.spanY = spanY;
+        launcherAppWidgetProviderInfo.label = label;
+
+        return new WidgetItem(launcherAppWidgetProviderInfo, mTestProfile, mIconCache);
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
new file mode 100644
index 0000000..36b6f01
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.search;
+
+import static android.os.Looper.getMainLooper;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.matches;
+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;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.UserHandle;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+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 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.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class SimpleWidgetsSearchAlgorithmTest {
+
+    @Mock private IconCache mIconCache;
+
+    private InvariantDeviceProfile mTestProfile;
+    private WidgetsListHeaderEntry mCalendarHeaderEntry;
+    private WidgetsListContentEntry mCalendarContentEntry;
+    private WidgetsListHeaderEntry mCameraHeaderEntry;
+    private WidgetsListContentEntry mCameraContentEntry;
+    private WidgetsListHeaderEntry mClockHeaderEntry;
+    private WidgetsListContentEntry mClockContentEntry;
+    private Context mContext;
+
+    private SimpleWidgetsSearchAlgorithm mSimpleWidgetsSearchAlgorithm;
+    @Mock
+    private PopupDataProvider mDataProvider;
+    @Mock
+    private SearchCallback<WidgetsListBaseEntry> mSearchCallback;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doAnswer(invocation -> {
+            ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+            return componentWithLabel.getComponent().getShortClassName();
+        }).when(mIconCache).getTitleNoCache(any());
+        mTestProfile = new InvariantDeviceProfile();
+        mTestProfile.numRows = 5;
+        mTestProfile.numColumns = 5;
+        mContext = RuntimeEnvironment.application;
+
+        mCalendarHeaderEntry =
+                createWidgetsHeaderEntry("com.example.android.Calendar", "Calendar", 2);
+        mCalendarContentEntry =
+                createWidgetsContentEntry("com.example.android.Calendar", "Calendar", 2);
+        mCameraHeaderEntry = createWidgetsHeaderEntry("com.example.android.Camera", "Camera", 11);
+        mCameraContentEntry = createWidgetsContentEntry("com.example.android.Camera", "Camera", 11);
+        mClockHeaderEntry = createWidgetsHeaderEntry("com.example.android.Clock", "Clock", 3);
+        mClockContentEntry = createWidgetsContentEntry("com.example.android.Clock", "Clock", 3);
+
+
+        mSimpleWidgetsSearchAlgorithm = new SimpleWidgetsSearchAlgorithm(mDataProvider);
+        doReturn(Collections.EMPTY_LIST).when(mDataProvider).getAllWidgets();
+    }
+
+    @Test
+    public void filter_shouldMatchOnAppName() {
+        doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
+                mCameraContentEntry, mClockHeaderEntry, mClockContentEntry))
+                .when(mDataProvider)
+                .getAllWidgets();
+
+        assertEquals(List.of(
+                new WidgetsListSearchHeaderEntry(
+                        mCalendarHeaderEntry.mPkgItem,
+                        mCalendarHeaderEntry.mTitleSectionName,
+                        mCalendarHeaderEntry.mWidgets),
+                mCalendarContentEntry,
+                new WidgetsListSearchHeaderEntry(
+                        mCameraHeaderEntry.mPkgItem,
+                        mCameraHeaderEntry.mTitleSectionName,
+                        mCameraHeaderEntry.mWidgets),
+                mCameraContentEntry),
+                SimpleWidgetsSearchAlgorithm.getFilteredWidgets(mDataProvider, "Ca"));
+    }
+
+    @Test
+    public void filter_shouldMatchOnWidgetLabel() {
+        doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
+                mCameraContentEntry))
+                .when(mDataProvider)
+                .getAllWidgets();
+
+        assertEquals(List.of(
+                new WidgetsListSearchHeaderEntry(
+                        mCalendarHeaderEntry.mPkgItem,
+                        mCalendarHeaderEntry.mTitleSectionName,
+                        mCalendarHeaderEntry.mWidgets.subList(1, 2)),
+                new WidgetsListContentEntry(
+                        mCalendarHeaderEntry.mPkgItem,
+                        mCalendarHeaderEntry.mTitleSectionName,
+                        mCalendarHeaderEntry.mWidgets.subList(1, 2)),
+                new WidgetsListSearchHeaderEntry(
+                        mCameraHeaderEntry.mPkgItem,
+                        mCameraHeaderEntry.mTitleSectionName,
+                        mCameraHeaderEntry.mWidgets.subList(1, 3)),
+                new WidgetsListContentEntry(
+                        mCameraHeaderEntry.mPkgItem,
+                        mCameraHeaderEntry.mTitleSectionName,
+                        mCameraHeaderEntry.mWidgets.subList(1, 3))),
+                SimpleWidgetsSearchAlgorithm.getFilteredWidgets(mDataProvider, "Widget1"));
+    }
+
+    @Test
+    public void doSearch_shouldInformCallback() {
+        doReturn(List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
+                mCameraContentEntry, mClockHeaderEntry, mClockContentEntry))
+                .when(mDataProvider)
+                .getAllWidgets();
+        mSimpleWidgetsSearchAlgorithm.doSearch("Ca", mSearchCallback);
+        shadowOf(getMainLooper()).idle();
+        verify(mSearchCallback).onSearchResult(
+                matches("Ca"), argThat(a -> a != null && !a.isEmpty()));
+    }
+
+    private WidgetsListHeaderEntry createWidgetsHeaderEntry(String packageName, String appName,
+            int numOfWidgets) {
+        List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+        PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+                widgetItems.get(0).user);
+
+        return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+    }
+
+    private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName,
+            int numOfWidgets) {
+        List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+        PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+                widgetItems.get(0).user);
+
+        return new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+    }
+
+    private PackageItemInfo createPackageItemInfo(String packageName, String appName,
+            UserHandle userHandle) {
+        PackageItemInfo pInfo = new PackageItemInfo(packageName);
+        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));
+
+            WidgetItem widgetItem = new WidgetItem(
+                    LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+                    mTestProfile, mIconCache);
+            widgetItems.add(widgetItem);
+        }
+        return widgetItems;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
new file mode 100644
index 0000000..7ac879a
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.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.widget.picker.search;
+
+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.view.View;
+import android.widget.ImageButton;
+
+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)
+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
+    private SearchModeListener mSearchModeListener;
+    @Mock
+    private SearchAlgorithm<WidgetsListBaseEntry> mSearchAlgorithm;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mActivityController = Robolectric.buildActivity(TestActivity.class);
+        TestActivity testActivity = mActivityController.setup().get();
+
+        mEditText = new ExtendedEditText(testActivity);
+        mCancelButton = new ImageButton(testActivity);
+        mController = new WidgetsSearchBarController(
+                mSearchAlgorithm, mEditText, mCancelButton, mSearchModeListener);
+    }
+
+    @After
+    public void tearDown() {
+        mActivityController.destroy();
+    }
+
+    @Test
+    public void onSearchResult_shouldInformSearchModeListener() {
+        ArrayList<WidgetsListBaseEntry> entries = new ArrayList<>();
+        mController.onSearchResult("abc", entries);
+
+        verify(mSearchModeListener).onSearchResults(entries);
+    }
+
+    @Test
+    public void afterTextChanged_shouldInformSearchModeListenerToEnterSearch() {
+        mEditText.setText("abc");
+
+        verify(mSearchModeListener).enterSearchMode();
+        verifyNoMoreInteractions(mSearchModeListener);
+    }
+
+    @Test
+    public void afterTextChanged_shouldDoSearch() {
+        mEditText.setText("abc");
+
+        verify(mSearchAlgorithm).doSearch(eq("abc"), any());
+    }
+
+    @Test
+    public void afterTextChanged_shouldShowCancelButton() {
+        mEditText.setText("abc");
+
+        assertEquals(mCancelButton.getVisibility(), View.VISIBLE);
+    }
+
+    @Test
+    public void afterTextChanged_empty_shouldInformSearchModeListenerToExitSearch() {
+        mEditText.setText("");
+
+        verify(mSearchModeListener).exitSearchMode();
+        verifyNoMoreInteractions(mSearchModeListener);
+    }
+
+    @Test
+    public void afterTextChanged_empty_shouldCancelSearch() {
+        mEditText.setText("");
+
+        verify(mSearchAlgorithm).cancel(true);
+        verifyNoMoreInteractions(mSearchAlgorithm);
+    }
+
+    @Test
+    public void afterTextChanged_empty_shouldHideCancelButton() {
+        mEditText.setText("");
+
+        assertEquals(mCancelButton.getVisibility(), View.GONE);
+    }
+
+    @Test
+    public void cancelSearch_shouldInformSearchModeListenerToClearResultsAndExitSearch() {
+        mCancelButton.performClick();
+
+        verify(mSearchModeListener).exitSearchMode();
+    }
+
+    @Test
+    public void cancelSearch_shouldCancelSearch() {
+        mCancelButton.performClick();
+
+        verify(mSearchAlgorithm).cancel(true);
+        verifyNoMoreInteractions(mSearchAlgorithm);
+    }
+
+    @Test
+    public void cancelSearch_shouldClearSearchBar() {
+        mCancelButton.performClick();
+
+        assertEquals(mEditText.getText().toString(), "");
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
new file mode 100644
index 0000000..56d7d68
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.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.launcher3.widget.picker.util;
+
+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.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.util.WidgetsTableUtils;
+
+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.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsTableUtilsTest {
+    private static final String TEST_PACKAGE = "com.google.test";
+
+    @Mock
+    private IconCache mIconCache;
+
+    private Context mContext;
+    private InvariantDeviceProfile mTestProfile;
+    private WidgetItem mWidget1x1;
+    private WidgetItem mWidget2x2;
+    private WidgetItem mWidget2x3;
+    private WidgetItem mWidget2x4;
+    private WidgetItem mWidget4x4;
+
+    private WidgetItem mShortcut1;
+    private WidgetItem mShortcut2;
+    private WidgetItem mShortcut3;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+
+        mTestProfile = new InvariantDeviceProfile();
+        mTestProfile.numRows = 5;
+        mTestProfile.numColumns = 5;
+
+        initTestWidgets();
+        initTestShortcuts();
+
+        doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
+                .getComponent().getPackageName())
+                .when(mIconCache).getTitleNoCache(any());
+    }
+
+
+    @Test
+    public void groupWidgetItemsIntoTable_widgetsOnly_maxSpansPerRow5_shouldGroupWidgetsInTable() {
+        List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
+                mWidget2x2);
+
+        List<ArrayList<WidgetItem>> widgetItemInTable = WidgetsTableUtils.groupWidgetItemsIntoTable(
+                widgetItems, /* maxSpansPerRow= */ 5);
+
+        // Row 0: 1x1, 2x2
+        // Row 1: 2x3, 2x4
+        // Row 2: 4x4
+        assertThat(widgetItemInTable).hasSize(3);
+        assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
+        assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget2x4);
+        assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
+    }
+
+    @Test
+    public void groupWidgetItemsIntoTable_widgetsOnly_maxSpansPerRow4_shouldGroupWidgetsInTable() {
+        List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
+                mWidget2x2);
+
+        List<ArrayList<WidgetItem>> widgetItemInTable = WidgetsTableUtils.groupWidgetItemsIntoTable(
+                widgetItems, /* maxSpansPerRow= */ 4);
+
+        // Row 0: 1x1, 2x2
+        // Row 1: 2x3,
+        // Row 2: 2x4,
+        // Row 3: 4x4
+        assertThat(widgetItemInTable).hasSize(4);
+        assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
+        assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3);
+        assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4);
+        assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
+    }
+
+    @Test
+    public void groupWidgetItemsIntoTable_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);
+
+        // Row 0: 1x1, 2x2
+        // Row 1: 2x3,
+        // Row 2: 2x4,
+        // Row 3: 4x4
+        // Row 4: shortcut3, shortcut1, shortcut2
+        assertThat(widgetItemInTable).hasSize(5);
+        assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
+        assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3);
+        assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4);
+        assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
+        assertThat(widgetItemInTable.get(4)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
+    }
+
+    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));
+
+        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);
+                    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));
+                }
+        );
+        mWidget1x1 = widgetItems.get(0);
+        mWidget2x2 = widgetItems.get(1);
+        mWidget2x3 = widgetItems.get(2);
+        mWidget2x4 = widgetItems.get(3);
+        mWidget4x4 = widgetItems.get(4);
+    }
+
+    private void initTestShortcuts() {
+        PackageManager packageManager = mContext.getPackageManager();
+        mShortcut1 = new WidgetItem(new TestShortcutConfigActivityInfo(
+                ComponentName.createRelative(TEST_PACKAGE, ".shortcut1"), UserHandle.CURRENT),
+                mIconCache, packageManager);
+        mShortcut2 = new WidgetItem(new TestShortcutConfigActivityInfo(
+                ComponentName.createRelative(TEST_PACKAGE, ".shortcut2"), UserHandle.CURRENT),
+                mIconCache, packageManager);
+        mShortcut3 = new WidgetItem(new TestShortcutConfigActivityInfo(
+                ComponentName.createRelative(TEST_PACKAGE, ".shortcut3"), UserHandle.CURRENT),
+                mIconCache, packageManager);
+
+    }
+
+    private final class TestShortcutConfigActivityInfo extends ShortcutConfigActivityInfo {
+
+        TestShortcutConfigActivityInfo(ComponentName componentName, UserHandle user) {
+            super(componentName, user);
+        }
+
+        @Override
+        public Drawable getFullResIcon(IconCache cache) {
+            return null;
+        }
+
+        @Override
+        public CharSequence getLabel(PackageManager pm) {
+            return null;
+        }
+    }
+}
diff --git a/robolectric_tests/unstaged/SettingsCacheTest.java b/robolectric_tests/unstaged/SettingsCacheTest.java
new file mode 100644
index 0000000..fbf4c63
--- /dev/null
+++ b/robolectric_tests/unstaged/SettingsCacheTest.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.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 ce37a30..4979b40 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
@@ -35,8 +36,6 @@
 import androidx.annotation.IntDef;
 
 import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
@@ -60,10 +59,12 @@
             TYPE_SNACKBAR,
             TYPE_LISTENER,
             TYPE_ALL_APPS_EDU,
-
+            TYPE_DRAG_DROP_POPUP,
             TYPE_TASK_MENU,
             TYPE_OPTIONS_POPUP,
-            TYPE_ICON_SURFACE
+            TYPE_ICON_SURFACE,
+            TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP,
+            TYPE_WIDGETS_EDUCATION_DIALOG
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FloatingViewType {}
@@ -77,22 +78,27 @@
     public static final int TYPE_SNACKBAR = 1 << 7;
     public static final int TYPE_LISTENER = 1 << 8;
     public static final int TYPE_ALL_APPS_EDU = 1 << 9;
+    public static final int TYPE_DRAG_DROP_POPUP = 1 << 10;
 
     // Popups related to quickstep UI
-    public static final int TYPE_TASK_MENU = 1 << 10;
-    public static final int TYPE_OPTIONS_POPUP = 1 << 11;
-    public static final int TYPE_ICON_SURFACE = 1 << 12;
+    public static final int TYPE_TASK_MENU = 1 << 11;
+    public static final int TYPE_OPTIONS_POPUP = 1 << 12;
+    public static final int TYPE_ICON_SURFACE = 1 << 13;
+
+    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_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_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
+            | TYPE_WIDGETS_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_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_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
@@ -104,7 +110,7 @@
     // These view all have particular operation associated with swipe down interaction.
     public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
             TYPE_WIDGETS_FULL_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_ON_BOARD_POPUP |
-            TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU ;
+            TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU | TYPE_DRAG_DROP_POPUP;
 
     protected boolean mIsOpen;
 
@@ -126,10 +132,9 @@
     }
 
     public final void close(boolean animate) {
-        animate &= Utilities.areAnimationsEnabled(getContext());
+        animate &= areAnimatorsEnabled();
         if (mIsOpen) {
-            BaseActivity.fromContext(getContext()).getUserEventDispatcher()
-                    .resetElapsedContainerMillis("container closed");
+            // Add to WW logging
         }
         handleClose(animate);
         mIsOpen = false;
@@ -144,12 +149,6 @@
     public void addHintCloseAnim(
             float distanceToMove, Interpolator interpolator, PendingAnimation target) { }
 
-    public abstract void logActionCommand(int command);
-
-    public int getLogContainerType() {
-        return ContainerType.DEFAULT_CONTAINERTYPE;
-    }
-
     public final boolean isOpen() {
         return mIsOpen;
     }
@@ -158,7 +157,6 @@
 
     /** @return Whether the back is consumed. If false, Launcher will handle the back as well. */
     public boolean onBackPressed() {
-        logActionCommand(Action.Command.BACK);
         close(true);
         return true;
     }
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index 9b6166f..3db456c 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -3,15 +3,25 @@
 import android.content.ComponentName;
 import android.content.Context;
 
-import com.android.launcher3.util.ResourceBasedOverride;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
 
-public class AppFilter implements ResourceBasedOverride {
+/**
+ * Utility class to filter out components from various lists
+ */
+public class AppFilter {
 
-    public static AppFilter newInstance(Context context) {
-        return Overrides.getObject(AppFilter.class, context, R.string.app_filter_class);
+    private final Set<ComponentName> mFilteredComponents;
+
+    public AppFilter(Context context) {
+        mFilteredComponents = Arrays.stream(
+                context.getResources().getStringArray(R.array.filtered_components))
+                .map(ComponentName::unflattenFromString)
+                .collect(Collectors.toSet());
     }
 
     public boolean shouldShowApp(ComponentName app) {
-        return true;
+        return !mFilteredComponents.contains(app);
     }
 }
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 6f7b684..ee71146 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -1,29 +1,42 @@
 package com.android.launcher3;
 
+import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
+
 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;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_STARTED;
 import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
 import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
 
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
-import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.Px;
 
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.util.FocusLogic;
-import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.util.WidgetSizes;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -33,16 +46,10 @@
     private static final float DIMMED_HANDLE_ALPHA = 0f;
     private static final float RESIZE_THRESHOLD = 0.66f;
 
+    private static final String KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
+            "launcher.reconfigurable_widget_education_tip_seen";
     private static final Rect sTmpRect = new Rect();
 
-    // Represents the cell size on the grid in the two orientations.
-    private static final MainThreadInitializedObject<Point[]> CELL_SIZE =
-            new MainThreadInitializedObject<>(c -> {
-                InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
-                return new Point[] {inv.landscapeProfile.getCellSize(),
-                        inv.portraitProfile.getCellSize()};
-            });
-
     private static final int HANDLE_COUNT = 4;
     private static final int INDEX_LEFT = 0;
     private static final int INDEX_TOP = 1;
@@ -55,10 +62,27 @@
 
     private final View[] mDragHandles = new View[HANDLE_COUNT];
     private final List<Rect> mSystemGestureExclusionRects = new ArrayList<>(HANDLE_COUNT);
+    private final OnAttachStateChangeListener mWidgetViewAttachStateChangeListener =
+            new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View view) {
+                    // Do nothing
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View view) {
+                    // When the app widget view is detached, we should close the resize frame.
+                    // An example is when the dragging starts, the widget view is detached from
+                    // CellLayout and then reattached to DragLayout.
+                    close(false);
+                }
+            };
+
 
     private LauncherAppWidgetHostView mWidgetView;
     private CellLayout mCellLayout;
     private DragLayer mDragLayer;
+    private ImageButton mReconfigureButton;
 
     private Rect mWidgetPadding;
 
@@ -77,17 +101,22 @@
     private final IntRange mDeltaYRange = new IntRange();
     private final IntRange mBaselineY = new IntRange();
 
+    private final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
+
     private boolean mLeftBorderActive;
     private boolean mRightBorderActive;
     private boolean mTopBorderActive;
     private boolean mBottomBorderActive;
 
-    private int mResizeMode;
+    private boolean mHorizontalResizeActive;
+    private boolean mVerticalResizeActive;
 
     private int mRunningHInc;
     private int mRunningVInc;
     private int mMinHSpan;
     private int mMinVSpan;
+    private int mMaxHSpan;
+    private int mMaxVSpan;
     private int mDeltaX;
     private int mDeltaY;
     private int mDeltaXAddOn;
@@ -126,10 +155,10 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        ViewGroup content = (ViewGroup) getChildAt(0);
-        for (int i = 0; i < HANDLE_COUNT; i ++) {
-            mDragHandles[i] = content.getChildAt(i);
-        }
+        mDragHandles[INDEX_LEFT] = findViewById(R.id.widget_resize_left_handle);
+        mDragHandles[INDEX_TOP] = findViewById(R.id.widget_resize_top_handle);
+        mDragHandles[INDEX_RIGHT] = findViewById(R.id.widget_resize_right_handle);
+        mDragHandles[INDEX_BOTTOM] = findViewById(R.id.widget_resize_bottom_handle);
     }
 
     @Override
@@ -152,6 +181,15 @@
         DragLayer dl = launcher.getDragLayer();
         AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater()
                 .inflate(R.layout.app_widget_resize_frame, dl, false);
+        if (widget.hasEnforcedCornerRadius()) {
+            float enforcedCornerRadius = widget.getEnforcedCornerRadius();
+            ImageView imageView = frame.findViewById(R.id.widget_resize_frame);
+            Drawable d = imageView.getDrawable();
+            if (d instanceof GradientDrawable) {
+                GradientDrawable gd = (GradientDrawable) d.mutate();
+                gd.setCornerRadius(enforcedCornerRadius);
+            }
+        }
         frame.setupForWidget(widget, cellLayout, dl);
         ((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true;
 
@@ -163,43 +201,96 @@
     private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
             DragLayer dragLayer) {
         mCellLayout = cellLayout;
+        if (mWidgetView != null) {
+            mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
+        }
         mWidgetView = widgetView;
+        mWidgetView.addOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
         LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
                 widgetView.getAppWidgetInfo();
-        mResizeMode = info.resizeMode;
         mDragLayer = dragLayer;
 
         mMinHSpan = info.minSpanX;
         mMinVSpan = info.minSpanY;
+        mMaxHSpan = info.maxSpanX;
+        mMaxVSpan = info.maxSpanY;
 
-        mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
+        mWidgetPadding = getDefaultPaddingForWidget(getContext(),
                 widgetView.getAppWidgetInfo().provider, null);
 
-        if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
+        // 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;
+        if (!mVerticalResizeActive) {
             mDragHandles[INDEX_TOP].setVisibility(GONE);
             mDragHandles[INDEX_BOTTOM].setVisibility(GONE);
-        } else if (mResizeMode == AppWidgetProviderInfo.RESIZE_VERTICAL) {
+        }
+        mHorizontalResizeActive = (info.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0
+                && mMinHSpan < idp.numColumns && mMaxHSpan > 1;
+        if (!mHorizontalResizeActive) {
             mDragHandles[INDEX_LEFT].setVisibility(GONE);
             mDragHandles[INDEX_RIGHT].setVisibility(GONE);
         }
 
+        mReconfigureButton = (ImageButton) findViewById(R.id.widget_reconfigure_button);
+        if (info.isReconfigurable()) {
+            mReconfigureButton.setVisibility(VISIBLE);
+            mReconfigureButton.setOnClickListener(view -> {
+                mLauncher.setWaitingForResult(
+                        PendingRequestArgs.forWidgetInfo(
+                                mWidgetView.getAppWidgetId(),
+                                // Widget add handler is null since we're reconfiguring an existing
+                                // widget.
+                                /* widgetHandler= */ null,
+                                (ItemInfo) mWidgetView.getTag()));
+                mLauncher
+                    .getAppWidgetHost()
+                    .startConfigActivity(
+                            mLauncher,
+                            mWidgetView.getAppWidgetId(),
+                            Launcher.REQUEST_RECONFIGURE_APPWIDGET);
+            });
+            if (!hasSeenReconfigurableWidgetEducationTip()) {
+                post(() -> {
+                    if (showReconfigurableWidgetEducationTip() != null) {
+                        mLauncher.getSharedPrefs().edit()
+                                .putBoolean(KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN,
+                                        true).apply();
+                    }
+                });
+            }
+        }
+
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) mWidgetView.getLayoutParams();
+        ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag();
+        lp.cellX = lp.tmpCellX = widgetInfo.cellX;
+        lp.cellY = lp.tmpCellY = widgetInfo.cellY;
+        lp.cellHSpan = widgetInfo.spanX;
+        lp.cellVSpan = widgetInfo.spanY;
+        lp.isLockedToGrid = true;
+
         // When we create the resize frame, we first mark all cells as unoccupied. The appropriate
         // cells (same if not resized, or different) will be marked as occupied when the resize
         // frame is dismissed.
         mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
 
+        mLauncher.getStatsLogManager()
+                .logger()
+                .withInstanceId(logInstanceId)
+                .withItemInfo(widgetInfo)
+                .log(LAUNCHER_WIDGET_RESIZE_STARTED);
+
         setOnKeyListener(this);
     }
 
     public boolean beginResizeIfPointInRegion(int x, int y) {
-        boolean horizontalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0;
-        boolean verticalActive = (mResizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0;
-
-        mLeftBorderActive = (x < mTouchTargetWidth) && horizontalActive;
-        mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && horizontalActive;
-        mTopBorderActive = (y < mTouchTargetWidth + mTopTouchRegionAdjustment) && verticalActive;
+        mLeftBorderActive = (x < mTouchTargetWidth) && mHorizontalResizeActive;
+        mRightBorderActive = (x > getWidth() - mTouchTargetWidth) && mHorizontalResizeActive;
+        mTopBorderActive = (y < mTouchTargetWidth + mTopTouchRegionAdjustment)
+                && mVerticalResizeActive;
         mBottomBorderActive = (y > getHeight() - mTouchTargetWidth + mBottomTouchRegionAdjustment)
-                && verticalActive;
+                && mVerticalResizeActive;
 
         boolean anyBordersActive = mLeftBorderActive || mRightBorderActive
                 || mTopBorderActive || mBottomBorderActive;
@@ -279,8 +370,9 @@
      *  Based on the current deltas, we determine if and how to resize the widget.
      */
     private void resizeWidgetIfNeeded(boolean onDismiss) {
-        float xThreshold = mCellLayout.getCellWidth();
-        float yThreshold = mCellLayout.getCellHeight();
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        float xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacingPx;
+        float yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacingPx;
 
         int hSpanInc = getSpanIncrement((mDeltaX + mDeltaXAddOn) / xThreshold - mRunningHInc);
         int vSpanInc = getSpanIncrement((mDeltaY + mDeltaYAddOn) / yThreshold - mRunningVInc);
@@ -301,7 +393,7 @@
         // expandability.
         mTempRange1.set(cellX, spanX + cellX);
         int hSpanDelta = mTempRange1.applyDeltaAndBound(mLeftBorderActive, mRightBorderActive,
-                hSpanInc, mMinHSpan, mCellLayout.getCountX(), mTempRange2);
+                hSpanInc, mMinHSpan, mMaxHSpan, mCellLayout.getCountX(), mTempRange2);
         cellX = mTempRange2.start;
         spanX = mTempRange2.size();
         if (hSpanDelta != 0) {
@@ -310,7 +402,7 @@
 
         mTempRange1.set(cellY, spanY + cellY);
         int vSpanDelta = mTempRange1.applyDeltaAndBound(mTopBorderActive, mBottomBorderActive,
-                vSpanInc, mMinVSpan, mCellLayout.getCountY(), mTempRange2);
+                vSpanInc, mMinVSpan, mMaxVSpan, mCellLayout.getCountY(), mTempRange2);
         cellY = mTempRange2.start;
         spanY = mTempRange2.size();
         if (vSpanDelta != 0) {
@@ -344,48 +436,29 @@
             mRunningHInc += hSpanDelta;
 
             if (!onDismiss) {
-                updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY);
+                WidgetSizes.updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY);
             }
         }
         mWidgetView.requestLayout();
     }
 
-    public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
-            int spanX, int spanY) {
-        getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
-        widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
-                sTmpRect.right, sTmpRect.bottom);
-    }
-
-    public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, Rect rect) {
-        if (rect == null) {
-            rect = new Rect();
-        }
-        final float density = context.getResources().getDisplayMetrics().density;
-        final Point[] cellSize = CELL_SIZE.get(context);
-
-        // Compute landscape size
-        int landWidth = (int) ((spanX * cellSize[0].x) / density);
-        int landHeight = (int) ((spanY * cellSize[0].y) / density);
-
-        // Compute portrait size
-        int portWidth = (int) ((spanX * cellSize[1].x) / density);
-        int portHeight = (int) ((spanY * cellSize[1].y) / density);
-        rect.set(portWidth, landHeight, landWidth, portHeight);
-        return rect;
-    }
-
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
         // We are done with resizing the widget. Save the widget size & position to LauncherModel
         resizeWidgetIfNeeded(true);
+        mLauncher.getStatsLogManager()
+                .logger()
+                .withInstanceId(logInstanceId)
+                .withItemInfo((ItemInfo) mWidgetView.getTag())
+                .log(LAUNCHER_WIDGET_RESIZE_COMPLETED);
     }
 
     private void onTouchUp() {
-        int xThreshold = mCellLayout.getCellWidth();
-        int yThreshold = mCellLayout.getCellHeight();
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        int xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacingPx;
+        int yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacingPx;
 
         mDeltaXAddOn = mRunningHInc * xThreshold;
         mDeltaYAddOn = mRunningVInc * yThreshold;
@@ -476,7 +549,7 @@
     @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         // Clear the frame and give focus to the widget host view when a directional key is pressed.
-        if (FocusLogic.shouldConsume(keyCode)) {
+        if (shouldConsume(keyCode)) {
             close(false);
             mWidgetView.requestFocus();
             return true;
@@ -500,6 +573,13 @@
         return false;
     }
 
+    private boolean isTouchOnReconfigureButton(MotionEvent ev) {
+        int xFrame = (int) ev.getX() - getLeft();
+        int yFrame = (int) ev.getY() - getTop();
+        mReconfigureButton.getHitRect(sTmpRect);
+        return sTmpRect.contains(xFrame, yFrame);
+    }
+
     @Override
     public boolean onControllerTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
@@ -527,6 +607,11 @@
         if (ev.getAction() == MotionEvent.ACTION_DOWN && handleTouchDown(ev)) {
             return true;
         }
+        // Keep the resize frame open but let a click on the reconfigure button fall through to the
+        // button's OnClickListener.
+        if (isTouchOnReconfigureButton(ev)) {
+            return false;
+        }
         close(false);
         return false;
     }
@@ -534,11 +619,9 @@
     @Override
     protected void handleClose(boolean animate) {
         mDragLayer.removeView(this);
-    }
-
-    @Override
-    public void logActionCommand(int command) {
-        // TODO: Log this case.
+        if (mWidgetView != null) {
+            mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
+        }
     }
 
     @Override
@@ -581,12 +664,15 @@
          * @param minSize minimum size after with the moving edge should not be shifted any further.
          *                For eg, if delta = -3 when moving the endEdge brings the size to less than
          *                minSize, only delta = -2 will applied
+         * @param maxSize maximum size after with the moving edge should not be shifted any further.
+         *                For eg, if delta = -3 when moving the endEdge brings the size to greater
+         *                than maxSize, only delta = -2 will applied
          * @param maxEnd The maximum value to the end edge (start edge is always restricted to 0)
          * @return the amount of increase when endEdge was moves and the amount of decrease when
          * the start edge was moved.
          */
         public int applyDeltaAndBound(boolean moveStart, boolean moveEnd, int delta,
-                int minSize, int maxEnd, IntRange out) {
+                int minSize, int maxSize, int maxEnd, IntRange out) {
             applyDelta(moveStart, moveEnd, delta, out);
             if (out.start < 0) {
                 out.start = 0;
@@ -601,7 +687,45 @@
                     out.end = out.start + minSize;
                 }
             }
+            if (out.size() > maxSize) {
+                if (moveStart) {
+                    out.start = out.end - maxSize;
+                } else if (moveEnd) {
+                    out.end = out.start + maxSize;
+                }
+            }
             return moveEnd ? out.size() - size() : size() - out.size();
         }
     }
+
+    /**
+     * Returns true only if this utility class handles the key code.
+     */
+    public static boolean shouldConsume(int keyCode) {
+        return (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
+                || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN
+                || keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END
+                || keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN);
+    }
+
+    @Nullable private ArrowTipView showReconfigurableWidgetEducationTip() {
+        Rect rect = new Rect();
+        if (!mReconfigureButton.getGlobalVisibleRect(rect)) {
+            return null;
+        }
+        @Px int tipMargin = mLauncher.getResources()
+                .getDimensionPixelSize(R.dimen.widget_reconfigure_tip_top_margin);
+        return new ArrowTipView(mLauncher, /* isPointingUp= */ true)
+                .showAroundRect(
+                        getContext().getString(R.string.reconfigurable_widget_education_tip),
+                        /* arrowXCoord= */ rect.left + mReconfigureButton.getWidth() / 2,
+                        /* rect= */ rect,
+                        /* margin= */ tipMargin);
+    }
+
+    private boolean hasSeenReconfigurableWidgetEducationTip() {
+        return mLauncher.getSharedPrefs()
+                .getBoolean(KEY_RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN, false)
+                || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+    }
 }
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index a55c90d..75e89b2 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -21,6 +21,7 @@
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
 
 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
 
@@ -86,13 +87,12 @@
             long mainProfileId = UserCache.INSTANCE.get(context)
                     .getSerialNumberForUser(myUserHandle());
             String oldWidgetId = Integer.toString(oldWidgetIds[i]);
-            int result = new ContentWriter(context, new ContentWriter.CommitParams(
-                    "appWidgetId=? and (restored & 1) = 1 and profileId=?",
-                    new String[] { oldWidgetId, Long.toString(mainProfileId) }))
+            final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
+            final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) };
+            int result = new ContentWriter(context, new ContentWriter.CommitParams(where, args))
                     .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
                     .put(LauncherSettings.Favorites.RESTORED, state)
                     .commit();
-
             if (result == 0) {
                 Cursor cursor = cr.query(Favorites.CONTENT_URI,
                         new String[] {Favorites.APPWIDGET_ID},
@@ -106,6 +106,12 @@
                     cursor.close();
                 }
             }
+            // attempt to update widget id in backup table as well
+            new ContentWriter(context, ContentWriter.CommitParams.backupCommitParams(
+                    "appWidgetId=? and profileId=?", args))
+                    .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
+                    .put(LauncherSettings.Favorites.RESTORED, state)
+                    .commit();
         }
 
         LauncherAppState app = LauncherAppState.getInstanceNoCreate();
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 432073e..5b655a4 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -89,7 +89,7 @@
 
         // Try with grid size and hotseat count
         String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
-                grid.numColumns, grid.numRows, grid.numHotseatIcons);
+                grid.numColumns, grid.numRows, grid.numDatabaseHotseatIcons);
         int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
 
         // Try with only grid size
@@ -658,7 +658,7 @@
         }
     }
 
-    protected static void beginDocument(XmlPullParser parser, String firstElementName)
+    public static void beginDocument(XmlPullParser parser, String firstElementName)
             throws XmlPullParserException, IOException {
         int type;
         while ((type = parser.next()) != XmlPullParser.START_TAG
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 310c306..9778b61 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -17,7 +17,7 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
@@ -31,17 +31,15 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 
 import androidx.annotation.IntDef;
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.ScrimView;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -82,7 +80,6 @@
             new ArrayList<>();
 
     protected DeviceProfile mDeviceProfile;
-    protected UserEventDispatcher mUserEventDispatcher;
     protected StatsLogManager mStatsLogManager;
     protected SystemUiController mSystemUiController;
 
@@ -135,6 +132,7 @@
 
     private final ViewCache mViewCache = new ViewCache();
 
+    @Override
     public ViewCache getViewCache() {
         return mViewCache;
     }
@@ -144,22 +142,16 @@
         return mDeviceProfile;
     }
 
-    public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {}
-
-    public final StatsLogManager getStatsLogManager() {
+    /**
+     * Returns {@link StatsLogManager} for user event logging.
+     */
+    public StatsLogManager getStatsLogManager() {
         if (mStatsLogManager == null) {
             mStatsLogManager = StatsLogManager.newInstance(this);
         }
         return mStatsLogManager;
     }
 
-    public final UserEventDispatcher getUserEventDispatcher() {
-        if (mUserEventDispatcher == null) {
-            mUserEventDispatcher = UserEventDispatcher.newInstance(this);
-        }
-        return mUserEventDispatcher;
-    }
-
     public SystemUiController getSystemUiController() {
         if (mSystemUiController == null) {
             mSystemUiController = new SystemUiController(getWindow());
@@ -167,6 +159,10 @@
         return mSystemUiController;
     }
 
+    public ScrimView getScrimView() {
+        return null;
+    }
+
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
@@ -207,7 +203,7 @@
 
         // Reset the overridden sysui flags used for the task-swipe launch animation, this is a
         // catch all for if we do not get resumed (and therefore not paused below)
-        getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
+        getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
     }
 
     @Override
@@ -219,7 +215,7 @@
         // here instead of at the end of the animation because the start of the new activity does
         // not happen immediately, which would cause us to reset to launcher's sysui flags and then
         // back to the new app (causing a flash)
-        getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
+        getSystemUiController().updateUiState(UI_STATE_FULLSCREEN_TASK, 0);
     }
 
     @Override
@@ -289,7 +285,7 @@
     /**
      * Used to set the override visibility state, used only to handle the transition home with the
      * recents animation.
-     * @see QuickstepAppTransitionManagerImpl#createWallpaperOpenRunner
+     * @see QuickstepTransitionManager#createWallpaperOpenRunner
      */
     public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
         mForceInvisible |= flag;
@@ -342,7 +338,7 @@
     public static <T extends BaseActivity> T fromContext(Context context) {
         if (context instanceof BaseActivity) {
             return (T) context;
-        } else if (context instanceof ContextThemeWrapper) {
+        } else if (context instanceof ContextWrapper) {
             return fromContext(((ContextWrapper) context).getBaseContext());
         } else {
             throw new IllegalArgumentException("Cannot find BaseActivity in parent tree");
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index f2a5c65..2c76e52 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -17,16 +17,22 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
+import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.app.ActivityOptions;
+import android.app.WallpaperColors;
+import android.app.WallpaperManager;
+import android.app.WallpaperManager.OnColorsChangedListener;
 import android.content.ActivityNotFoundException;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Process;
 import android.os.StrictMode;
@@ -40,20 +46,24 @@
 import android.view.WindowMetrics;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
-import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.uioverrides.WallpaperColorInfo;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.launcher3.util.DefaultDisplay.DisplayInfoChangeListener;
-import com.android.launcher3.util.DefaultDisplay.Info;
+import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.WindowBounds;
@@ -61,8 +71,9 @@
 /**
  * Extension of BaseActivity allowing support for drag-n-drop
  */
+@SuppressWarnings("NewApi")
 public abstract class BaseDraggingActivity extends BaseActivity
-        implements WallpaperColorInfo.OnChangeListener, DisplayInfoChangeListener {
+        implements OnColorsChangedListener, DisplayInfoChangeListener {
 
     private static final String TAG = "BaseDraggingActivity";
 
@@ -74,6 +85,7 @@
     protected boolean mIsSafeModeEnabled;
 
     private Runnable mOnStartCallback;
+    private RunnableList mOnResumeCallbacks = new RunnableList();
 
     private int mThemeRes = R.style.AppTheme;
 
@@ -81,13 +93,15 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-
         mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
                 () -> getPackageManager().isSafeMode());
-        DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
+        DisplayController.INSTANCE.get(this).addChangeListener(this);
 
         // Update theme
-        WallpaperColorInfo.INSTANCE.get(this).addOnChangeListener(this);
+        if (Utilities.ATLEAST_P) {
+            getSystemService(WallpaperManager.class)
+                    .addOnColorsChangedListener(this, MAIN_EXECUTOR.getHandler());
+        }
         int themeRes = Themes.getActivityThemeRes(this);
         if (themeRes != mThemeRes) {
             mThemeRes = themeRes;
@@ -96,7 +110,17 @@
     }
 
     @Override
-    public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
+    protected void onResume() {
+        super.onResume();
+        mOnResumeCallbacks.executeAllAndClear();
+    }
+
+    public void addOnResumeCallback(Runnable callback) {
+        mOnResumeCallbacks.add(callback);
+    }
+
+    @Override
+    public void onColorsChanged(WallpaperColors wallpaperColors, int which) {
         updateTheme();
     }
 
@@ -147,21 +171,35 @@
         return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
     }
 
-    public final Bundle getActivityLaunchOptionsAsBundle(View v) {
-        ActivityOptions activityOptions = getActivityLaunchOptions(v);
-        return activityOptions == null ? null : activityOptions.toBundle();
+    @NonNull
+    public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
+        int left = 0, top = 0;
+        int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+        if (v instanceof BubbleTextView) {
+            // Launch from center of icon, not entire view
+            Drawable icon = ((BubbleTextView) v).getIcon();
+            if (icon != null) {
+                Rect bounds = icon.getBounds();
+                left = (width - bounds.width()) / 2;
+                top = v.getPaddingTop();
+                width = bounds.width();
+                height = bounds.height();
+            }
+        }
+        ActivityOptions options =
+                ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+        RunnableList callback = new RunnableList();
+        addOnResumeCallback(callback::executeAllAndDestroy);
+        return new ActivityOptionsWrapper(options, callback);
     }
 
-    public abstract ActivityOptions getActivityLaunchOptions(View v);
-
-    public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
-            @Nullable String sourceContainer) {
+    public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
         if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
             return false;
         }
 
-        Bundle optsBundle = (v != null) ? getActivityLaunchOptionsAsBundle(v) : null;
+        Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v, item).toBundle() : null;
         UserHandle user = item == null ? null : item.user;
 
         // Prepare intent
@@ -176,25 +214,20 @@
                     && !((WorkspaceItemInfo) item).isPromise();
             if (isShortcut) {
                 // Shortcuts need some special checks due to legacy reasons.
-                startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
+                startShortcutIntentSafely(intent, optsBundle, item);
             } else if (user == null || user.equals(Process.myUserHandle())) {
                 // Could be launching some bookkeeping activity
                 startActivity(intent, optsBundle);
-                AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
-                        Process.myUserHandle(), sourceContainer);
             } else {
                 getSystemService(LauncherApps.class).startMainActivity(
                         intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
-                AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
-                        sourceContainer);
             }
-            getUserEventDispatcher().logAppLaunch(v, intent, user);
             if (item != null) {
                 InstanceId instanceId = new InstanceIdSequence().newInstanceId();
                 logAppLaunch(item, instanceId);
             }
             return true;
-        } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
+        } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
             Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
         }
@@ -206,8 +239,7 @@
                 .log(LAUNCHER_APP_LAUNCH_TAP);
     }
 
-    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
-            @Nullable String sourceContainer) {
+    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
         try {
             StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
             try {
@@ -221,8 +253,6 @@
                     String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
                     String packageName = intent.getPackage();
                     startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
-                    AppLaunchTracker.INSTANCE.get(this).onStartShortcut(packageName, id, info.user,
-                            sourceContainer);
                 } else {
                     // Could be launching some bookkeeping activity
                     startActivity(intent, optsBundle);
@@ -254,8 +284,10 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        WallpaperColorInfo.INSTANCE.get(this).removeOnChangeListener(this);
-        DefaultDisplay.INSTANCE.get(this).removeChangeListener(this);
+        if (Utilities.ATLEAST_P) {
+            getSystemService(WallpaperManager.class).removeOnColorsChangedListener(this);
+        }
+        DisplayController.INSTANCE.get(this).removeChangeListener(this);
     }
 
     public void runOnceOnStart(Runnable action) {
@@ -273,7 +305,7 @@
     }
 
     @Override
-    public void onDisplayInfoChanged(Info info, int flags) {
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
         if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.updateIsSeascape(this)) {
             reapplyUi();
         }
@@ -300,4 +332,12 @@
         display.getSize(mwSize);
         return new WindowBounds(new Rect(0, 0, mwSize.x, mwSize.y), new Rect());
     }
+
+    /**
+     * Creates and returns {@link SearchAdapterProvider} for build variant specific search result
+     * views
+     */
+    public SearchAdapterProvider createSearchAdapterProvider(AllAppsContainerView allapps) {
+        return new DefaultSearchAdapterProvider(this, allapps);
+    }
 }
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 8eceec0..9369bdc 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -16,21 +16,17 @@
 
 package com.android.launcher3;
 
-import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
-
 import android.content.Context;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
 
@@ -194,13 +190,20 @@
         if (isLayoutSuppressed()) info.setScrollable(false);
     }
 
-    @Override
-    public void setLayoutFrozen(boolean frozen) {
-        final boolean changing = frozen != isLayoutSuppressed();
-        super.setLayoutFrozen(frozen);
-        if (changing) {
-            ActivityContext.lookupContext(getContext()).getDragLayer()
-                    .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
+    /**
+     * Scrolls this recycler view to the top.
+     */
+    public void scrollToTop() {
+        if (mScrollbar != null) {
+            mScrollbar.reattachThumbToScroll();
         }
+        if (getLayoutManager() instanceof LinearLayoutManager) {
+            LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+            if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
+                // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
+                return;
+            }
+        }
+        scrollToPosition(0);
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 198f13d..ddbd425 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
@@ -43,7 +42,9 @@
 import android.view.ViewDebug;
 import android.widget.TextView;
 
-import com.android.launcher3.Launcher.OnResumeCallback;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DraggableView;
@@ -52,13 +53,15 @@
 import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.DotRenderer;
-import com.android.launcher3.icons.IconCache.IconLoadRequest;
+import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.PlaceHolderIconDrawable;
+import com.android.launcher3.icons.cache.HandlerRunnable;
 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.PackageItemInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.SearchActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.views.ActivityContext;
@@ -71,14 +74,18 @@
  * because we want to make the bubble taller than the text and TextView's clip is
  * too aggressive.
  */
-public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
         IconLabelDotView, DraggableView, Reorderable {
 
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
     private static final int DISPLAY_FOLDER = 2;
+    protected static final int DISPLAY_TASKBAR = 5;
+    private static final int DISPLAY_SEARCH_RESULT = 6;
+    private static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
 
-    private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
+    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);
@@ -113,10 +120,10 @@
     };
 
     private final ActivityContext mActivity;
-    private Drawable mIcon;
+    private FastBitmapDrawable mIcon;
     private boolean mCenterVertically;
 
-    private final int mDisplay;
+    protected final int mDisplay;
 
     private final CheckLongPressHelper mLongPressHelper;
 
@@ -134,7 +141,7 @@
     private DotInfo mDotInfo;
     private DotRenderer mDotRenderer;
     @ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
-    private DotRenderer.DrawParams mDotParams;
+    protected DotRenderer.DrawParams mDotParams;
     private Animator mDotScaleAnim;
     private boolean mForceHideDot;
 
@@ -145,7 +152,9 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mDisableRelayout = false;
 
-    private IconLoadRequest mIconLoadRequest;
+    private HandlerRunnable mIconLoadRequest;
+
+    private boolean mEnableIconUpdateAnimation = false;
 
     public BubbleTextView(Context context) {
         this(context, null, 0);
@@ -170,6 +179,7 @@
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
             setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
             defaultIconSize = grid.iconSizePx;
+            setCenterVertically(grid.isScalableGrid);
         } else if (mDisplay == DISPLAY_ALL_APPS) {
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
             setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
@@ -178,6 +188,13 @@
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
             setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
             defaultIconSize = grid.folderChildIconSizePx;
+        } else if (mDisplay == DISPLAY_SEARCH_RESULT) {
+            defaultIconSize = getResources().getDimensionPixelSize(R.dimen.search_row_icon_size);
+        } else if (mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
+            defaultIconSize = getResources().getDimensionPixelSize(
+                    R.dimen.search_row_small_icon_size);
+        } else if (mDisplay == DISPLAY_TASKBAR) {
+            defaultIconSize = grid.iconSizePx;
         } else {
             // widget_selection or shortcut_popup
             defaultIconSize = grid.iconSizePx;
@@ -235,6 +252,7 @@
         mDotScaleAnim.start();
     }
 
+    @UiThread
     public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
         applyFromWorkspaceItem(info, false);
     }
@@ -251,16 +269,16 @@
         }
     }
 
+    @UiThread
     public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info);
         setTag(info);
-        if (promiseStateChanged || (info.hasPromiseIconUi())) {
-            applyPromiseState(promiseStateChanged);
-        }
-
+        applyLoadingState(promiseStateChanged);
         applyDotState(info, false /* animate */);
+        setDownloadStateContentDescription(info, info.getProgressLevel());
     }
 
+    @UiThread
     public void applyFromApplicationInfo(AppInfo info) {
         applyIconAndLabel(info);
 
@@ -270,27 +288,49 @@
         // Verify high res immediately
         verifyHighRes();
 
-        if (info instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
-            applyProgressLevel(promiseAppInfo.level);
+        if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+            applyProgressLevel();
         }
         applyDotState(info, false /* animate */);
+        setDownloadStateContentDescription(info, info.getProgressLevel());
     }
 
-    public void applyFromPackageItemInfo(PackageItemInfo info) {
+    /**
+     * Apply label and tag using a generic {@link ItemInfoWithIcon}
+     */
+    @UiThread
+    public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {
         applyIconAndLabel(info);
         // We don't need to check the info since it's not a WorkspaceItemInfo
         super.setTag(info);
 
         // Verify high res immediately
         verifyHighRes();
+
+        setDownloadStateContentDescription(info, info.getProgressLevel());
     }
 
-    private void applyIconAndLabel(ItemInfoWithIcon info) {
-        FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
-        mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
+    /**
+     * Apply label and tag using a {@link SearchActionItemInfo}
+     */
+    @UiThread
+    public void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) {
+        applyIconAndLabel(searchActionItemInfo);
+        setTag(searchActionItemInfo);
+    }
+
+    @UiThread
+    protected void applyIconAndLabel(ItemInfoWithIcon info) {
+        boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER;
+        FastBitmapDrawable iconDrawable = info.newIcon(getContext(), useTheme);
+        mDotParams.color = IconPalette.getMutedColor(iconDrawable.getIconColor(), 0.54f);
 
         setIcon(iconDrawable);
+        applyLabel(info);
+    }
+
+    @UiThread
+    private void applyLabel(ItemInfoWithIcon info) {
         setText(info.title);
         if (info.contentDescription != null) {
             setContentDescription(info.isDisabled()
@@ -323,7 +363,7 @@
     }
 
     /** Returns the icon for this view. */
-    public Drawable getIcon() {
+    public FastBitmapDrawable getIcon() {
         return mIcon;
     }
 
@@ -367,13 +407,6 @@
         }
     }
 
-    @Override
-    public void onLauncherResume() {
-        // Reset the pressed state of icon that was locked in the press state while activity
-        // was launching
-        setStayPressed(false);
-    }
-
     void clearPressedBackground() {
         setPressed(false);
         setStayPressed(false);
@@ -404,12 +437,14 @@
 
     /**
      * Draws the notification dot in the top right corner of the icon bounds.
+     *
      * @param canvas The canvas to draw to.
      */
     protected void drawDotIfNecessary(Canvas canvas) {
         if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
             getIconBounds(mDotParams.iconBounds);
-            Utilities.scaleRectAboutCenter(mDotParams.iconBounds, IconShape.getNormalizationScale());
+            Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
+                    IconShape.getNormalizationScale());
             final int scrollX = getScrollX();
             final int scrollY = getScrollY();
             canvas.translate(scrollX, scrollY);
@@ -448,6 +483,14 @@
         outBounds.set(left, top, right, bottom);
     }
 
+
+    /**
+     * Sets whether to vertically center the content.
+     */
+    public void setCenterVertically(boolean centerVertically) {
+        mCenterVertically = centerVertically;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mCenterVertically) {
@@ -504,6 +547,7 @@
 
     /**
      * Creates an animator to fade the text in or out.
+     *
      * @param fadeIn Whether the text should fade in or fade out.
      */
     public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
@@ -517,51 +561,86 @@
         mLongPressHelper.cancelLongPress();
     }
 
-    public void applyPromiseState(boolean promiseStateChanged) {
-        if (getTag() instanceof WorkspaceItemInfo) {
+    /**
+     * Applies the loading progress value to the progress bar.
+     *
+     * If this app is installing, the progress bar will be updated with the installation progress.
+     * If this app is installed and downloading incrementally, the progress bar will be updated
+     * with the total download progress.
+     */
+    public void applyLoadingState(boolean promiseStateChanged) {
+        if (getTag() instanceof ItemInfoWithIcon) {
             WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
-            final boolean isPromise = info.hasPromiseIconUi();
-            final int progressLevel = isPromise ?
-                    ((info.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
-                            info.getInstallProgress() : 0)) : 100;
-
-            PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
-            if (preloadDrawable != null && promiseStateChanged) {
-                preloadDrawable.maybePerformFinishedAnimation();
+            if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)
+                    != 0) {
+                updateProgressBarUi(info.getProgressLevel() == 100);
+            } else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags
+                        & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                updateProgressBarUi(promiseStateChanged);
             }
         }
     }
 
-    public PreloadIconDrawable applyProgressLevel(int progressLevel) {
-        if (getTag() instanceof ItemInfoWithIcon) {
-            ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
-            if (progressLevel >= 100) {
-                setContentDescription(info.contentDescription != null
-                        ? info.contentDescription : "");
-            } else if (progressLevel > 0) {
-                setContentDescription(getContext()
-                        .getString(R.string.app_downloading_title, info.title,
-                                NumberFormat.getPercentInstance().format(progressLevel * 0.01)));
+    private void updateProgressBarUi(boolean maybePerformFinishedAnimation) {
+        PreloadIconDrawable preloadDrawable = applyProgressLevel();
+        if (preloadDrawable != null && maybePerformFinishedAnimation) {
+            preloadDrawable.maybePerformFinishedAnimation();
+        }
+    }
+
+    /** Applies the given progress level to the this icon's progress bar. */
+    @Nullable
+    public PreloadIconDrawable applyProgressLevel() {
+        if (!(getTag() instanceof ItemInfoWithIcon)) {
+            return null;
+        }
+
+        ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+        int progressLevel = info.getProgressLevel();
+        if (progressLevel >= 100) {
+            setContentDescription(info.contentDescription != null
+                    ? info.contentDescription : "");
+        } else if (progressLevel > 0) {
+            setDownloadStateContentDescription(info, progressLevel);
+        } else {
+            setContentDescription(getContext()
+                    .getString(R.string.app_waiting_download_title, info.title));
+        }
+        if (mIcon != null) {
+            PreloadIconDrawable preloadIconDrawable;
+            if (mIcon instanceof PreloadIconDrawable) {
+                preloadIconDrawable = (PreloadIconDrawable) mIcon;
+                preloadIconDrawable.setLevel(progressLevel);
+                preloadIconDrawable.setIsDisabled(!info.isAppStartable());
             } else {
-                setContentDescription(getContext()
-                        .getString(R.string.app_waiting_download_title, info.title));
+                preloadIconDrawable = makePreloadIcon();
+                setIcon(preloadIconDrawable);
             }
-            if (mIcon != null) {
-                final PreloadIconDrawable preloadDrawable;
-                if (mIcon instanceof PreloadIconDrawable) {
-                    preloadDrawable = (PreloadIconDrawable) mIcon;
-                    preloadDrawable.setLevel(progressLevel);
-                } else {
-                    preloadDrawable = newPendingIcon(getContext(), info);
-                    preloadDrawable.setLevel(progressLevel);
-                    setIcon(preloadDrawable);
-                }
-                return preloadDrawable;
-            }
+            return preloadIconDrawable;
         }
         return null;
     }
 
+    /**
+     * Creates a PreloadIconDrawable with the appropriate progress level without mutating this
+     * object.
+     */
+    @Nullable
+    public PreloadIconDrawable makePreloadIcon() {
+        if (!(getTag() instanceof ItemInfoWithIcon)) {
+            return null;
+        }
+
+        ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+        int progressLevel = info.getProgressLevel();
+        final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info);
+
+        preloadDrawable.setLevel(progressLevel);
+        preloadDrawable.setIsDisabled(!info.isAppStartable());
+
+        return preloadDrawable;
+    }
+
     public void applyDotState(ItemInfo itemInfo, boolean animate) {
         if (mIcon instanceof FastBitmapDrawable) {
             boolean wasDotted = mDotInfo != null;
@@ -598,10 +677,28 @@
         }
     }
 
+    private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) {
+        if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)
+                != 0) {
+            String percentageString = NumberFormat.getPercentInstance()
+                    .format(progressLevel * 0.01);
+            if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                setContentDescription(getContext()
+                        .getString(
+                            R.string.app_installing_title, info.title, percentageString));
+            } else if ((info.runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+                setContentDescription(getContext()
+                        .getString(
+                            R.string.app_downloading_title, info.title, percentageString));
+            }
+        }
+    }
+
     /**
      * Sets the icon for this view based on the layout direction.
      */
-    private void setIcon(Drawable icon) {
+    protected void setIcon(FastBitmapDrawable icon) {
         if (mIsIconVisible) {
             applyCompoundDrawables(icon);
         }
@@ -621,17 +718,26 @@
         applyCompoundDrawables(icon);
     }
 
+    protected boolean iconUpdateAnimationEnabled() {
+        return mEnableIconUpdateAnimation;
+    }
+
     protected void applyCompoundDrawables(Drawable icon) {
         // If we had already set an icon before, disable relayout as the icon size is the
         // same as before.
         mDisableRelayout = mIcon != null;
 
         icon.setBounds(0, 0, mIconSize, mIconSize);
-        if (mLayoutHorizontal) {
-            setCompoundDrawablesRelative(icon, null, null, null);
-        } else {
-            setCompoundDrawables(null, icon, null, null);
+
+        updateIcon(icon);
+
+        // If the current icon is a placeholder color, animate its update.
+        if (mIcon != null
+                && mIcon instanceof PlaceHolderIconDrawable
+                && iconUpdateAnimationEnabled()) {
+            ((PlaceHolderIconDrawable) mIcon).animateIconUpdate(icon);
         }
+
         mDisableRelayout = false;
     }
 
@@ -650,6 +756,7 @@
         if (getTag() == info) {
             mIconLoadRequest = null;
             mDisableRelayout = true;
+            mEnableIconUpdateAnimation = true;
 
             // Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
             info.bitmap.icon.prepareToDraw();
@@ -660,10 +767,13 @@
                 applyFromWorkspaceItem((WorkspaceItemInfo) info);
                 mActivity.invalidateParent(info);
             } else if (info instanceof PackageItemInfo) {
-                applyFromPackageItemInfo((PackageItemInfo) info);
+                applyFromItemInfoWithIcon((PackageItemInfo) info);
+            } else if (info instanceof SearchActionItemInfo) {
+                applyFromSearchActionItemInfo((SearchActionItemInfo) info);
             }
 
             mDisableRelayout = false;
+            mEnableIconUpdateAnimation = false;
         }
     }
 
@@ -763,7 +873,15 @@
 
     private void resetIconScale() {
         if (mIcon instanceof FastBitmapDrawable) {
-            ((FastBitmapDrawable) mIcon).setScale(1f);
+            ((FastBitmapDrawable) mIcon).resetScale();
+        }
+    }
+
+    private void updateIcon(Drawable newIcon) {
+        if (mLayoutHorizontal) {
+            setCompoundDrawablesRelative(newIcon, null, null, null);
+        } else {
+            setCompoundDrawables(null, newIcon, null, null);
         }
     }
 }
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 09827d6..61b5564 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -20,20 +20,12 @@
 
 import static com.android.launcher3.LauncherState.NORMAL;
 
-import android.animation.AnimatorSet;
-import android.animation.FloatArrayEvaluator;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Property;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -47,9 +39,6 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.Thunk;
 
 /**
  * Implements a DropTarget.
@@ -57,22 +46,9 @@
 public abstract class ButtonDropTarget extends TextView
         implements DropTarget, DragController.DragListener, OnClickListener {
 
-    private static final Property<ButtonDropTarget, Integer> TEXT_COLOR =
-            new Property<ButtonDropTarget, Integer>(Integer.TYPE, "textColor") {
-
-                @Override
-                public Integer get(ButtonDropTarget target) {
-                    return target.getTextColor();
-                }
-
-                @Override
-                public void set(ButtonDropTarget target, Integer value) {
-                    target.setTextColor(value);
-                }
-            };
-
     private static final int[] sTempCords = new int[2];
     private static final int DRAG_VIEW_DROP_DURATION = 285;
+    private static final float DRAG_VIEW_HOVER_OVER_OPACITY = 0.65f;
 
     public static final int TOOLTIP_DEFAULT = 0;
     public static final int TOOLTIP_LEFT = 1;
@@ -80,7 +56,6 @@
 
     protected final Launcher mLauncher;
 
-    private int mBottomDragPadding;
     protected DropTargetBar mDropTargetBar;
 
     /** Whether this drop target is active for the current drag */
@@ -89,21 +64,16 @@
     private boolean mAccessibleDrag;
     /** An item must be dragged at least this many pixels before this drop target is enabled. */
     private final int mDragDistanceThreshold;
-
-    /** The paint applied to the drag view on hover */
-    protected int mHoverColor = 0;
+    /** The size of the drawable shown in the drop target. */
+    private final int mDrawableSize;
 
     protected CharSequence mText;
-    protected ColorStateList mOriginalTextColor;
     protected Drawable mDrawable;
     private boolean mTextVisible = true;
 
     private PopupWindow mToolTip;
     private int mToolTipLocation;
 
-    private AnimatorSet mCurrentColorAnim;
-    @Thunk ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter;
-
     public ButtonDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -113,15 +83,14 @@
         mLauncher = Launcher.getLauncher(context);
 
         Resources resources = getResources();
-        mBottomDragPadding = resources.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
         mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
+        mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_text_size);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mText = getText();
-        mOriginalTextColor = getTextColors();
         setContentDescription(mText);
     }
 
@@ -134,13 +103,10 @@
     protected void setDrawable(int resId) {
         // We do not set the drawable in the xml as that inflates two drawables corresponding to
         // drawableLeft and drawableStart.
-        if (mTextVisible) {
-            setCompoundDrawablesRelativeWithIntrinsicBounds(resId, 0, 0, 0);
-            mDrawable = getCompoundDrawablesRelative()[0];
-        } else {
-            setCompoundDrawablesRelativeWithIntrinsicBounds(0, resId, 0, 0);
-            mDrawable = getCompoundDrawablesRelative()[1];
-        }
+        mDrawable = getContext().getDrawable(resId).mutate();
+        mDrawable.setBounds(0, 0, mDrawableSize, mDrawableSize);
+        mDrawable.setTintList(getTextColors());
+        setCompoundDrawablesRelative(mDrawable, null, null, null);
     }
 
     public void setDropTargetBar(DropTargetBar dropTargetBar) {
@@ -178,8 +144,8 @@
             mToolTip.showAsDropDown(this, x, y);
         }
 
-        d.dragView.setColor(mHoverColor);
-        animateTextColor(mHoverColor);
+        d.dragView.setAlpha(DRAG_VIEW_HOVER_OVER_OPACITY);
+        setSelected(true);
         if (d.stateAnnouncer != null) {
             d.stateAnnouncer.cancel();
         }
@@ -191,63 +157,21 @@
         // Do nothing
     }
 
-    protected void resetHoverColor() {
-        animateTextColor(mOriginalTextColor.getDefaultColor());
-    }
-
-    private void animateTextColor(int targetColor) {
-        if (mCurrentColorAnim != null) {
-            mCurrentColorAnim.cancel();
-        }
-
-        mCurrentColorAnim = new AnimatorSet();
-        mCurrentColorAnim.setDuration(DragView.COLOR_CHANGE_DURATION);
-
-        if (mSrcFilter == null) {
-            mSrcFilter = new ColorMatrix();
-            mDstFilter = new ColorMatrix();
-            mCurrentFilter = new ColorMatrix();
-        }
-
-        int defaultTextColor = mOriginalTextColor.getDefaultColor();
-        Themes.setColorChangeOnMatrix(defaultTextColor, getTextColor(), mSrcFilter);
-        Themes.setColorChangeOnMatrix(defaultTextColor, targetColor, mDstFilter);
-
-        ValueAnimator anim1 = ValueAnimator.ofObject(
-                new FloatArrayEvaluator(mCurrentFilter.getArray()),
-                mSrcFilter.getArray(), mDstFilter.getArray());
-        anim1.addUpdateListener((anim) -> {
-            mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
-            invalidate();
-        });
-
-        mCurrentColorAnim.play(anim1);
-        mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, TEXT_COLOR, targetColor));
-        mCurrentColorAnim.start();
-    }
-
     @Override
     public final void onDragExit(DragObject d) {
         hideTooltip();
 
         if (!d.dragComplete) {
-            d.dragView.setColor(0);
-            resetHoverColor();
+            d.dragView.setAlpha(1f);
+            setSelected(false);
         } else {
-            // Restore the hover color
-            d.dragView.setColor(mHoverColor);
+            d.dragView.setAlpha(DRAG_VIEW_HOVER_OVER_OPACITY);
         }
     }
 
     @Override
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
-        mActive = supportsDrop(dragObject.dragInfo);
-        mDrawable.setColorFilter(null);
-        if (mCurrentColorAnim != null) {
-            mCurrentColorAnim.cancel();
-            mCurrentColorAnim = null;
-        }
-        setTextColor(mOriginalTextColor);
+        mActive = !options.isKeyboardDrag && supportsDrop(dragObject.dragInfo);
         setVisibility(mActive ? View.VISIBLE : View.GONE);
 
         mAccessibleDrag = options.isAccessibleDrag;
@@ -273,6 +197,7 @@
     public void onDragEnd() {
         mActive = false;
         setOnClickListener(null);
+        setSelected(false);
     }
 
     /**
@@ -285,17 +210,23 @@
             return;
         }
         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();
+        dragView.detachContentView(/* reattachToPreviousParent= */ true);
         mDropTargetBar.deferOnDragEnd();
 
         Runnable onAnimationEndRunnable = () -> {
             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,
@@ -316,7 +247,7 @@
     @Override
     public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
         super.getHitRect(outRect);
-        outRect.bottom += mBottomDragPadding;
+        outRect.bottom += mLauncher.getDeviceProfile().dropTargetDragPaddingPx;
 
         sTempCords[0] = sTempCords[1] = 0;
         mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, sTempCords);
@@ -366,20 +297,12 @@
         mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null);
     }
 
-    public int getTextColor() {
-        return getTextColors().getDefaultColor();
-    }
-
     public void setTextVisible(boolean isVisible) {
         CharSequence newText = isVisible ? mText : "";
         if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
             mTextVisible = isVisible;
             setText(newText);
-            if (mTextVisible) {
-                setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
-            } else {
-                setCompoundDrawablesRelativeWithIntrinsicBounds(null, mDrawable, null, null);
-            }
+            setCompoundDrawablesRelative(mDrawable, null, null, null);
         }
     }
 
@@ -395,6 +318,4 @@
                 TextUtils.TruncateAt.END);
         return !mText.equals(displayedText);
     }
-
-    public abstract Target getDropTargetForLogging();
 }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 89d768c..a6adfc4 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3;
 
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
+
+import static com.android.launcher3.Utilities.getBoundsForViewInDragLayer;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
 
 import android.animation.Animator;
@@ -28,18 +31,19 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 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.Parcelable;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.util.Property;
 import android.util.SparseArray;
@@ -50,15 +54,15 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
 import androidx.core.view.ViewCompat;
 
 import com.android.launcher3.LauncherSettings.Favorites;
 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.graphics.DragPreviewProvider;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.CellAndSpan;
 import com.android.launcher3.util.GridOccupancy;
@@ -66,6 +70,7 @@
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -86,6 +91,8 @@
     @Thunk int mCellHeight;
     private int mFixedCellWidth;
     private int mFixedCellHeight;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private final int mBorderSpacing;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private int mCountX;
@@ -100,11 +107,6 @@
     @Thunk final int[] mTempLocation = new int[2];
     final PointF mTmpPointF = new PointF();
 
-    // Used to visualize / debug the Grid of the CellLayout
-    private static final boolean VISUALIZE_GRID = false;
-    private Rect mVisualizeGridRect = new Rect();
-    private Paint mVisualizeGridPaint = new Paint();
-
     private GridOccupancy mOccupied;
     private GridOccupancy mTmpOccupied;
 
@@ -126,7 +128,7 @@
 
     // These arrays are used to implement the drag visualization on x-large screens.
     // They are used as circular arrays, indexed by mDragOutlineCurrent.
-    @Thunk final Rect[] mDragOutlines = new Rect[4];
+    @Thunk final CellLayout.LayoutParams[] mDragOutlines = new CellLayout.LayoutParams[4];
     @Thunk final float[] mDragOutlineAlphas = new float[mDragOutlines.length];
     private final InterruptibleInOutAnimator[] mDragOutlineAnims =
             new InterruptibleInOutAnimator[mDragOutlines.length];
@@ -140,8 +142,21 @@
 
     private boolean mItemPlacementDirty = false;
 
+    // Used to visualize the grid and drop locations
+    private boolean mVisualizeCells = false;
+    private boolean mVisualizeDropLocation = true;
+    private RectF mVisualizeGridRect = new RectF();
+    private Paint mVisualizeGridPaint = new Paint();
+    private int mGridVisualizationPadding;
+    private int mGridVisualizationRoundingRadius;
+    private float mGridAlpha = 0f;
+    private int mGridColor = 0;
+    private float mSpringLoadedProgress = 0f;
+    private float mScrollProgress = 0f;
+
     // When a drag operation is in progress, holds the nearest cell to the touch point
     private final int[] mDragCell = new int[2];
+    private final int[] mDragCellSpan = new int[2];
 
     private boolean mDragging = false;
 
@@ -174,16 +189,33 @@
     private final ArrayList<View> mIntersectingViews = new ArrayList<>();
     private final Rect mOccupiedRect = new Rect();
     private final int[] mDirectionVector = new int[2];
+
     final int[] mPreviousReorderDirection = new int[2];
     private static final int INVALID_DIRECTION = -100;
 
     private final Rect mTempRect = new Rect();
+    private final RectF mTempRectF = new RectF();
+    private final float[] mTmpFloatArray = new float[4];
 
     private static final Paint sPaint = new Paint();
 
     // Related to accessible drag and drop
     DragAndDropAccessibilityDelegate mTouchHelper;
 
+
+    public static final FloatProperty<CellLayout> SPRING_LOADED_PROGRESS =
+            new FloatProperty<CellLayout>("spring_loaded_progress") {
+                @Override
+                public Float get(CellLayout cl) {
+                    return cl.getSpringLoadedProgress();
+                }
+
+                @Override
+                public void setValue(CellLayout cl, float progress) {
+                    cl.setSpringLoadedProgress(progress);
+                }
+            };
+
     public CellLayout(Context context) {
         this(context, null);
     }
@@ -203,14 +235,17 @@
         setWillNotDraw(false);
         setClipToPadding(false);
         mActivity = ActivityContext.lookupContext(context);
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
 
-        DeviceProfile grid = mActivity.getDeviceProfile();
+        mBorderSpacing = mContainerType == FOLDER
+                ? deviceProfile.folderCellLayoutBorderSpacingPx
+                : deviceProfile.cellLayoutBorderSpacingPx;
 
         mCellWidth = mCellHeight = -1;
         mFixedCellWidth = mFixedCellHeight = -1;
 
-        mCountX = grid.inv.numColumns;
-        mCountY = grid.inv.numRows;
+        mCountX = deviceProfile.inv.numColumns;
+        mCountY = deviceProfile.inv.numRows;
         mOccupied =  new GridOccupancy(mCountX, mCountY);
         mTmpOccupied = new GridOccupancy(mCountX, mCountY);
 
@@ -221,19 +256,26 @@
         mFolderLeaveBehind.mDelegateCellY = -1;
 
         setAlwaysDrawnWithCacheEnabled(false);
-        final Resources res = getResources();
 
-        mBackground = res.getDrawable(R.drawable.bg_celllayout);
+        Resources res = getResources();
+
+        mBackground = getContext().getDrawable(R.drawable.bg_celllayout);
         mBackground.setCallback(this);
         mBackground.setAlpha(0);
 
-        mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx);
+        mGridColor = Themes.getAttrColor(getContext(), R.attr.workspaceAccentColor);
+        mGridVisualizationPadding =
+                res.getDimensionPixelSize(R.dimen.grid_visualization_cell_spacing);
+        mGridVisualizationRoundingRadius =
+                res.getDimensionPixelSize(R.dimen.grid_visualization_rounding_radius);
+        mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * deviceProfile.iconSizePx);
 
         // Initialize the data structures used for the drag visualization.
         mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease out
         mDragCell[0] = mDragCell[1] = -1;
+        mDragCellSpan[0] = mDragCellSpan[1] = -1;
         for (int i = 0; i < mDragOutlines.length; i++) {
-            mDragOutlines[i] = new Rect(-1, -1, -1, -1);
+            mDragOutlines[i] = new CellLayout.LayoutParams(0, 0, 0, 0);
         }
         mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
 
@@ -254,39 +296,20 @@
             final int thisIndex = i;
             anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
                 public void onAnimationUpdate(ValueAnimator animation) {
-                    final Bitmap outline = (Bitmap)anim.getTag();
-
                     // If an animation is started and then stopped very quickly, we can still
                     // get spurious updates we've cleared the tag. Guard against this.
-                    if (outline == null) {
-                        if (LOGD) {
-                            Object val = animation.getAnimatedValue();
-                            Log.d(TAG, "anim " + thisIndex + " update: " + val +
-                                     ", isStopped " + anim.isStopped());
-                        }
-                        // Try to prevent it from continuing to run
-                        animation.cancel();
-                    } else {
-                        mDragOutlineAlphas[thisIndex] = (Float) animation.getAnimatedValue();
-                        CellLayout.this.invalidate(mDragOutlines[thisIndex]);
-                    }
+                    mDragOutlineAlphas[thisIndex] = (Float) animation.getAnimatedValue();
+                    CellLayout.this.invalidate();
                 }
             });
             // The animation holds a reference to the drag outline bitmap as long is it's
             // running. This way the bitmap can be GCed when the animations are complete.
-            anim.getAnimator().addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if ((Float) ((ValueAnimator) animation).getAnimatedValue() == 0f) {
-                        anim.setTag(null);
-                    }
-                }
-            });
             mDragOutlineAnims[i] = anim;
         }
 
         mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context, mContainerType);
-        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
+        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
+                mBorderSpacing);
         addView(mShortcutsAndWidgets);
     }
 
@@ -303,6 +326,8 @@
         setImportantForAccessibility(accessibilityFlag);
         getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
 
+        // ExploreByTouchHelper sets focusability. Clear it when the delegate is cleared.
+        setFocusable(delegate != null);
         // Invalidate the accessibility hierarchy
         if (getParent() != null) {
             getParent().notifySubtreeAccessibilityStateChanged(
@@ -310,6 +335,13 @@
         }
     }
 
+    /**
+     * Returns the currently set accessibility delegate
+     */
+    public DragAndDropAccessibilityDelegate getDragAndDropAccessibilityDelegate() {
+        return mTouchHelper;
+    }
+
     @Override
     public boolean dispatchHoverEvent(MotionEvent event) {
         // Always attempt to dispatch hover events to accessibility first.
@@ -321,11 +353,8 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mTouchHelper != null
-                || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) {
-            return true;
-        }
-        return false;
+        return mTouchHelper != null
+                || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev));
     }
 
     public void enableHardwareLayer(boolean hasLayer) {
@@ -339,7 +368,8 @@
     public void setCellDimensions(int width, int height) {
         mFixedCellWidth = mCellWidth = width;
         mFixedCellHeight = mCellHeight = height;
-        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
+        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
+                mBorderSpacing);
     }
 
     public void setGridSize(int x, int y) {
@@ -348,7 +378,8 @@
         mOccupied = new GridOccupancy(mCountX, mCountY);
         mTmpOccupied = new GridOccupancy(mCountX, mCountY);
         mTempRectStack.clear();
-        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
+        mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
+                mBorderSpacing);
         requestLayout();
     }
 
@@ -411,16 +442,6 @@
             mBackground.draw(canvas);
         }
 
-        final Paint paint = mDragOutlinePaint;
-        for (int i = 0; i < mDragOutlines.length; i++) {
-            final float alpha = mDragOutlineAlphas[i];
-            if (alpha > 0) {
-                final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
-                paint.setAlpha((int)(alpha + .5f));
-                canvas.drawBitmap(b, null, mDragOutlines[i], paint);
-            }
-        }
-
         if (DEBUG_VISUALIZE_OCCUPIED) {
             int[] pt = new int[2];
             ColorDrawable cd = new ColorDrawable(Color.RED);
@@ -456,34 +477,113 @@
             canvas.restore();
         }
 
-        if (VISUALIZE_GRID) {
+        if (mVisualizeCells || mVisualizeDropLocation) {
             visualizeGrid(canvas);
         }
     }
 
+    /**
+     * Indicates the progress of the Workspace entering the SpringLoaded state; allows the
+     * CellLayout to update various visuals for this state.
+     *
+     * @param progress
+     */
+    public void setSpringLoadedProgress(float progress) {
+        if (Float.compare(progress, mSpringLoadedProgress) != 0) {
+            mSpringLoadedProgress = progress;
+            updateBgAlpha();
+            setGridAlpha(progress);
+        }
+    }
+
+    /**
+     * See setSpringLoadedProgress
+     * @return progress
+     */
+    public float getSpringLoadedProgress() {
+        return mSpringLoadedProgress;
+    }
+
+    private void updateBgAlpha() {
+        mBackground.setAlpha((int) (mSpringLoadedProgress * 255));
+    }
+
+    /**
+     * Set the progress of this page's scroll
+     *
+     * @param progress 0 if the screen is centered, +/-1 if it is to the right / left respectively
+     */
+    public void setScrollProgress(float progress) {
+        if (Float.compare(Math.abs(progress), mScrollProgress) != 0) {
+            mScrollProgress = Math.abs(progress);
+            updateBgAlpha();
+        }
+    }
+
+    private void setGridAlpha(float gridAlpha) {
+        if (Float.compare(gridAlpha, mGridAlpha) != 0) {
+            mGridAlpha = gridAlpha;
+            invalidate();
+        }
+    }
+
     protected void visualizeGrid(Canvas canvas) {
-        mVisualizeGridRect.set(0, 0, mCellWidth, mCellHeight);
-        mVisualizeGridPaint.setStrokeWidth(4);
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        int paddingX = (int) Math.min((mCellWidth - dp.iconSizePx) / 2, mGridVisualizationPadding);
+        int paddingY = (int) Math.min((mCellHeight - dp.iconSizePx) / 2, mGridVisualizationPadding);
+        mVisualizeGridRect.set(paddingX, paddingY,
+                mCellWidth - paddingX,
+                mCellHeight - paddingY);
 
-        for (int i = 0; i < mCountX; i++) {
-            for (int j = 0; j < mCountY; j++) {
-                canvas.save();
+        mVisualizeGridPaint.setStrokeWidth(8);
+        int paintAlpha = (int) (120 * mGridAlpha);
+        mVisualizeGridPaint.setColor(ColorUtils.setAlphaComponent(mGridColor, paintAlpha));
 
-                int transX = i * mCellWidth;
-                int transY = j * mCellHeight;
+        if (mVisualizeCells) {
+            for (int i = 0; i < mCountX; i++) {
+                for (int j = 0; j < mCountY; j++) {
+                    int transX = i * mCellWidth + (i * mBorderSpacing) + getPaddingLeft()
+                            + paddingX;
+                    int transY = j * mCellHeight + (j * mBorderSpacing) + getPaddingTop()
+                            + paddingY;
 
-                canvas.translate(getPaddingLeft() + transX, getPaddingTop() + transY);
+                    mVisualizeGridRect.offsetTo(transX, transY);
+                    mVisualizeGridPaint.setStyle(Paint.Style.FILL);
+                    canvas.drawRoundRect(mVisualizeGridRect, mGridVisualizationRoundingRadius,
+                            mGridVisualizationRoundingRadius, mVisualizeGridPaint);
+                }
+            }
+        }
 
-                mVisualizeGridPaint.setStyle(Paint.Style.FILL);
-                mVisualizeGridPaint.setColor(Color.argb(80, 255, 100, 100));
+        if (mVisualizeDropLocation) {
+            for (int i = 0; i < mDragOutlines.length; i++) {
+                final float alpha = mDragOutlineAlphas[i];
+                if (alpha <= 0) continue;
 
-                canvas.drawRect(mVisualizeGridRect, mVisualizeGridPaint);
+                mVisualizeGridPaint.setAlpha(255);
+                int x = mDragOutlines[i].cellX;
+                int y = mDragOutlines[i].cellY;
+                int spanX = mDragOutlines[i].cellHSpan;
+                int spanY = mDragOutlines[i].cellVSpan;
+
+                // 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);
+
+                int transX = x * mCellWidth + (x * mBorderSpacing)
+                        + getPaddingLeft() + paddingX;
+                int transY = y * mCellHeight + (y * mBorderSpacing)
+                        + getPaddingTop() + paddingY;
+
+                mVisualizeGridRect.offsetTo(transX, transY);
 
                 mVisualizeGridPaint.setStyle(Paint.Style.STROKE);
-                mVisualizeGridPaint.setColor(Color.argb(255, 255, 100, 100));
+                mVisualizeGridPaint.setColor(Color.argb((int) (alpha),
+                        Color.red(mGridColor), Color.green(mGridColor), Color.blue(mGridColor)));
 
-                canvas.drawRect(mVisualizeGridRect, mVisualizeGridPaint);
-                canvas.restore();
+                canvas.drawRoundRect(mVisualizeGridRect, mGridVisualizationRoundingRadius,
+                        mGridVisualizationRoundingRadius, mVisualizeGridPaint);
             }
         }
     }
@@ -700,11 +800,9 @@
      * @param result Array of 2 ints to hold the x and y coordinate of the point
      */
     void cellToPoint(int cellX, int cellY, int[] result) {
-        final int hStartPadding = getPaddingLeft();
-        final int vStartPadding = getPaddingTop();
-
-        result[0] = hStartPadding + cellX * mCellWidth;
-        result[1] = vStartPadding + cellY * mCellHeight;
+        cellToRect(cellX, cellY, 1, 1, mTempRect);
+        result[0] = mTempRect.left;
+        result[1] = mTempRect.top;
     }
 
     /**
@@ -728,25 +826,9 @@
      * @param result Array of 2 ints to hold the x and y coordinate of the point
      */
     void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
-        final int hStartPadding = getPaddingLeft();
-        final int vStartPadding = getPaddingTop();
-        result[0] = hStartPadding + cellX * mCellWidth + (spanX * mCellWidth) / 2;
-        result[1] = vStartPadding + cellY * mCellHeight + (spanY * mCellHeight) / 2;
-    }
-
-     /**
-     * Given a cell coordinate and span fills out a corresponding pixel rect
-     *
-     * @param cellX X coordinate of the cell
-     * @param cellY Y coordinate of the cell
-     * @param result Rect in which to write the result
-     */
-     void regionToRect(int cellX, int cellY, int spanX, int spanY, Rect result) {
-        final int hStartPadding = getPaddingLeft();
-        final int vStartPadding = getPaddingTop();
-        final int left = hStartPadding + cellX * mCellWidth;
-        final int top = vStartPadding + cellY * mCellHeight;
-        result.set(left, top, left + (spanX * mCellWidth), top + (spanY * mCellHeight));
+        cellToRect(cellX, cellY, spanX, spanY, mTempRect);
+        result[0] = mTempRect.centerX();
+        result[1] = mTempRect.centerY();
     }
 
     public float getDistanceFromCell(float x, float y, int[] cell) {
@@ -777,12 +859,15 @@
         int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
 
         if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
-            int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX);
-            int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY);
+            int cw = DeviceProfile.calculateCellWidth(childWidthSize, mBorderSpacing,
+                    mCountX);
+            int ch = DeviceProfile.calculateCellHeight(childHeightSize, mBorderSpacing,
+                    mCountY);
             if (cw != mCellWidth || ch != mCellHeight) {
                 mCellWidth = cw;
                 mCellHeight = ch;
-                mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
+                mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
+                        mBorderSpacing);
             }
         }
 
@@ -832,14 +917,11 @@
     /**
      * Returns the amount of space left over after subtracting padding and cells. This space will be
      * very small, a few pixels at most, and is a result of rounding down when calculating the cell
-     * width in {@link DeviceProfile#calculateCellWidth(int, int)}.
+     * width in {@link DeviceProfile#calculateCellWidth(int, int, int)}.
      */
     public int getUnusedHorizontalSpace() {
-        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
-    }
-
-    public Drawable getScrimBackground() {
-        return mBackground;
+        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth)
+                - ((mCountX - 1) * mBorderSpacing);
     }
 
     @Override
@@ -851,8 +933,8 @@
         return mShortcutsAndWidgets;
     }
 
-    public View getChildAt(int x, int y) {
-        return mShortcutsAndWidgets.getChildAt(x, y);
+    public View getChildAt(int cellX, int cellY) {
+        return mShortcutsAndWidgets.getChildAt(cellX, cellY);
     }
 
     public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
@@ -951,58 +1033,56 @@
         return false;
     }
 
-    void visualizeDropLocation(DraggableView v, DragPreviewProvider outlineProvider, int cellX, int
-            cellY, int spanX, int spanY, boolean resize, DropTarget.DragObject dragObject) {
-        final int oldDragCellX = mDragCell[0];
-        final int oldDragCellY = mDragCell[1];
-
-        if (outlineProvider == null || outlineProvider.generatedDragOutline == null) {
-            return;
-        }
-
-        Bitmap dragOutline = outlineProvider.generatedDragOutline;
-        if (cellX != oldDragCellX || cellY != oldDragCellY) {
+    void visualizeDropLocation(int cellX, int cellY, int spanX, int spanY,
+            DropTarget.DragObject dragObject) {
+        if (mDragCell[0] != cellX || mDragCell[1] != cellY || mDragCellSpan[0] != spanX
+                || mDragCellSpan[1] != spanY) {
             mDragCell[0] = cellX;
             mDragCell[1] = cellY;
+            mDragCellSpan[0] = spanX;
+            mDragCellSpan[1] = spanY;
+
+            // Apply color extraction on a widget when dragging.
+            applyColorExtractionOnWidget(dragObject, mDragCell, spanX, spanY);
 
             final int oldIndex = mDragOutlineCurrent;
             mDragOutlineAnims[oldIndex].animateOut();
             mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
-            Rect r = mDragOutlines[mDragOutlineCurrent];
 
-            cellToRect(cellX, cellY, spanX, spanY, r);
-            int left = r.left;
-            int top = r.top;
+            LayoutParams cell = mDragOutlines[mDragOutlineCurrent];
+            cell.cellX = cellX;
+            cell.cellY = cellY;
+            cell.cellHSpan = spanX;
+            cell.cellVSpan = spanY;
 
-            int width = dragOutline.getWidth();
-            int height = dragOutline.getHeight();
-
-            if (resize) {
-                width = r.width();
-                height = r.height();
-            }
-
-            // Center horizontaly
-            left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
-
-            if (v != null && v.getViewType() == DraggableView.DRAGGABLE_WIDGET) {
-                // Center vertically
-                top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
-            } else if (v != null && v.getViewType() == DraggableView.DRAGGABLE_ICON) {
-                int cHeight = getShortcutsAndWidgets().getCellContentHeight();
-                int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
-                top += cellPaddingY;
-            }
-
-            r.set(left, top, left + width, top + height);
-
-            Utilities.scaleRectAboutCenter(r, mChildScale);
-            mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
             mDragOutlineAnims[mDragOutlineCurrent].animateIn();
+            invalidate();
 
             if (dragObject.stateAnnouncer != null) {
                 dragObject.stateAnnouncer.announce(getItemMoveDescription(cellX, cellY));
             }
+
+        }
+    }
+
+    /** Applies the local color extraction to a dragging widget object. */
+    private void applyColorExtractionOnWidget(DropTarget.DragObject dragObject, int[] targetCell,
+            int spanX, int spanY) {
+        // 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());
+            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);
         }
     }
 
@@ -2009,7 +2089,7 @@
             // Animations are disabled in power save mode, causing the repeated animation to jump
             // spastically between beginning and end states. Since this looks bad, we don't repeat
             // the animation in power save mode.
-            if (Utilities.areAnimationsEnabled(getContext())) {
+            if (areAnimatorsEnabled()) {
                 va.setRepeatMode(ValueAnimator.REVERSE);
                 va.setRepeatCount(ValueAnimator.INFINITE);
             }
@@ -2147,7 +2227,7 @@
 
         findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
         Rect dragRect = new Rect();
-        regionToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
+        cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
         dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
 
         Rect dropRegionRect = new Rect();
@@ -2157,7 +2237,7 @@
         int dropRegionSpanX = dropRegionRect.width();
         int dropRegionSpanY = dropRegionRect.height();
 
-        regionToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
+        cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
                 dropRegionRect.height(), dropRegionRect);
 
         int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
@@ -2477,6 +2557,7 @@
 
         // Invalidate the drag data
         mDragCell[0] = mDragCell[1] = -1;
+        mDragCellSpan[0] = mDragCellSpan[1] = -1;
         mDragOutlineAnims[mDragOutlineCurrent].animateOut();
         mDragOutlineCurrent = (mDragOutlineCurrent + 1) % mDragOutlineAnims.length;
         revertTempState();
@@ -2512,13 +2593,16 @@
         final int cellWidth = mCellWidth;
         final int cellHeight = mCellHeight;
 
-        final int hStartPadding = getPaddingLeft();
+        // We observe a shift of 1 pixel on the x coordinate compared to the actual cell coordinates
+        final int hStartPadding = getPaddingLeft()
+                + (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
         final int vStartPadding = getPaddingTop();
 
-        int width = cellHSpan * cellWidth;
-        int height = cellVSpan * cellHeight;
-        int x = hStartPadding + cellX * cellWidth;
-        int y = vStartPadding + cellY * cellHeight;
+        int x = hStartPadding + (cellX * mBorderSpacing) + (cellX * cellWidth);
+        int y = vStartPadding + (cellY * mBorderSpacing) + (cellY * cellHeight);
+
+        int width = cellHSpan * cellWidth + ((cellHSpan - 1) * mBorderSpacing);
+        int height = cellVSpan * cellHeight + ((cellVSpan - 1) * mBorderSpacing);
 
         resultRect.set(x, y, x + width, y + height);
     }
@@ -2536,11 +2620,13 @@
     }
 
     public int getDesiredWidth() {
-        return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth);
+        return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth)
+                + ((mCountX - 1) * mBorderSpacing);
     }
 
     public int getDesiredHeight()  {
-        return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight);
+        return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight)
+                + ((mCountY - 1) * mBorderSpacing);
     }
 
     public boolean isOccupied(int x, int y) {
@@ -2655,19 +2741,22 @@
             this.cellVSpan = cellVSpan;
         }
 
-        public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount) {
-            setup(cellWidth, cellHeight, invertHorizontally, colCount, 1.0f, 1.0f);
+        public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+                int rowCount, int borderSpacing, @Nullable Rect inset) {
+            setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
+                    borderSpacing, inset);
         }
 
         /**
-         * Use this method, as opposed to {@link #setup(int, int, boolean, int)}, if the view needs
-         * to be scaled.
+         * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, int, 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,
-                float cellScaleX, float cellScaleY) {
+                int rowCount, float cellScaleX, float cellScaleY, int borderSpacing,
+                @Nullable Rect inset) {
             if (isLockedToGrid) {
                 final int myCellHSpan = cellHSpan;
                 final int myCellVSpan = cellVSpan;
@@ -2678,17 +2767,30 @@
                     myCellX = colCount - myCellX - cellHSpan;
                 }
 
-                width = (int) (myCellHSpan * cellWidth / cellScaleX - leftMargin - rightMargin);
-                height = (int) (myCellVSpan * cellHeight / cellScaleY - topMargin - bottomMargin);
-                x = (myCellX * cellWidth + leftMargin);
-                y = (myCellY * cellHeight + topMargin);
+                int hBorderSpacing = (myCellHSpan - 1) * borderSpacing;
+                int vBorderSpacing = (myCellVSpan - 1) * borderSpacing;
+
+                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);
+
+                if (inset != null) {
+                    x -= inset.left;
+                    y -= inset.top;
+                    width += inset.left + inset.right;
+                    height += inset.top + inset.bottom;
+                }
             }
         }
 
         /**
          * Sets the position to the provided point
          */
-        public void setXY(Point point) {
+        public void setCellXY(Point point) {
             cellX = point.x;
             cellY = point.y;
         }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 2857497..80ec192 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -18,8 +18,7 @@
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNDO;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNDO;
 
 import android.content.Context;
 import android.text.TextUtils;
@@ -28,22 +27,19 @@
 
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.ModelWriter;
 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.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.views.Snackbar;
 
 public class DeleteDropTarget extends ButtonDropTarget {
 
     private final StatsLogManager mStatsLogManager;
 
-    private int mControlType = ControlType.DEFAULT_CONTROLTYPE;
+    private StatsLogManager.LauncherEvent mLauncherEvent;
 
     public DeleteDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -57,10 +53,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        // Get the hover color
-        mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
-
-        setDrawable(R.drawable.ic_remove_shadow);
+        setDrawable(R.drawable.ic_remove_no_shadow);
     }
 
     @Override
@@ -115,8 +108,8 @@
      * Set mControlType depending on the drag item.
      */
     private void setControlTypeBasedOnDragSource(ItemInfo item) {
-        mControlType = item.id != ItemInfo.NO_ID ? ControlType.REMOVE_TARGET
-                : ControlType.CANCEL_TARGET;
+        mLauncherEvent = item.id != ItemInfo.NO_ID ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
+                : LAUNCHER_ITEM_DROPPED_ON_CANCEL;
     }
 
     @Override
@@ -127,8 +120,7 @@
         }
         super.onDrop(d, options);
         mStatsLogManager.logger().withInstanceId(d.logInstanceId)
-                .log(mControlType == ControlType.REMOVE_TARGET ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
-                        : LAUNCHER_ITEM_DROPPED_ON_CANCEL);
+                .log(mLauncherEvent);
     }
 
     @Override
@@ -141,7 +133,7 @@
             Runnable onUndoClicked = () -> {
                 mLauncher.setPageToBindSynchronously(itemPage);
                 modelWriter.abortDelete();
-                mLauncher.getUserEventDispatcher().logActionOnControl(TAP, UNDO);
+                mLauncher.getStatsLogManager().logger().log(LAUNCHER_UNDO);
             };
             Snackbar.show(mLauncher, R.string.item_removed, R.string.undo,
                     modelWriter::commitDelete, onUndoClicked);
@@ -161,11 +153,4 @@
         mLauncher.getDragLayer()
                 .announceForAccessibility(getContext().getString(R.string.item_removed));
     }
-
-    @Override
-    public Target getDropTargetForLogging() {
-        Target t = LoggerUtils.newTarget(Target.Type.CONTROL);
-        t.controlType = mControlType;
-        return t;
-    }
 }
diff --git a/src/com/android/launcher3/DevicePaddings.java b/src/com/android/launcher3/DevicePaddings.java
new file mode 100644
index 0000000..7c387b1
--- /dev/null
+++ b/src/com/android/launcher3/DevicePaddings.java
@@ -0,0 +1,236 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Workspace items have a fixed height, so we need a way to distribute any unused workspace height.
+ *
+ * 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
+ */
+public class DevicePaddings {
+
+    private static final String DEVICE_PADDING = "device-paddings";
+    private static final String DEVICE_PADDINGS = "device-padding";
+
+    private static final String WORKSPACE_TOP_PADDING = "workspaceTopPadding";
+    private static final String WORKSPACE_BOTTOM_PADDING = "workspaceBottomPadding";
+    private static final String HOTSEAT_BOTTOM_PADDING = "hotseatBottomPadding";
+
+    private static final String TAG = DevicePaddings.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    ArrayList<DevicePadding> mDevicePaddings = new ArrayList<>();
+
+    public DevicePaddings(Context context, int devicePaddingId) {
+        try (XmlResourceParser parser = context.getResources().getXml(devicePaddingId)) {
+            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) && DEVICE_PADDING.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())) {
+                            TypedArray a = context.obtainStyledAttributes(
+                                    Xml.asAttributeSet(parser), R.styleable.DevicePadding);
+                            int maxWidthPx = a.getDimensionPixelSize(
+                                    R.styleable.DevicePadding_maxEmptySpace, 0);
+                            a.recycle();
+
+                            PaddingFormula workspaceTopPadding = null;
+                            PaddingFormula workspaceBottomPadding = null;
+                            PaddingFormula hotseatBottomPadding = null;
+
+                            final int limitDepth = parser.getDepth();
+                            while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                                    parser.getDepth() > limitDepth)
+                                    && type != XmlPullParser.END_DOCUMENT) {
+                                AttributeSet attr = Xml.asAttributeSet(parser);
+                                if ((type == XmlPullParser.START_TAG)) {
+                                    if (WORKSPACE_TOP_PADDING.equals(parser.getName())) {
+                                        workspaceTopPadding = new PaddingFormula(context, attr);
+                                    } else if (WORKSPACE_BOTTOM_PADDING.equals(parser.getName())) {
+                                        workspaceBottomPadding = new PaddingFormula(context, attr);
+                                    } else if (HOTSEAT_BOTTOM_PADDING.equals(parser.getName())) {
+                                        hotseatBottomPadding = new PaddingFormula(context, attr);
+                                    }
+                                }
+                            }
+
+                            if (workspaceTopPadding == null
+                                    || workspaceBottomPadding == null
+                                    || hotseatBottomPadding == null) {
+                                if (Utilities.IS_DEBUG_DEVICE) {
+                                    throw new RuntimeException("DevicePadding missing padding.");
+                                }
+                            }
+
+                            DevicePadding dp = new DevicePadding(maxWidthPx, workspaceTopPadding,
+                                    workspaceBottomPadding, hotseatBottomPadding);
+                            if (dp.isValid()) {
+                                mDevicePaddings.add(dp);
+                            } else {
+                                Log.e(TAG, "Invalid device padding found.");
+                                if (Utilities.IS_DEBUG_DEVICE) {
+                                    throw new RuntimeException("DevicePadding is invalid");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (IOException | XmlPullParserException e) {
+            Log.e(TAG, "Failure parsing device padding layout.", e);
+            throw new RuntimeException(e);
+        }
+
+        // Sort ascending by maxEmptySpacePx
+        mDevicePaddings.sort((sl1, sl2) -> Integer.compare(sl1.maxEmptySpacePx,
+                sl2.maxEmptySpacePx));
+    }
+
+    public DevicePadding getDevicePadding(int extraSpacePx) {
+        for (DevicePadding limit : mDevicePaddings) {
+            if (extraSpacePx <= limit.maxEmptySpacePx) {
+                return limit;
+            }
+        }
+
+        return mDevicePaddings.get(mDevicePaddings.size() - 1);
+    }
+
+    /**
+     * Holds all the formulas to calculate the padding for a particular device based on the
+     * amount of extra space.
+     */
+    public static final class DevicePadding {
+
+        // One for each padding since they can each be off by 1 due to rounding errors.
+        private static final int ROUNDING_THRESHOLD_PX = 3;
+
+        private final int maxEmptySpacePx;
+        private final PaddingFormula workspaceTopPadding;
+        private final PaddingFormula workspaceBottomPadding;
+        private final PaddingFormula hotseatBottomPadding;
+
+        public DevicePadding(int maxEmptySpacePx,
+                PaddingFormula workspaceTopPadding,
+                PaddingFormula workspaceBottomPadding,
+                PaddingFormula hotseatBottomPadding) {
+            this.maxEmptySpacePx = maxEmptySpacePx;
+            this.workspaceTopPadding = workspaceTopPadding;
+            this.workspaceBottomPadding = workspaceBottomPadding;
+            this.hotseatBottomPadding = hotseatBottomPadding;
+        }
+
+        public int getMaxEmptySpacePx() {
+            return maxEmptySpacePx;
+        }
+
+        public int getWorkspaceTopPadding(int extraSpacePx) {
+            return workspaceTopPadding.calculate(extraSpacePx);
+        }
+
+        public int getWorkspaceBottomPadding(int extraSpacePx) {
+            return workspaceBottomPadding.calculate(extraSpacePx);
+        }
+
+        public int getHotseatBottomPadding(int extraSpacePx) {
+            return hotseatBottomPadding.calculate(extraSpacePx);
+        }
+
+        public boolean isValid() {
+            int workspaceTopPadding = getWorkspaceTopPadding(maxEmptySpacePx);
+            int workspaceBottomPadding = getWorkspaceBottomPadding(maxEmptySpacePx);
+            int hotseatBottomPadding = getHotseatBottomPadding(maxEmptySpacePx);
+            int sum = workspaceTopPadding + workspaceBottomPadding + hotseatBottomPadding;
+            int diff = Math.abs(sum - maxEmptySpacePx);
+            if (DEBUG) {
+                Log.d(TAG, "isValid: workspaceTopPadding=" + workspaceTopPadding
+                        + ", workspaceBottomPadding=" + workspaceBottomPadding
+                        + ", hotseatBottomPadding=" + hotseatBottomPadding
+                        + ", sum=" + sum
+                        + ", diff=" + diff);
+            }
+            return diff <= ROUNDING_THRESHOLD_PX;
+        }
+    }
+
+    /**
+     * Used to calculate a padding based on three variables: a, b, and c.
+     *
+     * Calculation: a * (extraSpace - c) + b
+     */
+    private static final class PaddingFormula {
+
+        private final float a;
+        private final float b;
+        private final float c;
+
+        public PaddingFormula(Context context, AttributeSet attrs) {
+            TypedArray t = context.obtainStyledAttributes(attrs,
+                    R.styleable.DevicePaddingFormula);
+
+            a = getValue(t, R.styleable.DevicePaddingFormula_a);
+            b = getValue(t, R.styleable.DevicePaddingFormula_b);
+            c = getValue(t, R.styleable.DevicePaddingFormula_c);
+
+            t.recycle();
+        }
+
+        public int calculate(int extraSpacePx) {
+            if (DEBUG) {
+                Log.d(TAG, "a=" + a + " * (" + extraSpacePx + " - " + c + ") + b=" + b);
+            }
+            return Math.round(a * (extraSpacePx - c) + b);
+        }
+
+        private static float getValue(TypedArray a, int index) {
+            if (a.getType(index) == TypedValue.TYPE_DIMENSION) {
+                return a.getDimensionPixelSize(index, 0);
+            } else if (a.getType(index) == TypedValue.TYPE_FLOAT) {
+                return a.getFloat(index, 0);
+            }
+            return 0;
+        }
+
+        @Override
+        public String toString() {
+            return "a=" + a + ", b=" + b + ", c=" + c;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 49caa93..81eda10 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,35 +16,56 @@
 
 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;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Path;
 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.graphics.IconShape;
+import com.android.launcher3.DevicePaddings.DevicePadding;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.IconNormalizer;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.WindowBounds;
 
+import java.io.PrintWriter;
+
+@SuppressLint("NewApi")
 public class DeviceProfile {
 
-    private static final float TABLET_MIN_DPS = 600;
-    private static final float LARGE_TABLET_MIN_DPS = 720;
-
+    private static final int DEFAULT_DOT_SIZE = 100;
 
     public final InvariantDeviceProfile inv;
-    private final DefaultDisplay.Info mInfo;
+    private final Info mInfo;
+    private final DisplayMetrics mMetrics;
 
     // Device properties
     public final boolean isTablet;
-    public final boolean isLargeTablet;
     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;
@@ -59,6 +80,8 @@
 
     public final float aspectRatio;
 
+    public final boolean isScalableGrid;
+
     /**
      * The maximum amount of left/right workspace padding as a percentage of the screen width.
      * To be clear, this means that up to 7% of the screen width can be used as left padding, and
@@ -67,36 +90,58 @@
     private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f;
 
     private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f;
+    private static final float TALLER_DEVICE_ASPECT_RATIO_THRESHOLD = 2.15f;
+    private static final float TALL_DEVICE_EXTRA_SPACE_THRESHOLD_DP = 252;
+    private static final float TALL_DEVICE_MORE_EXTRA_SPACE_THRESHOLD_DP = 268;
 
     // To evenly space the icons, increase the left/right margins for tablets in portrait mode.
     private static final int PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER = 4;
 
     // Workspace
-    public final int desiredWorkspaceLeftRightMarginPx;
+    public final int desiredWorkspaceLeftRightOriginalPx;
+    public int desiredWorkspaceLeftRightMarginPx;
+    public final int cellLayoutBorderSpacingOriginalPx;
+    public int cellLayoutBorderSpacingPx;
     public final int cellLayoutPaddingLeftRightPx;
     public final int cellLayoutBottomPaddingPx;
     public final int edgeMarginPx;
     public float workspaceSpringLoadShrinkFactor;
     public final int workspaceSpringLoadedBottomSpace;
 
+    private final int extraSpace;
+    public int workspaceTopPadding;
+    public int workspaceBottomPadding;
+    public int extraHotseatBottomPadding;
+
     // Workspace page indicator
     public final int workspacePageIndicatorHeight;
     private final int mWorkspacePageIndicatorOverlapWorkspace;
 
     // Workspace icons
+    public float iconScale;
     public int iconSizePx;
     public int iconTextSizePx;
     public int iconDrawablePaddingPx;
     public int iconDrawablePaddingOriginalPx;
 
+    public float cellScaleToFit;
     public int cellWidthPx;
     public int cellHeightPx;
     public int workspaceCellPaddingXPx;
 
+    public int cellYPaddingPx;
+
     // Folder
+    public float folderLabelTextScale;
+    public int folderLabelTextSizePx;
     public int folderIconSizePx;
     public int folderIconOffsetYPx;
 
+    // Folder content
+    public int folderCellLayoutBorderSpacingPx;
+    public int folderContentPaddingLeftRight;
+    public int folderContentPaddingTop;
+
     // Folder cell
     public int folderCellWidthPx;
     public int folderCellHeightPx;
@@ -107,27 +152,44 @@
     public int folderChildDrawablePaddingPx;
 
     // Hotseat
+    public int hotseatBarSizeExtraSpacePx;
+    public final int numShownHotseatIcons;
     public int hotseatCellHeightPx;
+    private final int hotseatExtraVerticalSize;
     // In portrait: size = height, in landscape: size = width
     public int hotseatBarSizePx;
-    public final int hotseatBarTopPaddingPx;
-    public int hotseatBarBottomPaddingPx;
+    public int hotseatBarTopPaddingPx;
+    public final int hotseatBarBottomPaddingPx;
     // 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 float qsbBottomMarginOriginalPx;
+    public int qsbBottomMarginPx;
+
     // All apps
+    public int allAppsOpenVerticalTranslate;
     public int allAppsCellHeightPx;
     public int allAppsCellWidthPx;
     public int allAppsIconSizePx;
     public int allAppsIconDrawablePaddingPx;
+    public final int numShownAllAppsColumns;
     public float allAppsIconTextSizePx;
 
+    // Overview
+    public int overviewTaskMarginPx;
+    public int overviewTaskIconSizePx;
+    public int overviewTaskThumbnailTopMarginPx;
+    public final int overviewActionsMarginThreeButtonPx;
+    public final int overviewActionsMarginGesturePx;
+
     // Widgets
     public final PointF appWidgetScale = new PointF(1.0f, 1.0f);
 
     // Drop Target
     public int dropTargetBarSizePx;
+    public int dropTargetDragPaddingPx;
+    public int dropTargetTextSizePx;
 
     // Insets
     private final Rect mInsets = new Rect();
@@ -140,54 +202,103 @@
     public DotRenderer mDotRendererWorkSpace;
     public DotRenderer mDotRendererAllApps;
 
-    DeviceProfile(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info,
-            Point minSize, Point maxSize, int width, int height, boolean isLandscape,
+     // Taskbar
+    public boolean isTaskbarPresent;
+    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;
+
+    DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
             boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
-            Point windowPosition) {
+            boolean useTwoPanels) {
 
         this.inv = inv;
-        this.isLandscape = isLandscape;
+        this.isLandscape = windowBounds.isLandscape();
         this.isMultiWindowMode = isMultiWindowMode;
-        windowX = windowPosition.x;
-        windowY = windowPosition.y;
+        this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
+        windowX = windowBounds.bounds.left;
+        windowY = windowBounds.bounds.top;
+
+        isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
 
         // Determine sizes.
-        widthPx = width;
-        heightPx = height;
-        if (isLandscape) {
-            availableWidthPx = maxSize.x;
-            availableHeightPx = minSize.y;
-        } else {
-            availableWidthPx = minSize.x;
-            availableHeightPx = maxSize.y;
-        }
+        widthPx = windowBounds.bounds.width();
+        heightPx = windowBounds.bounds.height();
+        availableWidthPx = windowBounds.availableSize.x;
+        int nonFinalAvailableHeightPx = 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);
+        isPhone = !isTablet;
+        isTwoPanels = isTablet && useTwoPanels;
 
-        // Constants from resources
-        float swDPs = Utilities.dpiFromPx(
-                Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics);
-        isTablet = swDPs >= TABLET_MIN_DPS;
-        isLargeTablet = swDPs >= LARGE_TABLET_MIN_DPS;
-        isPhone = !isTablet && !isLargeTablet;
         aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
         boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
 
         // Some more constants
-        this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
-
         context = getContext(context, info, isVerticalBarLayout()
                 ? Configuration.ORIENTATION_LANDSCAPE
                 : Configuration.ORIENTATION_PORTRAIT);
+        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;
+            }
+        }
+        availableHeightPx = nonFinalAvailableHeightPx;
+
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
-        desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx;
+
+        desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : isScalableGrid
+                ? res.getDimensionPixelSize(R.dimen.scalable_grid_left_right_margin)
+                : res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
+        desiredWorkspaceLeftRightOriginalPx = desiredWorkspaceLeftRightMarginPx;
+
+
+        allAppsOpenVerticalTranslate = res.getDimensionPixelSize(
+                R.dimen.all_apps_open_vertical_translate);
+
+        folderLabelTextScale = res.getFloat(R.dimen.folder_label_text_scale);
+        folderContentPaddingLeftRight =
+                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;
 
         int cellLayoutPaddingLeftRightMultiplier = !isVerticalBarLayout() && isTablet
                 ? PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER : 1;
-        int cellLayoutPadding = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
-        if (isLandscape) {
+        int cellLayoutPadding = isScalableGrid
+                ? 0
+                : res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
+
+        if (isTwoPanels) {
+            cellLayoutPaddingLeftRightPx =
+                    res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding);
+            cellLayoutBottomPaddingPx = 0;
+        } else if (isLandscape) {
             cellLayoutPaddingLeftRightPx = 0;
             cellLayoutBottomPaddingPx = cellLayoutPadding;
         } else {
@@ -202,12 +313,21 @@
 
         iconDrawablePaddingOriginalPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
+
         dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
+        dropTargetDragPaddingPx = res.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
+        dropTargetTextSizePx = res.getDimensionPixelSize(R.dimen.drop_target_text_size);
+
         workspaceSpringLoadedBottomSpace =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
+        numShownHotseatIcons =
+                isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
+        numShownAllAppsColumns =
+                isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
+        hotseatBarSizeExtraSpacePx = 0;
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx = (isTallDevice ? 0
@@ -217,45 +337,129 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
         // Add a bit of space between nav bar and hotseat in vertical bar layout.
         hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
-        hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, mInfo.metrics)
-                + (isVerticalBarLayout()
-                ? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx)
-                : (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size)
-                        + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx));
+        hotseatExtraVerticalSize =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
+        updateHotseatIconSize(pxFromDp(inv.iconSize, 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);
+        overviewTaskThumbnailTopMarginPx = overviewTaskIconSizePx + overviewTaskMarginPx * 2;
+        overviewActionsMarginGesturePx = res.getDimensionPixelSize(
+                R.dimen.overview_actions_bottom_margin_gesture);
+        overviewActionsMarginThreeButtonPx = res.getDimensionPixelSize(
+                R.dimen.overview_actions_bottom_margin_three_button);
 
         // Calculate all of the remaining variables.
-        updateAvailableDimensions(res);
+        extraSpace = updateAvailableDimensions(res);
 
         // Now that we have all of the variables calculated, we can tune certain sizes.
-        if (!isVerticalBarLayout() && isPhone && isTallDevice) {
+        if (isScalableGrid && inv.devicePaddings != null) {
+            // Paddings were created assuming no scaling, so we first unscale the extra space.
+            int unscaledExtraSpace = (int) (extraSpace / cellScaleToFit);
+            DevicePadding padding = inv.devicePaddings.getDevicePadding(unscaledExtraSpace);
+
+            int paddingWorkspaceTop = padding.getWorkspaceTopPadding(unscaledExtraSpace);
+            int paddingWorkspaceBottom = padding.getWorkspaceBottomPadding(unscaledExtraSpace);
+            int paddingHotseatBottom = padding.getHotseatBottomPadding(unscaledExtraSpace);
+
+            workspaceTopPadding = Math.round(paddingWorkspaceTop * cellScaleToFit);
+            workspaceBottomPadding = Math.round(paddingWorkspaceBottom * cellScaleToFit);
+            extraHotseatBottomPadding = Math.round(paddingHotseatBottom * cellScaleToFit);
+
+            hotseatBarSizePx += extraHotseatBottomPadding;
+
+            qsbBottomMarginPx = Math.round(qsbBottomMarginOriginalPx * cellScaleToFit);
+        } else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
             // We increase the hotseat size when there is extra space.
-            // ie. For a display with a large aspect ratio, we can keep the icons on the workspace
-            // in portrait mode closer together by adding more height to the hotseat.
-            // Note: This calculation was created after noticing a pattern in the design spec.
-            int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2
-                    - workspacePageIndicatorHeight;
-            hotseatBarSizePx += extraSpace;
-            hotseatBarBottomPaddingPx += extraSpace;
+
+            if (Float.compare(aspectRatio, TALLER_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0
+                    && extraSpace >= Utilities.dpToPx(TALL_DEVICE_EXTRA_SPACE_THRESHOLD_DP)) {
+                // For taller devices, we will take a piece of the extra space from each row,
+                // and add it to the space above and below the hotseat.
+
+                // For devices with more extra space, we take a larger piece from each cell.
+                int piece = extraSpace < Utilities.dpToPx(TALL_DEVICE_MORE_EXTRA_SPACE_THRESHOLD_DP)
+                        ? 7 : 5;
+
+                int extraSpace = ((getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2)
+                        * inv.numRows) / piece;
+
+                workspaceTopPadding = extraSpace / 8;
+                int halfLeftOver = (extraSpace - workspaceTopPadding) / 2;
+                hotseatBarTopPaddingPx += halfLeftOver;
+                hotseatBarSizeExtraSpacePx = halfLeftOver;
+            } else {
+                // ie. For a display with a large aspect ratio, we can keep the icons on the
+                // workspace in portrait mode closer together by adding more height to the hotseat.
+                // Note: This calculation was created after noticing a pattern in the design spec.
+                hotseatBarSizeExtraSpacePx = getCellSize().y - iconSizePx
+                        - iconDrawablePaddingPx * 2 - workspacePageIndicatorHeight;
+            }
+
+            updateHotseatIconSize(iconSizePx);
 
             // Recalculate the available dimensions using the new hotseat size.
             updateAvailableDimensions(res);
         }
         updateWorkspacePadding();
 
+        flingToDeleteThresholdVelocity = res.getDimensionPixelSize(
+                R.dimen.drag_flingToDeleteMinVelocity);
+
         // This is done last, after iconSizePx is calculated above.
-        mDotRendererWorkSpace = new DotRenderer(iconSizePx, IconShape.getShapePath(),
-                IconShape.DEFAULT_PATH_SIZE);
+        Path dotPath = GraphicsUtils.getShapePath(DEFAULT_DOT_SIZE);
+        mDotRendererWorkSpace = new DotRenderer(iconSizePx, dotPath, DEFAULT_DOT_SIZE);
         mDotRendererAllApps = iconSizePx == allAppsIconSizePx ? mDotRendererWorkSpace :
-                new DotRenderer(allAppsIconSizePx, IconShape.getShapePath(),
-                        IconShape.DEFAULT_PATH_SIZE);
+                new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE);
+    }
+
+    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);
+        if (isVerticalBarLayout()) {
+            hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx
+                    + hotseatBarSidePaddingEndPx;
+        } else {
+            hotseatBarSizePx = hotseatIconSizePx + hotseatBarTopPaddingPx
+                    + hotseatBarBottomPaddingPx + (isScalableGrid ? 0 : hotseatExtraVerticalSize)
+                    + hotseatBarSizeExtraSpacePx;
+        }
+    }
+
+    private void setCellLayoutBorderSpacing(int borderSpacing) {
+        cellLayoutBorderSpacingPx = isScalableGrid ? borderSpacing : 0;
+    }
+
+    /**
+     * We inset the widget padding added by the system and instead rely on the border spacing
+     * between cells to create reliable consistency between widgets
+     */
+    public boolean shouldInsetWidgets() {
+        Rect widgetPadding = inv.defaultWidgetPadding;
+
+        // 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;
     }
 
     public Builder toBuilder(Context context) {
-        Point size = new Point(availableWidthPx, availableHeightPx);
+        WindowBounds bounds =
+                new WindowBounds(widthPx, heightPx, availableWidthPx, availableHeightPx);
+        bounds.bounds.offsetTo(windowX, windowY);
         return new Builder(context, inv, mInfo)
-                .setSizeRange(size, size)
-                .setSize(widthPx, heightPx)
-                .setWindowPosition(windowX, windowY)
+                .setWindowBounds(bounds)
+                .setUseTwoPanels(isTwoPanels)
                 .setMultiWindowMode(isMultiWindowMode);
     }
 
@@ -267,24 +471,12 @@
      * TODO: Move this to the builder as part of setMultiWindowMode
      */
     public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowBounds) {
-        // We take the minimum sizes of this profile and it's multi-window variant to ensure that
-        // the system decor is always excluded.
-        Point mwSize = new Point(Math.min(availableWidthPx, windowBounds.availableSize.x),
-                Math.min(availableHeightPx, windowBounds.availableSize.y));
-
         DeviceProfile profile = toBuilder(context)
-                .setSizeRange(mwSize, mwSize)
-                .setSize(windowBounds.bounds.width(), windowBounds.bounds.height())
-                .setWindowPosition(windowBounds.bounds.left, windowBounds.bounds.top)
+                .setWindowBounds(windowBounds)
                 .setMultiWindowMode(true)
                 .build();
 
-        // If there isn't enough vertical cell padding with the labels displayed, hide the labels.
-        float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx
-                - iconDrawablePaddingPx - profile.iconTextSizePx;
-        if (workspaceCellPaddingY < profile.iconDrawablePaddingPx * 2) {
-            profile.adjustToHideWorkspaceLabels();
-        }
+        profile.hideWorkspaceLabelsIfNotEnoughSpace();
 
         // We use these scales to measure and layout the widgets using their full invariant profile
         // sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
@@ -297,45 +489,74 @@
     }
 
     /**
-     * Inverse of {@link #getMultiWindowProfile(Context, WindowBounds)}
-     * @return device profile corresponding to the current orientation in non multi-window mode.
-     */
-    public DeviceProfile getFullScreenProfile() {
-        return isLandscape ? inv.landscapeProfile : inv.portraitProfile;
-    }
-
-    /**
-     * Adjusts the profile so that the labels on the Workspace are hidden.
+     * Checks if there is enough space for labels on the workspace.
+     * If there is not, labels on the Workspace are hidden.
      * It is important to call this method after the All Apps variables have been set.
      */
-    private void adjustToHideWorkspaceLabels() {
-        iconTextSizePx = 0;
-        iconDrawablePaddingPx = 0;
-        cellHeightPx = iconSizePx;
-        autoResizeAllAppsCells();
+    private void hideWorkspaceLabelsIfNotEnoughSpace() {
+        float iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx);
+        float workspaceCellPaddingY = getCellSize().y - iconSizePx - iconDrawablePaddingPx
+                - iconTextHeight;
+
+        // We want enough space so that the text is closer to its corresponding icon.
+        if (workspaceCellPaddingY < iconTextHeight) {
+            iconTextSizePx = 0;
+            iconDrawablePaddingPx = 0;
+            cellHeightPx = (int) Math.ceil(iconSizePx * ICON_OVERLAP_FACTOR);
+            autoResizeAllAppsCells();
+        }
     }
 
     /**
      * Re-computes the all-apps cell size to be independent of workspace
      */
     public void autoResizeAllAppsCells() {
-        int topBottomPadding = allAppsIconDrawablePaddingPx * (isVerticalBarLayout() ? 2 : 1);
+        int textHeight = Utilities.calculateTextHeight(allAppsIconTextSizePx);
+        int topBottomPadding = textHeight;
         allAppsCellHeightPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx
-                + Utilities.calculateTextHeight(allAppsIconTextSizePx)
-                + topBottomPadding * 2;
+                + textHeight + (topBottomPadding * 2);
     }
 
-    private void updateAvailableDimensions(Resources res) {
+    /**
+     * Returns the amount of extra (or unused) vertical space.
+     */
+    private int updateAvailableDimensions(Resources res) {
         updateIconSize(1f, res);
 
-        // Check to see if the icons fit within the available height.  If not, then scale down.
-        float usedHeight = (cellHeightPx * inv.numRows);
-        int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y);
-        if (usedHeight > maxHeight) {
-            float scale = maxHeight / usedHeight;
-            updateIconSize(scale, res);
+        Point workspacePadding = getTotalWorkspacePadding();
+
+        // Check to see if the icons fit within the available height.
+        float usedHeight = getCellLayoutHeight();
+        final int maxHeight = availableHeightPx - workspacePadding.y;
+        float extraHeight = Math.max(0, maxHeight - usedHeight);
+        float scaleY = maxHeight / usedHeight;
+        boolean shouldScale = scaleY < 1f;
+
+        float scaleX = 1f;
+        if (isScalableGrid) {
+            // 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);
+            // We do not subtract padding here, as we also scale the workspace padding if needed.
+            scaleX = availableWidthPx / usedWidth;
+            shouldScale = true;
         }
+
+        if (shouldScale) {
+            float scale = Math.min(scaleX, scaleY);
+            updateIconSize(scale, res);
+            extraHeight = Math.max(0, maxHeight - getCellLayoutHeight());
+        }
+
         updateAvailableFolderCellDimensions(res);
+        return Math.round(extraHeight);
+    }
+
+    private int getCellLayoutHeight() {
+        return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacingPx * (inv.numRows - 1));
     }
 
     /**
@@ -343,36 +564,51 @@
      * iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants,
      * hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx.
      */
-    private void updateIconSize(float scale, Resources res) {
+    public void updateIconSize(float scale, Resources res) {
+        // Icon scale should never exceed 1, otherwise pixellation may occur.
+        iconScale = Math.min(1f, scale);
+        cellScaleToFit = scale;
+
+
         // Workspace
         final boolean isVerticalLayout = isVerticalBarLayout();
-        float invIconSizeDp = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
-        iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizeDp, mInfo.metrics)
-                * scale));
-        iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, mInfo.metrics) * scale);
-        iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
+        float invIconSizeDp = isLandscape ? inv.landscapeIconSize : inv.iconSize;
+        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);
 
-        cellHeightPx = iconSizePx + iconDrawablePaddingPx
-                + Utilities.calculateTextHeight(iconTextSizePx);
-        int cellYPadding = (getCellSize().y - cellHeightPx) / 2;
-        if (iconDrawablePaddingPx > cellYPadding && !isVerticalLayout
-                && !isMultiWindowMode) {
-            // Ensures that the label is closer to its corresponding icon. This is not an issue
-            // with vertical bar layout or multi-window mode since the issue is handled separately
-            // with their calls to {@link #adjustToHideWorkspaceLabels}.
-            cellHeightPx -= (iconDrawablePaddingPx - cellYPadding);
-            iconDrawablePaddingPx = cellYPadding;
+        setCellLayoutBorderSpacing((int) (cellLayoutBorderSpacingOriginalPx * scale));
+
+        if (isScalableGrid) {
+            cellWidthPx = pxFromDp(inv.minCellWidth, mMetrics, scale);
+            cellHeightPx = pxFromDp(inv.minCellHeight, mMetrics, scale);
+            int cellContentHeight = iconSizePx + iconDrawablePaddingPx
+                    + Utilities.calculateTextHeight(iconTextSizePx);
+            cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
+            desiredWorkspaceLeftRightMarginPx = (int) (desiredWorkspaceLeftRightOriginalPx * scale);
+        } else {
+            cellWidthPx = iconSizePx + iconDrawablePaddingPx;
+            cellHeightPx = (int) Math.ceil(iconSizePx * ICON_OVERLAP_FACTOR)
+                    + iconDrawablePaddingPx
+                    + Utilities.calculateTextHeight(iconTextSizePx);
+            int cellPaddingY = (getCellSize().y - cellHeightPx) / 2;
+            if (iconDrawablePaddingPx > cellPaddingY && !isVerticalLayout
+                    && !isMultiWindowMode) {
+                // Ensures that the label is closer to its corresponding icon. This is not an issue
+                // with vertical bar layout or multi-window mode since the issue is handled
+                // separately with their calls to {@link #adjustToHideWorkspaceLabels}.
+                cellHeightPx -= (iconDrawablePaddingPx - cellPaddingY);
+                iconDrawablePaddingPx = cellPaddingY;
+            }
         }
-        cellWidthPx = iconSizePx + iconDrawablePaddingPx;
 
         // All apps
-        if (allAppsHasDifferentNumColumns()) {
-            allAppsIconSizePx = ResourceUtils.pxFromDp(inv.allAppsIconSize, mInfo.metrics);
-            allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mInfo.metrics);
+        if (numShownAllAppsColumns != inv.numColumns) {
+            allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics);
+            allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize, mMetrics);
             allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
-            // We use 4 below to ensure labels are closer to their corresponding icon.
-            allAppsCellHeightPx = Math.round(allAppsIconSizePx + allAppsIconTextSizePx
-                    + (4 * allAppsIconDrawablePaddingPx));
+            autoResizeAllAppsCells();
         } else {
             allAppsIconSizePx = iconSizePx;
             allAppsIconTextSizePx = iconTextSizePx;
@@ -381,17 +617,12 @@
         }
         allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
 
-        if (isVerticalBarLayout()) {
-            // Always hide the Workspace text with vertical bar layout.
-            adjustToHideWorkspaceLabels();
+        if (isVerticalLayout) {
+            hideWorkspaceLabelsIfNotEnoughSpace();
         }
 
         // Hotseat
-        if (isVerticalLayout) {
-            hotseatBarSizePx = iconSizePx + hotseatBarSidePaddingStartPx
-                    + hotseatBarSidePaddingEndPx;
-        }
-        hotseatCellHeightPx = iconSizePx;
+        updateHotseatIconSize(iconSizePx);
 
         if (!isVerticalLayout) {
             int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
@@ -411,25 +642,26 @@
     }
 
     private void updateAvailableFolderCellDimensions(Resources res) {
-        int folderBottomPanelSize = res.getDimensionPixelSize(R.dimen.folder_label_padding_top)
-                + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom)
-                + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size));
-
         updateFolderCellSize(1f, res);
 
+        final int folderBottomPanelSize = res.getDimensionPixelSize(R.dimen.folder_label_height);
+
         // Don't let the folder get too close to the edges of the screen.
         int folderMargin = edgeMarginPx * 2;
         Point totalWorkspacePadding = getTotalWorkspacePadding();
 
         // Check if the icons fit within the available height.
-        float contentUsedHeight = folderCellHeightPx * inv.numFolderRows;
+        float contentUsedHeight = folderCellHeightPx * inv.numFolderRows
+                + ((inv.numFolderRows - 1) * folderCellLayoutBorderSpacingPx);
         int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y - folderBottomPanelSize
-                - folderMargin;
+                - folderMargin - folderContentPaddingTop;
         float scaleY = contentMaxHeight / contentUsedHeight;
 
         // Check if the icons fit within the available width.
-        float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns;
-        int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin;
+        float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns
+                + ((inv.numFolderColumns - 1) * folderCellLayoutBorderSpacingPx);
+        int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin
+                - folderContentPaddingLeftRight * 2;
         float scaleX = contentMaxWidth / contentUsedWidth;
 
         float scale = Math.min(scaleX, scaleY);
@@ -439,16 +671,34 @@
     }
 
     private void updateFolderCellSize(float scale, Resources res) {
-        folderChildIconSizePx = (int) (ResourceUtils.pxFromDp(inv.iconSize, mInfo.metrics) * scale);
-        folderChildTextSizePx =
-                (int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale);
+        float invIconSizeDp = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+        folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
+        folderChildTextSizePx = pxFromSp(inv.iconTextSize, mMetrics, scale);
+        folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale);
 
         int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
-        int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding) * scale);
-        int cellPaddingY = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_y_padding) * scale);
 
-        folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX;
-        folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight;
+        if (isScalableGrid) {
+            int minWidth = folderChildIconSizePx + iconDrawablePaddingPx * 2;
+            int minHeight = folderChildIconSizePx + iconDrawablePaddingPx * 2 + textHeight;
+
+            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;
+        } else {
+            int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding)
+                    * scale);
+            int cellPaddingY = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_y_padding)
+                    * scale);
+
+            folderCellWidthPx = folderChildIconSizePx + 2 * cellPaddingX;
+            folderCellHeightPx = folderChildIconSizePx + 2 * cellPaddingY + textHeight;
+        }
+
         folderChildDrawablePaddingPx = Math.max(0,
                 (folderCellHeightPx - folderChildIconSizePx - textHeight) / 3);
     }
@@ -467,18 +717,20 @@
     }
 
     public Point getCellSize() {
-        return getCellSize(inv.numColumns, inv.numRows);
+        return getCellSize(null);
     }
 
-    private Point getCellSize(int numColumns, int numRows) {
-        Point result = new Point();
+    public Point getCellSize(Point result) {
+        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, numColumns);
+                - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, inv.numColumns);
         result.y = calculateCellHeight(availableHeightPx - padding.y
-                - cellLayoutBottomPaddingPx, numRows);
+                - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows);
         return result;
     }
 
@@ -505,8 +757,9 @@
                 padding.right = hotseatBarSizePx;
             }
         } else {
-            int paddingBottom = hotseatBarSizePx + workspacePageIndicatorHeight
-                    - mWorkspacePageIndicatorOverlapWorkspace;
+            int hotseatTop = isTaskbarPresent ? taskbarSize : hotseatBarSizePx;
+            int paddingBottom = hotseatTop + workspacePageIndicatorHeight
+                    + workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace;
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
@@ -515,15 +768,20 @@
                         ((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) - hotseatBarTopPaddingPx
-                        - hotseatBarBottomPaddingPx);
+                        - (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,
-                        edgeMarginPx,
+                        workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx),
                         desiredWorkspaceLeftRightMarginPx,
                         paddingBottom);
             }
@@ -545,7 +803,7 @@
             // for this, we pad the left and right of the hotseat with half of the difference of a
             // workspace cell vs a hotseat cell.
             float workspaceCellWidth = (float) widthPx / inv.numColumns;
-            float hotseatCellWidth = (float) widthPx / inv.numHotseatIcons;
+            float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
             int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
             mHotseatPadding.set(
                     hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx
@@ -553,7 +811,8 @@
                     hotseatBarTopPaddingPx,
                     hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx
                             + mInsets.right,
-                    hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
+                    hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
+                            + cellLayoutBottomPaddingPx + mInsets.bottom);
         }
         return mHotseatPadding;
     }
@@ -570,19 +829,20 @@
                     mInsets.top + availableHeightPx);
         } else {
             // Folders should only appear below the drop target bar and above the hotseat
+            int hotseatTop = isTaskbarPresent ? taskbarSize : hotseatBarSizePx;
             return new Rect(mInsets.left + edgeMarginPx,
                     mInsets.top + dropTargetBarSizePx + edgeMarginPx,
                     mInsets.left + availableWidthPx - edgeMarginPx,
-                    mInsets.top + availableHeightPx - hotseatBarSizePx
+                    mInsets.top + availableHeightPx - hotseatTop
                             - workspacePageIndicatorHeight - edgeMarginPx);
         }
     }
 
-    public static int calculateCellWidth(int width, int countX) {
-        return width / countX;
+    public static int calculateCellWidth(int width, int borderSpacing, int countX) {
+        return (width - ((countX - 1) * borderSpacing)) / countX;
     }
-    public static int calculateCellHeight(int height, int countY) {
-        return height / countY;
+    public static int calculateCellHeight(int height, int borderSpacing, int countY) {
+        return (height - ((countY - 1) * borderSpacing)) / countY;
     }
 
     /**
@@ -595,19 +855,12 @@
     }
 
     /**
-     * Returns true when the number of workspace columns and all apps columns differs.
-     */
-    private boolean allAppsHasDifferentNumColumns() {
-        return inv.numAllAppsColumns != inv.numColumns;
-    }
-
-    /**
      * Updates orientation information and returns true if it has changed from the previous value.
      */
     public boolean updateIsSeascape(Context context) {
         if (isVerticalBarLayout()) {
-            boolean isSeascape = DefaultDisplay.INSTANCE.get(context).getInfo().rotation
-                    == Surface.ROTATION_270;
+            boolean isSeascape = DisplayController.INSTANCE.get(context)
+                    .getInfo().rotation == Surface.ROTATION_270;
             if (mIsSeascape != isSeascape) {
                 mIsSeascape = isSeascape;
                 return true;
@@ -621,27 +874,132 @@
     }
 
     public boolean shouldFadeAdjacentWorkspaceScreens() {
-        return isVerticalBarLayout() || isLargeTablet;
+        return isVerticalBarLayout();
     }
 
-    public int getCellHeight(@ContainerType int containerType) {
+    public int getCellContentHeight(@ContainerType int containerType) {
         switch (containerType) {
             case CellLayout.WORKSPACE:
                 return cellHeightPx;
             case CellLayout.FOLDER:
                 return folderCellHeightPx;
             case CellLayout.HOTSEAT:
-                return hotseatCellHeightPx;
+                // The hotseat is the only container where the cell height is going to be
+                // different from the content within that cell.
+                return iconSizePx;
             default:
                 // ??
                 return 0;
         }
     }
 
-    private static Context getContext(Context c, DefaultDisplay.Info info, int orientation) {
+    private String pxToDpStr(String name, float value) {
+        return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mMetrics.densityDpi) + "dp)";
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        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:"
+                + transposeLayoutWithOrientation);
+
+        writer.println(prefix + "\tisLandscape:" + isLandscape);
+        writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
+        writer.println(prefix + "\tisTwoPanels:" + isTwoPanels);
+
+        writer.println(prefix + pxToDpStr("windowX", windowX));
+        writer.println(prefix + pxToDpStr("windowY", windowY));
+        writer.println(prefix + pxToDpStr("widthPx", widthPx));
+        writer.println(prefix + pxToDpStr("heightPx", heightPx));
+
+        writer.println(prefix + pxToDpStr("availableWidthPx", availableWidthPx));
+        writer.println(prefix + pxToDpStr("availableHeightPx", availableHeightPx));
+
+        writer.println(prefix + "\taspectRatio:" + aspectRatio);
+
+        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 + pxToDpStr("cellWidthPx", cellWidthPx));
+        writer.println(prefix + pxToDpStr("cellHeightPx", cellHeightPx));
+
+        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("iconSizePx", iconSizePx));
+        writer.println(prefix + pxToDpStr("iconTextSizePx", iconTextSizePx));
+        writer.println(prefix + pxToDpStr("iconDrawablePaddingPx", iconDrawablePaddingPx));
+
+        writer.println(prefix + pxToDpStr("folderCellWidthPx", folderCellWidthPx));
+        writer.println(prefix + pxToDpStr("folderCellHeightPx", folderCellHeightPx));
+        writer.println(prefix + pxToDpStr("folderChildIconSizePx", folderChildIconSizePx));
+        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("allAppsIconSizePx", allAppsIconSizePx));
+        writer.println(prefix + pxToDpStr("allAppsIconTextSizePx", allAppsIconTextSizePx));
+        writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx",
+                allAppsIconDrawablePaddingPx));
+        writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx));
+        writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns);
+
+        writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
+        writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
+        writer.println(prefix + pxToDpStr("hotseatBarTopPaddingPx", hotseatBarTopPaddingPx));
+        writer.println(prefix + pxToDpStr("hotseatBarBottomPaddingPx", hotseatBarBottomPaddingPx));
+        writer.println(prefix + pxToDpStr("hotseatBarSidePaddingStartPx",
+                hotseatBarSidePaddingStartPx));
+        writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
+                hotseatBarSidePaddingEndPx));
+        writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
+
+        writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
+
+        writer.println(prefix + pxToDpStr("taskbarSize", taskbarSize));
+        writer.println(prefix + pxToDpStr("nonOverlappingTaskbarInset",
+                nonOverlappingTaskbarInset));
+
+        writer.println(prefix + pxToDpStr("workspacePadding.left", workspacePadding.left));
+        writer.println(prefix + pxToDpStr("workspacePadding.top", workspacePadding.top));
+        writer.println(prefix + pxToDpStr("workspacePadding.right", workspacePadding.right));
+        writer.println(prefix + pxToDpStr("workspacePadding.bottom", workspacePadding.bottom));
+
+        writer.println(prefix + pxToDpStr("iconScale", iconScale));
+        writer.println(prefix + pxToDpStr("cellScaleToFit ", cellScaleToFit));
+        writer.println(prefix + pxToDpStr("extraSpace", extraSpace));
+
+        if (inv.devicePaddings != null) {
+            int unscaledExtraSpace = (int) (extraSpace / iconScale);
+            writer.println(prefix + pxToDpStr("maxEmptySpace",
+                    inv.devicePaddings.getDevicePadding(unscaledExtraSpace).getMaxEmptySpacePx()));
+        }
+        writer.println(prefix + pxToDpStr("workspaceTopPadding", workspaceTopPadding));
+        writer.println(prefix + pxToDpStr("workspaceBottomPadding", workspaceBottomPadding));
+        writer.println(prefix + pxToDpStr("extraHotseatBottomPadding", extraHotseatBottomPadding));
+    }
+
+    private static Context getContext(Context c, Info info, int orientation) {
         Configuration config = new Configuration(c.getResources().getConfiguration());
         config.orientation = orientation;
-        config.densityDpi = info.metrics.densityDpi;
+        config.densityDpi = info.densityDpi;
         return c.createConfigurationContext(config);
     }
 
@@ -662,35 +1020,18 @@
     public static class Builder {
         private Context mContext;
         private InvariantDeviceProfile mInv;
-        private DefaultDisplay.Info mInfo;
+        private Info mInfo;
 
-        private final Point mWindowPosition = new Point();
-        private Point mMinSize, mMaxSize;
-        private int mWidth, mHeight;
+        private WindowBounds mWindowBounds;
+        private boolean mUseTwoPanels;
 
-        private boolean mIsLandscape;
         private boolean mIsMultiWindowMode = false;
-        private boolean mTransposeLayoutWithOrientation;
+        private Boolean mTransposeLayoutWithOrientation;
 
-        public Builder(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info) {
+        public Builder(Context context, InvariantDeviceProfile inv, Info info) {
             mContext = context;
             mInv = inv;
             mInfo = info;
-            mTransposeLayoutWithOrientation = context.getResources()
-                    .getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
-        }
-
-        public Builder setSizeRange(Point minSize, Point maxSize) {
-            mMinSize = minSize;
-            mMaxSize = maxSize;
-            return this;
-        }
-
-        public Builder setSize(int width, int height) {
-            mWidth = width;
-            mHeight = height;
-            mIsLandscape = mWidth > mHeight;
-            return this;
         }
 
         public Builder setMultiWindowMode(boolean isMultiWindowMode) {
@@ -698,11 +1039,14 @@
             return this;
         }
 
-        /**
-         * Sets the window position if not full-screen
-         */
-        public Builder setWindowPosition(int x, int y) {
-            mWindowPosition.set(x, y);
+        public Builder setUseTwoPanels(boolean useTwoPanels) {
+            mUseTwoPanels = useTwoPanels;
+            return this;
+        }
+
+
+        public Builder setWindowBounds(WindowBounds bounds) {
+            mWindowBounds = bounds;
             return this;
         }
 
@@ -712,9 +1056,14 @@
         }
 
         public DeviceProfile build() {
-            return new DeviceProfile(mContext, mInv, mInfo, mMinSize, mMaxSize,
-                    mWidth, mHeight, mIsLandscape, mIsMultiWindowMode,
-                    mTransposeLayoutWithOrientation, mWindowPosition);
+            if (mWindowBounds == null) {
+                throw new IllegalArgumentException("Window bounds not set");
+            }
+            if (mTransposeLayoutWithOrientation == null) {
+                mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
+            }
+            return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds,
+                    mIsMultiWindowMode, mTransposeLayoutWithOrientation, mUseTwoPanels);
         }
     }
 
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index d4d7b99..ba227d4 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -19,12 +19,11 @@
 import android.view.View;
 
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
 
 /**
  * Interface defining an object that can originate a drag.
  */
-public interface DragSource extends LogContainerProvider {
+public interface DragSource {
 
     /**
      * A callback made back to the source after an item from this source has been dropped on a
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index b27abc4..70d8476 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -79,9 +79,7 @@
         public DraggableView originalView = null;
 
         /** Used for matching DROP event with its corresponding DRAG event on the server side. */
-        public final InstanceId logInstanceId =
-                new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
-                    .newInstanceId();
+        public final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
 
         public DragObject(Context context) {
             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
@@ -113,18 +111,6 @@
 
             return res;
         }
-
-
-        /**
-         * This is used to determine if an object is dropped at a different location than it was
-         * dragged from
-         */
-        public boolean isMoved() {
-            return dragInfo.cellX != originalDragInfo.cellX
-                    || dragInfo.cellY != originalDragInfo.cellY
-                    || dragInfo.screenId != originalDragInfo.screenId
-                    || dragInfo.container != originalDragInfo.container;
-        }
     }
 
     /**
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index ca001a3..88f6c49 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.TypedValue;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewDebug;
@@ -104,7 +105,8 @@
                         / (2 * (grid.inv.numColumns + 1)))
                         + grid.edgeMarginPx;
             } else {
-                gap = grid.desiredWorkspaceLeftRightMarginPx - grid.inv.defaultWidgetPadding.right;
+                gap = getContext().getResources()
+                        .getDimensionPixelSize(R.dimen.drop_target_bar_margin_horizontal);
             }
             lp.width = grid.availableWidthPx - 2 * gap;
 
@@ -114,6 +116,7 @@
         }
         setLayoutParams(lp);
         for (ButtonDropTarget button : mDropTargets) {
+            button.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.dropTargetTextSizePx);
             button.setToolTipLocation(tooltipLocation);
         }
     }
@@ -131,7 +134,10 @@
         int width = MeasureSpec.getSize(widthMeasureSpec);
         int height = MeasureSpec.getSize(heightMeasureSpec);
 
-        if (mIsVertical) {
+        int visibleCount = getVisibleButtonsCount();
+        if (visibleCount == 0) {
+            // do nothing
+        } else if (mIsVertical) {
             int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
             int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
 
@@ -142,7 +148,6 @@
                 }
             }
         } else {
-            int visibleCount = getVisibleButtonsCount();
             int availableWidth = width / visibleCount;
             boolean textVisible = true;
             for (ButtonDropTarget buttons : mDropTargets) {
@@ -165,7 +170,10 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (mIsVertical) {
+        int visibleCount = getVisibleButtonsCount();
+        if (visibleCount == 0) {
+            // do nothing
+        } else if (mIsVertical) {
             int gap = getResources().getDimensionPixelSize(R.dimen.drop_target_vertical_gap);
             int start = gap;
             int end;
@@ -178,7 +186,6 @@
                 }
             }
         } else {
-            int visibleCount = getVisibleButtonsCount();
             int frameSize = (right - left) / visibleCount;
 
             int start = frameSize / 2;
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index d64967b..a4e1af6 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3;
 
+import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync;
+
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -24,7 +26,8 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 
-import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.views.ActivityContext;
 
 
 /**
@@ -67,6 +70,9 @@
     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
         // If this is a back key, propagate the key back to the listener
         if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+            if (TextUtils.isEmpty(getText())) {
+                hideKeyboard();
+            }
             if (mBackKeyListener != null) {
                 return mBackKeyListener.onBackKey();
             }
@@ -93,12 +99,15 @@
         }
     }
 
+    // inherited class can override to change the appearance of the edit text.
+    public void show() {}
+
     public void showKeyboard() {
         mShowImeAfterFirstLayout = !showSoftInput();
     }
 
     public void hideKeyboard() {
-        UiThreadHelper.hideKeyboardAsync(getContext(), getWindowToken());
+        hideKeyboardAsync(ActivityContext.lookupContext(getContext()), getWindowToken());
     }
 
     private boolean showSoftInput() {
@@ -131,6 +140,9 @@
         if (!TextUtils.isEmpty(getText())) {
             setText("");
         }
+        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+            return;
+        }
         if (isFocused()) {
             View nextFocus = focusSearch(View.FOCUS_DOWN);
             if (nextFocus != null) {
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
deleted file mode 100644
index d3b86de..0000000
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.Property;
-
-import com.android.launcher3.graphics.PlaceHolderIconDrawable;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.util.Themes;
-
-
-public class FastBitmapDrawable extends Drawable {
-
-    private static final float PRESSED_SCALE = 1.1f;
-
-    private static final float DISABLED_DESATURATION = 1f;
-    private static final float DISABLED_BRIGHTNESS = 0.5f;
-
-    public static final int CLICK_FEEDBACK_DURATION = 200;
-
-    private static ColorFilter sDisabledFColorFilter;
-
-    protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
-    protected Bitmap mBitmap;
-    protected final int mIconColor;
-
-    private boolean mIsPressed;
-    private boolean mIsDisabled;
-    private float mDisabledAlpha = 1f;
-
-    // Animator and properties for the fast bitmap drawable's scale
-    private static final Property<FastBitmapDrawable, Float> SCALE
-            = new Property<FastBitmapDrawable, Float>(Float.TYPE, "scale") {
-        @Override
-        public Float get(FastBitmapDrawable fastBitmapDrawable) {
-            return fastBitmapDrawable.mScale;
-        }
-
-        @Override
-        public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
-            fastBitmapDrawable.mScale = value;
-            fastBitmapDrawable.invalidateSelf();
-        }
-    };
-    private ObjectAnimator mScaleAnimation;
-    private float mScale = 1;
-
-    private int mAlpha = 255;
-
-    public FastBitmapDrawable(Bitmap b) {
-        this(b, Color.TRANSPARENT);
-    }
-
-    public FastBitmapDrawable(BitmapInfo info) {
-        this(info.icon, info.color);
-    }
-
-    protected FastBitmapDrawable(Bitmap b, int iconColor) {
-        this(b, iconColor, false);
-    }
-
-    protected FastBitmapDrawable(Bitmap b, int iconColor, boolean isDisabled) {
-        mBitmap = b;
-        mIconColor = iconColor;
-        setFilterBitmap(true);
-        setIsDisabled(isDisabled);
-    }
-
-    @Override
-    public final void draw(Canvas canvas) {
-        if (mScale != 1f) {
-            int count = canvas.save();
-            Rect bounds = getBounds();
-            canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY());
-            drawInternal(canvas, bounds);
-            canvas.restoreToCount(count);
-        } else {
-            drawInternal(canvas, getBounds());
-        }
-    }
-
-    protected void drawInternal(Canvas canvas, Rect bounds) {
-        canvas.drawBitmap(mBitmap, null, bounds, mPaint);
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        // No op
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        if (mAlpha != alpha) {
-            mAlpha = alpha;
-            mPaint.setAlpha(alpha);
-            invalidateSelf();
-        }
-    }
-
-    @Override
-    public void setFilterBitmap(boolean filterBitmap) {
-        mPaint.setFilterBitmap(filterBitmap);
-        mPaint.setAntiAlias(filterBitmap);
-    }
-
-    public int getAlpha() {
-        return mAlpha;
-    }
-
-    public void setScale(float scale) {
-        if (mScaleAnimation != null) {
-            mScaleAnimation.cancel();
-            mScaleAnimation = null;
-        }
-        mScale = scale;
-        invalidateSelf();
-    }
-
-    public float getAnimatedScale() {
-        return mScaleAnimation == null ? 1 : mScale;
-    }
-
-    public float getScale() {
-        return mScale;
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return mBitmap.getWidth();
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mBitmap.getHeight();
-    }
-
-    @Override
-    public int getMinimumWidth() {
-        return getBounds().width();
-    }
-
-    @Override
-    public int getMinimumHeight() {
-        return getBounds().height();
-    }
-
-    @Override
-    public boolean isStateful() {
-        return true;
-    }
-
-    @Override
-    public ColorFilter getColorFilter() {
-        return mPaint.getColorFilter();
-    }
-
-    @Override
-    protected boolean onStateChange(int[] state) {
-        boolean isPressed = false;
-        for (int s : state) {
-            if (s == android.R.attr.state_pressed) {
-                isPressed = true;
-                break;
-            }
-        }
-        if (mIsPressed != isPressed) {
-            mIsPressed = isPressed;
-
-            if (mScaleAnimation != null) {
-                mScaleAnimation.cancel();
-                mScaleAnimation = null;
-            }
-
-            if (mIsPressed) {
-                // Animate when going to pressed state
-                mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, PRESSED_SCALE);
-                mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
-                mScaleAnimation.setInterpolator(ACCEL);
-                mScaleAnimation.start();
-            } else {
-                if (isVisible()) {
-                    mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, 1f);
-                    mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
-                    mScaleAnimation.setInterpolator(DEACCEL);
-                    mScaleAnimation.start();
-                } else {
-                    mScale = 1f;
-                    invalidateSelf();
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-    public void setIsDisabled(boolean isDisabled) {
-        if (mIsDisabled != isDisabled) {
-            mIsDisabled = isDisabled;
-            updateFilter();
-        }
-    }
-
-    protected boolean isDisabled() {
-        return mIsDisabled;
-    }
-
-    private ColorFilter getDisabledColorFilter() {
-        if (sDisabledFColorFilter == null) {
-            ColorMatrix tempBrightnessMatrix = new ColorMatrix();
-            ColorMatrix tempFilterMatrix = new ColorMatrix();
-
-            tempFilterMatrix.setSaturation(1f - DISABLED_DESATURATION);
-            float scale = 1 - DISABLED_BRIGHTNESS;
-            int brightnessI =   (int) (255 * DISABLED_BRIGHTNESS);
-            float[] mat = tempBrightnessMatrix.getArray();
-            mat[0] = scale;
-            mat[6] = scale;
-            mat[12] = scale;
-            mat[4] = brightnessI;
-            mat[9] = brightnessI;
-            mat[14] = brightnessI;
-            mat[18] = mDisabledAlpha;
-            tempFilterMatrix.preConcat(tempBrightnessMatrix);
-            sDisabledFColorFilter = new ColorMatrixColorFilter(tempFilterMatrix);
-        }
-        return sDisabledFColorFilter;
-    }
-
-    /**
-     * Updates the paint to reflect the current brightness and saturation.
-     */
-    protected void updateFilter() {
-        mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : null);
-        invalidateSelf();
-    }
-
-    @Override
-    public ConstantState getConstantState() {
-        return new MyConstantState(mBitmap, mIconColor, mIsDisabled);
-    }
-
-    protected static class MyConstantState extends ConstantState {
-        protected final Bitmap mBitmap;
-        protected final int mIconColor;
-        protected final boolean mIsDisabled;
-
-        public MyConstantState(Bitmap bitmap, int color, boolean isDisabled) {
-            mBitmap = bitmap;
-            mIconColor = color;
-            mIsDisabled = isDisabled;
-        }
-
-        @Override
-        public FastBitmapDrawable newDrawable() {
-            return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled);
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return 0;
-        }
-    }
-
-    /**
-     * Interface to be implemented by custom {@link BitmapInfo} to handle drawable construction
-     */
-    public interface Factory {
-
-        /**
-         * Called to create a new drawable
-         */
-        FastBitmapDrawable newDrawable();
-    }
-
-    /**
-     * Returns a FastBitmapDrawable with the icon.
-     */
-    public static FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
-        FastBitmapDrawable drawable = newIcon(context, info.bitmap);
-        drawable.setIsDisabled(info.isDisabled());
-        return drawable;
-    }
-
-    /**
-     * Creates a drawable for the provided BitmapInfo
-     */
-    public static FastBitmapDrawable newIcon(Context context, BitmapInfo info) {
-        final FastBitmapDrawable drawable;
-        if (info instanceof Factory) {
-            drawable = ((Factory) info).newDrawable();
-        } else if (info.isLowRes()) {
-            drawable = new PlaceHolderIconDrawable(info, context);
-        } else {
-            drawable = new FastBitmapDrawable(info);
-        }
-        drawable.mDisabledAlpha = Themes.getFloat(context, R.attr.disabledIconAlpha, 1f);
-        return drawable;
-    }
-}
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index 6c5bc40..a199a57 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3;
 
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
deleted file mode 100644
index e5aecf7..0000000
--- a/src/com/android/launcher3/FocusHelper.java
+++ /dev/null
@@ -1,567 +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.
- */
-
-package com.android.launcher3;
-
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.SoundEffectConstants;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderPagedView;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.util.FocusLogic;
-import com.android.launcher3.util.Thunk;
-
-/**
- * A keyboard listener we set on all the workspace icons.
- */
-class IconKeyEventListener implements View.OnKeyListener {
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        return FocusHelper.handleIconKeyEvent(v, keyCode, event);
-    }
-}
-
-/**
- * A keyboard listener we set on all the hotseat buttons.
- */
-class HotseatIconKeyEventListener implements View.OnKeyListener {
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event);
-    }
-}
-
-/**
- * A keyboard listener we set on full screen pages (e.g. custom content).
- */
-class FullscreenKeyEventListener implements View.OnKeyListener {
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
-                || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || keyCode == KeyEvent.KEYCODE_PAGE_UP) {
-            // Handle the key event just like a workspace icon would in these cases. In this case,
-            // it will basically act as if there is a single icon in the top left (so you could
-            // think of the fullscreen page as a focusable fullscreen widget).
-            return FocusHelper.handleIconKeyEvent(v, keyCode, event);
-        }
-        return false;
-    }
-}
-
-/**
- * TODO: Reevaluate if this is still required
- */
-public class FocusHelper {
-
-    private static final String TAG = "FocusHelper";
-    private static final boolean DEBUG = false;
-
-    /**
-     * Handles key events in paged folder.
-     */
-    public static class PagedFolderKeyEventListener implements View.OnKeyListener {
-
-        private final Folder mFolder;
-
-        public PagedFolderKeyEventListener(Folder folder) {
-            mFolder = folder;
-        }
-
-        @Override
-        public boolean onKey(View v, int keyCode, KeyEvent e) {
-            boolean consume = FocusLogic.shouldConsume(keyCode);
-            if (e.getAction() == KeyEvent.ACTION_UP) {
-                return consume;
-            }
-            if (DEBUG) {
-                Log.v(TAG, String.format("Handle ALL Folders keyevent=[%s].",
-                        KeyEvent.keyCodeToString(keyCode)));
-            }
-
-            if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
-                if (FeatureFlags.IS_STUDIO_BUILD) {
-                    throw new IllegalStateException("Parent of the focused item is not supported.");
-                } else {
-                    return false;
-                }
-            }
-
-            // Initialize variables.
-            final ShortcutAndWidgetContainer itemContainer = (ShortcutAndWidgetContainer) v.getParent();
-            final CellLayout cellLayout = (CellLayout) itemContainer.getParent();
-
-            final int iconIndex = itemContainer.indexOfChild(v);
-            final FolderPagedView pagedView = (FolderPagedView) cellLayout.getParent();
-
-            final int pageIndex = pagedView.indexOfChild(cellLayout);
-            final int pageCount = pagedView.getPageCount();
-            final boolean isLayoutRtl = Utilities.isRtl(v.getResources());
-
-            int[][] matrix = FocusLogic.createSparseMatrix(cellLayout);
-            // Process focus.
-            int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
-                    pageCount, isLayoutRtl);
-            if (newIconIndex == FocusLogic.NOOP) {
-                handleNoopKey(keyCode, v);
-                return consume;
-            }
-            ShortcutAndWidgetContainer newParent = null;
-            View child = null;
-
-            switch (newIconIndex) {
-                case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
-                case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
-                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
-                    if (newParent != null) {
-                        int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
-                        pagedView.snapToPage(pageIndex - 1);
-                        child = newParent.getChildAt(
-                                ((newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN)
-                                    ^ newParent.invertLayoutHorizontally()) ? 0 : matrix.length - 1,
-                                row);
-                    }
-                    break;
-                case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
-                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
-                    if (newParent != null) {
-                        pagedView.snapToPage(pageIndex - 1);
-                        child = newParent.getChildAt(0, 0);
-                    }
-                    break;
-                case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
-                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
-                    if (newParent != null) {
-                        pagedView.snapToPage(pageIndex - 1);
-                        child = newParent.getChildAt(matrix.length - 1, matrix[0].length - 1);
-                    }
-                    break;
-                case FocusLogic.NEXT_PAGE_FIRST_ITEM:
-                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1);
-                    if (newParent != null) {
-                        pagedView.snapToPage(pageIndex + 1);
-                        child = newParent.getChildAt(0, 0);
-                    }
-                    break;
-                case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
-                case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
-                    newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1);
-                    if (newParent != null) {
-                        pagedView.snapToPage(pageIndex + 1);
-                        child = FocusLogic.getAdjacentChildInNextFolderPage(
-                                newParent, v, newIconIndex);
-                    }
-                    break;
-                case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
-                    child = cellLayout.getChildAt(0, 0);
-                    break;
-                case FocusLogic.CURRENT_PAGE_LAST_ITEM:
-                    child = pagedView.getLastItem();
-                    break;
-                default: // Go to some item on the current page.
-                    child = itemContainer.getChildAt(newIconIndex);
-                    break;
-            }
-            if (child != null) {
-                child.requestFocus();
-                playSoundEffect(keyCode, v);
-            } else {
-                handleNoopKey(keyCode, v);
-            }
-            return consume;
-        }
-
-        public void handleNoopKey(int keyCode, View v) {
-            if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
-                mFolder.mFolderName.requestFocus();
-                playSoundEffect(keyCode, v);
-            }
-        }
-    }
-
-    /**
-     * Handles key events in the workspace hotseat (bottom of the screen).
-     * <p>Currently we don't special case for the phone UI in different orientations, even though
-     * the hotseat is on the side in landscape mode. This is to ensure that accessibility
-     * consistency is maintained across rotations.
-     */
-    static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e) {
-        boolean consume = FocusLogic.shouldConsume(keyCode);
-        if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
-            return consume;
-        }
-
-        final Launcher launcher = Launcher.getLauncher(v.getContext());
-        final DeviceProfile profile = launcher.getDeviceProfile();
-
-        if (DEBUG) {
-            Log.v(TAG, String.format(
-                    "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
-                    KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
-        }
-
-        // Initialize the variables.
-        final Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
-        final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
-        final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
-
-        final ItemInfo itemInfo = (ItemInfo) v.getTag();
-        int pageIndex = workspace.getNextPage();
-        int pageCount = workspace.getChildCount();
-        int iconIndex = hotseatParent.indexOfChild(v);
-        int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
-                .getChildAt(iconIndex).getLayoutParams()).cellX;
-
-        final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
-        if (iconLayout == null) {
-            // This check is to guard against cases where key strokes rushes in when workspace
-            // child creation/deletion is still in flux. (e.g., during drop or fling
-            // animation.)
-            return consume;
-        }
-        final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
-
-        ViewGroup parent = null;
-        int[][] matrix = null;
-
-        if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
-                !profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
-            iconIndex += iconParent.getChildCount();
-            parent = iconParent;
-        } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
-                profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
-            iconIndex += iconParent.getChildCount();
-            parent = iconParent;
-        } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
-                profile.isVerticalBarLayout()) {
-            keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
-        } else {
-            // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
-            // matrix extended with hotseat.
-            matrix = FocusLogic.createSparseMatrix(hotseatLayout);
-            parent = hotseatParent;
-        }
-
-        // Process the focus.
-        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
-                pageCount, Utilities.isRtl(v.getResources()));
-
-        View newIcon = null;
-        switch (newIconIndex) {
-            case FocusLogic.NEXT_PAGE_FIRST_ITEM:
-                parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
-                newIcon = parent.getChildAt(0);
-                // TODO(hyunyoungs): handle cases where the child is not an icon but
-                // a folder or a widget.
-                workspace.snapToPage(pageIndex + 1);
-                break;
-            case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
-                parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
-                newIcon = parent.getChildAt(0);
-                // TODO(hyunyoungs): handle cases where the child is not an icon but
-                // a folder or a widget.
-                workspace.snapToPage(pageIndex - 1);
-                break;
-            case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
-                parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
-                newIcon = parent.getChildAt(parent.getChildCount() - 1);
-                // TODO(hyunyoungs): handle cases where the child is not an icon but
-                // a folder or a widget.
-                workspace.snapToPage(pageIndex - 1);
-                break;
-            case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
-            case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
-                // Go to the previous page but keep the focus on the same hotseat icon.
-                workspace.snapToPage(pageIndex - 1);
-                break;
-            case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
-            case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
-                // Go to the next page but keep the focus on the same hotseat icon.
-                workspace.snapToPage(pageIndex + 1);
-                break;
-        }
-        if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
-            newIconIndex -= iconParent.getChildCount();
-        }
-        if (parent != null) {
-            if (newIcon == null && newIconIndex >= 0) {
-                newIcon = parent.getChildAt(newIconIndex);
-            }
-            if (newIcon != null) {
-                newIcon.requestFocus();
-                playSoundEffect(keyCode, v);
-            }
-        }
-        return consume;
-    }
-
-    /**
-     * Handles key events in a workspace containing icons.
-     */
-    static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
-        boolean consume = FocusLogic.shouldConsume(keyCode);
-        if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
-            return consume;
-        }
-
-        Launcher launcher = Launcher.getLauncher(v.getContext());
-        DeviceProfile profile = launcher.getDeviceProfile();
-
-        if (DEBUG) {
-            Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s",
-                    KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
-        }
-
-        // Initialize the variables.
-        ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
-        CellLayout iconLayout = (CellLayout) parent.getParent();
-        final Workspace workspace = (Workspace) iconLayout.getParent();
-        final ViewGroup dragLayer = (ViewGroup) workspace.getParent();
-        final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.drop_target_bar);
-        final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat);
-
-        final ItemInfo itemInfo = (ItemInfo) v.getTag();
-        final int iconIndex = parent.indexOfChild(v);
-        final int pageIndex = workspace.indexOfChild(iconLayout);
-        final int pageCount = workspace.getChildCount();
-
-        CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
-        ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
-        int[][] matrix;
-
-        // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed
-        // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
-        // with the hotseat.
-        if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
-        } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
-                profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
-        } else {
-            matrix = FocusLogic.createSparseMatrix(iconLayout);
-        }
-
-        // Process the focus.
-        int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
-                pageCount, Utilities.isRtl(v.getResources()));
-        boolean isRtl = Utilities.isRtl(v.getResources());
-        View newIcon = null;
-        CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex);
-        switch (newIconIndex) {
-            case FocusLogic.NOOP:
-                if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
-                    newIcon = tabs;
-                }
-                break;
-            case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
-            case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
-                int newPageIndex = pageIndex - 1;
-                if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) {
-                    newPageIndex = pageIndex + 1;
-                }
-                int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
-                parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
-                if (parent != null) {
-                    iconLayout = (CellLayout) parent.getParent();
-                    matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout,
-                            iconLayout.getCountX(), row);
-                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT,
-                            newPageIndex, pageCount, Utilities.isRtl(v.getResources()));
-                    if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
-                        newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex,
-                                isRtl);
-                    } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) {
-                        newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex,
-                                isRtl);
-                    } else {
-                        newIcon = parent.getChildAt(newIconIndex);
-                    }
-                }
-                break;
-            case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
-                workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1);
-                newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
-                if (newIcon == null) {
-                    // Check the hotseat if no focusable item was found on the workspace.
-                    newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
-                    workspace.snapToPage(pageIndex - 1);
-                }
-                break;
-            case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
-                newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex, isRtl);
-                break;
-            case FocusLogic.NEXT_PAGE_FIRST_ITEM:
-                newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex, isRtl);
-                break;
-            case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
-            case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
-                newPageIndex = pageIndex + 1;
-                if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
-                    newPageIndex = pageIndex - 1;
-                }
-                row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
-                parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
-                if (parent != null) {
-                    iconLayout = (CellLayout) parent.getParent();
-                    matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout, -1, row);
-                    newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT,
-                            newPageIndex, pageCount, Utilities.isRtl(v.getResources()));
-                    if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
-                        newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex,
-                                isRtl);
-                    } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) {
-                        newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex,
-                                isRtl);
-                    } else {
-                        newIcon = parent.getChildAt(newIconIndex);
-                    }
-                }
-                break;
-            case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
-                newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
-                if (newIcon == null) {
-                    // Check the hotseat if no focusable item was found on the workspace.
-                    newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
-                }
-                break;
-            case FocusLogic.CURRENT_PAGE_LAST_ITEM:
-                newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl);
-                if (newIcon == null) {
-                    // Check the hotseat if no focusable item was found on the workspace.
-                    newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout, isRtl);
-                }
-                break;
-            default:
-                // current page, some item.
-                if (0 <= newIconIndex && newIconIndex < parent.getChildCount()) {
-                    newIcon = parent.getChildAt(newIconIndex);
-                } else if (parent.getChildCount() <= newIconIndex &&
-                        newIconIndex < parent.getChildCount() + hotseatParent.getChildCount()) {
-                    newIcon = hotseatParent.getChildAt(newIconIndex - parent.getChildCount());
-                }
-                break;
-        }
-        if (newIcon != null) {
-            newIcon.requestFocus();
-            playSoundEffect(keyCode, v);
-        }
-        return consume;
-    }
-
-    //
-    // Helper methods.
-    //
-
-    /**
-     * Private helper method to get the CellLayoutChildren given a CellLayout index.
-     */
-    @Thunk static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
-            ViewGroup container, int i) {
-        CellLayout parent = (CellLayout) container.getChildAt(i);
-        return parent.getShortcutsAndWidgets();
-    }
-
-    /**
-     * Helper method to be used for playing sound effects.
-     */
-    @Thunk static void playSoundEffect(int keyCode, View v) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
-                break;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
-                break;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-            case KeyEvent.KEYCODE_PAGE_DOWN:
-            case KeyEvent.KEYCODE_MOVE_END:
-                v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
-                break;
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_PAGE_UP:
-            case KeyEvent.KEYCODE_MOVE_HOME:
-                v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
-                break;
-            default:
-                break;
-        }
-    }
-
-    private static View handlePreviousPageLastItem(Workspace workspace, CellLayout hotseatLayout,
-            int pageIndex, boolean isRtl) {
-        if (pageIndex - 1 < 0) {
-            return null;
-        }
-        CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1);
-        View newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl);
-        if (newIcon == null) {
-            // Check the hotseat if no focusable item was found on the workspace.
-            newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout,isRtl);
-            workspace.snapToPage(pageIndex - 1);
-        }
-        return newIcon;
-    }
-
-    private static View handleNextPageFirstItem(Workspace workspace, CellLayout hotseatLayout,
-            int pageIndex, boolean isRtl) {
-        if (pageIndex + 1 >= workspace.getPageCount()) {
-            return null;
-        }
-        CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex + 1);
-        View newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
-        if (newIcon == null) {
-            // Check the hotseat if no focusable item was found on the workspace.
-            newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
-            workspace.snapToPage(pageIndex + 1);
-        }
-        return newIcon;
-    }
-
-    private static View getFirstFocusableIconInReadingOrder(CellLayout cellLayout, boolean isRtl) {
-        View icon;
-        int countX = cellLayout.getCountX();
-        for (int y = 0; y < cellLayout.getCountY(); y++) {
-            int increment = isRtl ? -1 : 1;
-            for (int x = isRtl ? countX - 1 : 0; 0 <= x && x < countX; x += increment) {
-                if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) {
-                    return icon;
-                }
-            }
-        }
-        return null;
-    }
-
-    private static View getFirstFocusableIconInReverseReadingOrder(CellLayout cellLayout,
-            boolean isRtl) {
-        View icon;
-        int countX = cellLayout.getCountX();
-        for (int y = cellLayout.getCountY() - 1; y >= 0; y--) {
-            int increment = isRtl ? 1 : -1;
-            for (int x = isRtl ? 0 : countX - 1; 0 <= x && x < countX; x += increment) {
-                if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) {
-                    return icon;
-                }
-            }
-        }
-        return null;
-    }
-}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 51f3819..eaca162 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,30 +16,40 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import androidx.annotation.Nullable;
 
-import java.util.ArrayList;
+import java.util.function.Consumer;
 
-public class Hotseat extends CellLayout implements LogContainerProvider, Insettable {
+/**
+ * View class that represents the bottom row of the home screen.
+ */
+public class Hotseat extends CellLayout implements Insettable {
+
+    // Ratio of empty space, qsb should take up to appear visually centered.
+    public static final float QSB_CENTER_FACTOR = .325f;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mHasVerticalHotseat;
     private Workspace mWorkspace;
     private boolean mSendTouchToWorkspace;
+    @Nullable
+    private Consumer<Boolean> mOnVisibilityAggregatedCallback;
+
+    private final View mQsb;
+    private final int mQsbHeight;
+
+    private final int mTaskbarViewHeight;
 
     public Hotseat(Context context) {
         this(context, null);
@@ -51,6 +61,12 @@
 
     public Hotseat(Context context, AttributeSet attrs, int defStyle) {
         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);
     }
 
     /**
@@ -70,29 +86,21 @@
     public void resetLayout(boolean hasVerticalHotseat) {
         removeAllViewsInLayout();
         mHasVerticalHotseat = hasVerticalHotseat;
-        InvariantDeviceProfile idp = mActivity.getDeviceProfile().inv;
+        DeviceProfile dp = mActivity.getDeviceProfile();
         if (hasVerticalHotseat) {
-            setGridSize(1, idp.numHotseatIcons);
+            setGridSize(1, dp.numShownHotseatIcons);
         } else {
-            setGridSize(idp.numHotseatIcons, 1);
+            setGridSize(dp.numShownHotseatIcons, 1);
         }
     }
 
     @Override
-    public void fillInLogContainerData(ItemInfo childInfo, Target child,
-            ArrayList<Target> parents) {
-        child.rank = childInfo.rank;
-        child.gridX = childInfo.cellX;
-        child.gridY = childInfo.cellY;
-        parents.add(newContainerTarget(LauncherLogProto.ContainerType.HOTSEAT));
-    }
-
-    @Override
     public void setInsets(Rect insets) {
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
         DeviceProfile grid = mActivity.getDeviceProfile();
 
         if (grid.isVerticalBarLayout()) {
+            mQsb.setVisibility(View.GONE);
             lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
             if (grid.isSeascape()) {
                 lp.gravity = Gravity.LEFT;
@@ -102,12 +110,21 @@
                 lp.width = grid.hotseatBarSizePx + insets.right;
             }
         } else {
+            mQsb.setVisibility(View.VISIBLE);
             lp.gravity = Gravity.BOTTOM;
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
-            lp.height = grid.hotseatBarSizePx + insets.bottom;
+            lp.height = (grid.isTaskbarPresent
+                        ? grid.workspacePadding.bottom
+                        : grid.hotseatBarSizePx)
+                    + (grid.isTaskbarPresent ? grid.taskbarSize : insets.bottom);
         }
-        Rect padding = grid.getHotseatLayoutPadding();
-        setPadding(padding.left, padding.top, padding.right, padding.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);
+        }
 
         setLayoutParams(lp);
         InsettableFrameLayout.dispatchInsets(this, insets);
@@ -144,4 +161,80 @@
         }
         return event.getY() > getCellHeight();
     }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+
+        if (mOnVisibilityAggregatedCallback != null) {
+            mOnVisibilityAggregatedCallback.accept(isVisible);
+        }
+    }
+
+    /** Sets a callback to be called onVisibilityAggregated */
+    public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
+        mOnVisibilityAggregatedCallback = callback;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        int width = getShortcutsAndWidgets().getMeasuredWidth();
+        mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        int qsbWidth = mQsb.getMeasuredWidth();
+        int left = (r - l - qsbWidth) / 2;
+        int right = left + qsbWidth;
+
+        int bottom = b - t - 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);
+    }
+
+    /**
+     * Returns the QSB inside hotseat
+     */
+    public View getQsb() {
+        return mQsb;
+    }
+
 }
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
deleted file mode 100644
index 62c9b4d..0000000
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ /dev/null
@@ -1,672 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.GraphicsUtils;
-import com.android.launcher3.icons.LauncherIcons;
-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.pm.UserCache;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.Thunk;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONStringer;
-
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-public class InstallShortcutReceiver extends BroadcastReceiver {
-
-    public static final int FLAG_ACTIVITY_PAUSED = 1;
-    public static final int FLAG_LOADER_RUNNING = 2;
-    public static final int FLAG_DRAG_AND_DROP = 4;
-
-    // Determines whether to defer installing shortcuts immediately until
-    // processAllPendingInstalls() is called.
-    private static int sInstallQueueDisabledFlags = 0;
-
-    private static final String TAG = "InstallShortcutReceiver";
-    private static final boolean DBG = false;
-
-    private static final String ACTION_INSTALL_SHORTCUT =
-            "com.android.launcher.action.INSTALL_SHORTCUT";
-
-    private static final String LAUNCH_INTENT_KEY = "intent.launch";
-    private static final String NAME_KEY = "name";
-    private static final String ICON_KEY = "icon";
-    private static final String ICON_RESOURCE_NAME_KEY = "iconResource";
-    private static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage";
-
-    private static final String APP_SHORTCUT_TYPE_KEY = "isAppShortcut";
-    private static final String DEEPSHORTCUT_TYPE_KEY = "isDeepShortcut";
-    private static final String APP_WIDGET_TYPE_KEY = "isAppWidget";
-    private static final String USER_HANDLE_KEY = "userHandle";
-
-    // The set of shortcuts that are pending install
-    private static final String APPS_PENDING_INSTALL = "apps_to_install";
-
-    public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
-    public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
-
-    @WorkerThread
-    private static void addToQueue(Context context, PendingInstallShortcutInfo info) {
-        String encoded = info.encodeToString();
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
-        strings = (strings != null) ? new HashSet<>(strings) : new HashSet<>(1);
-        strings.add(encoded);
-        prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
-    }
-
-    @WorkerThread
-    private static void flushQueueInBackground(Context context) {
-        if (Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null) {
-            // Launcher not loaded
-            return;
-        }
-
-        ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
-        if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
-        if (strings == null) {
-            return;
-        }
-
-        LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
-        for (String encoded : strings) {
-            PendingInstallShortcutInfo info = decode(encoded, context);
-            if (info == null) {
-                continue;
-            }
-
-            String pkg = getIntentPackage(info.launchIntent);
-            if (!TextUtils.isEmpty(pkg)
-                    && !launcherApps.isPackageEnabled(pkg, info.user)
-                    && !info.isActivity) {
-                if (DBG) {
-                    Log.d(TAG, "Ignoring shortcut for absent package: " + info.launchIntent);
-                }
-                continue;
-            }
-
-            // Generate a shortcut info to add into the model
-            installQueue.add(info.getItemInfo());
-        }
-        prefs.edit().remove(APPS_PENDING_INSTALL).apply();
-        if (!installQueue.isEmpty()) {
-            LauncherAppState.getInstance(context).getModel()
-                    .addAndBindAddedWorkspaceItems(installQueue);
-        }
-    }
-
-    public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
-            UserHandle user) {
-        if (packageNames.isEmpty()) {
-            return;
-        }
-        Preconditions.assertWorkerThread();
-
-        SharedPreferences sp = Utilities.getPrefs(context);
-        Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
-        if (DBG) {
-            Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
-                    + ", removing packages: " + packageNames);
-        }
-        if (strings == null || ((Collection) strings).isEmpty()) {
-            return;
-        }
-        Set<String> newStrings = new HashSet<>(strings);
-        Iterator<String> newStringsIter = newStrings.iterator();
-        while (newStringsIter.hasNext()) {
-            String encoded = newStringsIter.next();
-            try {
-                Decoder decoder = new Decoder(encoded, context);
-                if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) &&
-                        user.equals(decoder.user)) {
-                    newStringsIter.remove();
-                }
-            } catch (JSONException | URISyntaxException e) {
-                Log.d(TAG, "Exception reading shortcut to add: " + e);
-                newStringsIter.remove();
-            }
-        }
-        sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
-    }
-
-    public void onReceive(Context context, Intent data) {
-        if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
-            return;
-        }
-        PendingInstallShortcutInfo info = createPendingInfo(context, data);
-        if (info != null) {
-            if (!info.isLauncherActivity()) {
-                // Since its a custom shortcut, verify that it is safe to launch.
-                if (!new PackageManagerHelper(context).hasPermissionForActivity(
-                        info.launchIntent, null)) {
-                    // Target cannot be launched, or requires some special permission to launch
-                    Log.e(TAG, "Ignoring malicious intent " + info.launchIntent.toUri(0));
-                    return;
-                }
-            }
-            queuePendingShortcutInfo(info, context);
-        }
-    }
-
-    /**
-     * @return true is the extra is either null or is of type {@param type}
-     */
-    private static boolean isValidExtraType(Intent intent, String key, Class type) {
-        Object extra = intent.getParcelableExtra(key);
-        return extra == null || type.isInstance(extra);
-    }
-
-    /**
-     * Verifies the intent and creates a {@link PendingInstallShortcutInfo}
-     */
-    private static PendingInstallShortcutInfo createPendingInfo(Context context, Intent data) {
-        if (!isValidExtraType(data, Intent.EXTRA_SHORTCUT_INTENT, Intent.class) ||
-                !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
-                        Intent.ShortcutIconResource.class)) ||
-                !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON, Bitmap.class))) {
-
-            if (DBG) Log.e(TAG, "Invalid install shortcut intent");
-            return null;
-        }
-
-        PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(
-                data, Process.myUserHandle(), context);
-        if (info.launchIntent == null || info.label == null) {
-            if (DBG) Log.e(TAG, "Invalid install shortcut intent");
-            return null;
-        }
-
-        return convertToLauncherActivityIfPossible(info);
-    }
-
-    public static WorkspaceItemInfo fromShortcutIntent(Context context, Intent data) {
-        PendingInstallShortcutInfo info = createPendingInfo(context, data);
-        return info == null ? null : (WorkspaceItemInfo) info.getItemInfo().first;
-    }
-
-    public static void queueShortcut(ShortcutInfo info, Context context) {
-        queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
-    }
-
-    public static void queueWidget(AppWidgetProviderInfo info, int widgetId, Context context) {
-        queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context);
-    }
-
-    public static void queueApplication(Intent data, UserHandle user, Context context) {
-        queuePendingShortcutInfo(new PendingInstallShortcutInfo(data, context, user),
-                context);
-    }
-
-    public static HashSet<ShortcutKey> getPendingShortcuts(Context context) {
-        HashSet<ShortcutKey> result = new HashSet<>();
-
-        Set<String> strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null);
-        if (strings == null || ((Collection) strings).isEmpty()) {
-            return result;
-        }
-
-        for (String encoded : strings) {
-            try {
-                Decoder decoder = new Decoder(encoded, context);
-                if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
-                    result.add(ShortcutKey.fromIntent(decoder.launcherIntent, decoder.user));
-                }
-            } catch (JSONException | URISyntaxException e) {
-                Log.d(TAG, "Exception reading shortcut to add: " + e);
-            }
-        }
-        return result;
-    }
-
-    private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
-        // Queue the item up for adding if launcher has not loaded properly yet
-        MODEL_EXECUTOR.post(() -> addToQueue(context, info));
-        flushInstallQueue(context);
-    }
-
-    public static void enableInstallQueue(int flag) {
-        sInstallQueueDisabledFlags |= flag;
-    }
-    public static void disableAndFlushInstallQueue(int flag, Context context) {
-        sInstallQueueDisabledFlags &= ~flag;
-        flushInstallQueue(context);
-    }
-
-    static void flushInstallQueue(Context context) {
-        if (sInstallQueueDisabledFlags != 0) {
-            return;
-        }
-        MODEL_EXECUTOR.post(() -> flushQueueInBackground(context));
-    }
-
-    /**
-     * Ensures that we have a valid, non-null name.  If the provided name is null, we will return
-     * the application name instead.
-     */
-    @Thunk static CharSequence ensureValidName(Context context, Intent intent, CharSequence name) {
-        if (name == null) {
-            try {
-                PackageManager pm = context.getPackageManager();
-                ActivityInfo info = pm.getActivityInfo(intent.getComponent(), 0);
-                name = info.loadLabel(pm);
-            } catch (PackageManager.NameNotFoundException nnfe) {
-                return "";
-            }
-        }
-        return name;
-    }
-
-    private static class PendingInstallShortcutInfo {
-
-        final boolean isActivity;
-        @Nullable final ShortcutInfo shortcutInfo;
-        @Nullable final AppWidgetProviderInfo providerInfo;
-
-        @Nullable final Intent data;
-        final Context mContext;
-        final Intent launchIntent;
-        final String label;
-        final UserHandle user;
-
-        /**
-         * Initializes a PendingInstallShortcutInfo received from a different app.
-         */
-        public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) {
-            isActivity = false;
-            shortcutInfo = null;
-            providerInfo = null;
-
-            this.data = data;
-            this.user = user;
-            mContext = context;
-
-            launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
-            label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-        }
-
-        /**
-         * Initializes a PendingInstallShortcutInfo to represent a launcher target.
-         */
-        public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) {
-            isActivity = true;
-            shortcutInfo = null;
-            providerInfo = null;
-
-            String packageName = info.getComponentName().getPackageName();
-            data = new Intent();
-            data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
-                    new ComponentName(packageName, "")).setPackage(packageName));
-            data.putExtra(Intent.EXTRA_SHORTCUT_NAME, info.getLabel());
-
-            user = info.getUser();
-            mContext = context;
-
-            launchIntent = AppInfo.makeLaunchIntent(info);
-            label = info.getLabel().toString();
-        }
-
-        /**
-         * Initializes a PendingInstallShortcutInfo to represent a launcher target.
-         */
-        public PendingInstallShortcutInfo(Intent data, Context context, UserHandle user) {
-            isActivity = true;
-            shortcutInfo = null;
-            providerInfo = null;
-
-            this.data = data;
-            this.user = user;
-            mContext = context;
-
-            launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
-            label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-        }
-
-        /**
-         * Initializes a PendingInstallShortcutInfo to represent a launcher target.
-         */
-        public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
-            isActivity = false;
-            shortcutInfo = info;
-            providerInfo = null;
-
-            data = null;
-            mContext = context;
-            user = info.getUserHandle();
-
-            launchIntent = ShortcutKey.makeIntent(info);
-            label = info.getShortLabel().toString();
-        }
-
-        /**
-         * Initializes a PendingInstallShortcutInfo to represent a launcher target.
-         */
-        public PendingInstallShortcutInfo(
-                AppWidgetProviderInfo info, int widgetId, Context context) {
-            isActivity = false;
-            shortcutInfo = null;
-            providerInfo = info;
-
-            data = null;
-            mContext = context;
-            user = info.getProfile();
-
-            launchIntent = new Intent().setComponent(info.provider)
-                    .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
-            label = info.label;
-        }
-
-        public String encodeToString() {
-            try {
-                if (shortcutInfo != null) {
-                    // If it a launcher target, we only need component name, and user to
-                    // recreate this.
-                    return new JSONStringer()
-                            .object()
-                            .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
-                            .key(DEEPSHORTCUT_TYPE_KEY).value(true)
-                            .key(USER_HANDLE_KEY).value(UserCache.INSTANCE.get(mContext)
-                                    .getSerialNumberForUser(user))
-                            .endObject().toString();
-                } else if (providerInfo != null) {
-                    // If it a launcher target, we only need component name, and user to
-                    // recreate this.
-                    return new JSONStringer()
-                            .object()
-                            .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
-                            .key(APP_WIDGET_TYPE_KEY).value(true)
-                            .key(USER_HANDLE_KEY).value(UserCache.INSTANCE.get(mContext)
-                                    .getSerialNumberForUser(user))
-                            .endObject().toString();
-                }
-
-                if (launchIntent.getAction() == null) {
-                    launchIntent.setAction(Intent.ACTION_VIEW);
-                } else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) &&
-                        launchIntent.getCategories() != null &&
-                        launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
-                    launchIntent.addFlags(
-                            Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                }
-
-                // This name is only used for comparisons and notifications, so fall back to activity
-                // name if not supplied
-                String name = ensureValidName(mContext, launchIntent, label).toString();
-                Bitmap icon = data == null ? null
-                        : data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
-                Intent.ShortcutIconResource iconResource = data == null ? null
-                    : data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
-
-                // Only encode the parameters which are supported by the API.
-                JSONStringer json = new JSONStringer()
-                    .object()
-                    .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
-                    .key(NAME_KEY).value(name)
-                    .key(USER_HANDLE_KEY).value(
-                            UserCache.INSTANCE.get(mContext).getSerialNumberForUser(user))
-                    .key(APP_SHORTCUT_TYPE_KEY).value(isActivity);
-                if (icon != null) {
-                    byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
-                    if (iconByteArray != null) {
-                        json = json.key(ICON_KEY).value(
-                                Base64.encodeToString(
-                                        iconByteArray, 0, iconByteArray.length, Base64.DEFAULT));
-                    }
-                }
-                if (iconResource != null) {
-                    json = json.key(ICON_RESOURCE_NAME_KEY).value(iconResource.resourceName);
-                    json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY)
-                            .value(iconResource.packageName);
-                }
-                return json.endObject().toString();
-            } catch (JSONException e) {
-                Log.d(TAG, "Exception when adding shortcut: " + e);
-                return null;
-            }
-        }
-
-        public Pair<ItemInfo, Object> getItemInfo() {
-            if (isActivity) {
-                WorkspaceItemInfo si = createWorkspaceItemInfo(data, user,
-                        LauncherAppState.getInstance(mContext));
-                si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-                si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
-                return Pair.create(si, null);
-            } else if (shortcutInfo != null) {
-                WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
-                LauncherAppState.getInstance(mContext).getIconCache().getShortcutIcon(
-                        itemInfo, shortcutInfo);
-                return Pair.create(itemInfo, shortcutInfo);
-            } else if (providerInfo != null) {
-                LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
-                        .fromProviderInfo(mContext, providerInfo);
-                LauncherAppWidgetInfo widgetInfo = new LauncherAppWidgetInfo(
-                        launchIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0),
-                        info.provider);
-                InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
-                widgetInfo.minSpanX = info.minSpanX;
-                widgetInfo.minSpanY = info.minSpanY;
-                widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
-                widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
-                return Pair.create(widgetInfo, providerInfo);
-            } else {
-                WorkspaceItemInfo itemInfo =
-                        createWorkspaceItemInfo(data, user, LauncherAppState.getInstance(mContext));
-                return Pair.create(itemInfo, null);
-            }
-        }
-
-        public boolean isLauncherActivity() {
-            return isActivity;
-        }
-    }
-
-    private static String getIntentPackage(Intent intent) {
-        return intent.getComponent() == null
-                ? intent.getPackage() : intent.getComponent().getPackageName();
-    }
-
-    private static PendingInstallShortcutInfo decode(String encoded, Context context) {
-        try {
-            Decoder decoder = new Decoder(encoded, context);
-            if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
-                LauncherActivityInfo info = context.getSystemService(LauncherApps.class)
-                        .resolveActivity(decoder.launcherIntent, decoder.user);
-                if (info != null) {
-                    return new PendingInstallShortcutInfo(info, context);
-                }
-            } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
-                List<ShortcutInfo> si = ShortcutKey.fromIntent(decoder.launcherIntent, decoder.user)
-                        .buildRequest(context)
-                        .query(ShortcutRequest.ALL);
-                if (si.isEmpty()) {
-                    return null;
-                } else {
-                    return new PendingInstallShortcutInfo(si.get(0), context);
-                }
-            } else if (decoder.optBoolean(APP_WIDGET_TYPE_KEY)) {
-                int widgetId = decoder.launcherIntent
-                        .getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0);
-                AppWidgetProviderInfo info = AppWidgetManager.getInstance(context)
-                        .getAppWidgetInfo(widgetId);
-                if (info == null || !info.provider.equals(decoder.launcherIntent.getComponent()) ||
-                        !info.getProfile().equals(decoder.user)) {
-                    return null;
-                }
-                return new PendingInstallShortcutInfo(info, widgetId, context);
-            }
-
-            Intent data = new Intent();
-            data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, decoder.launcherIntent);
-            data.putExtra(Intent.EXTRA_SHORTCUT_NAME, decoder.getString(NAME_KEY));
-
-            String iconBase64 = decoder.optString(ICON_KEY);
-            String iconResourceName = decoder.optString(ICON_RESOURCE_NAME_KEY);
-            String iconResourcePackageName = decoder.optString(ICON_RESOURCE_PACKAGE_NAME_KEY);
-            if (iconBase64 != null && !iconBase64.isEmpty()) {
-                byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT);
-                Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length);
-                data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b);
-            } else if (iconResourceName != null && !iconResourceName.isEmpty()) {
-                Intent.ShortcutIconResource iconResource =
-                    new Intent.ShortcutIconResource();
-                iconResource.resourceName = iconResourceName;
-                iconResource.packageName = iconResourcePackageName;
-                data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
-            }
-
-            if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
-                return new PendingInstallShortcutInfo(data, context, decoder.user);
-            } else {
-                return new PendingInstallShortcutInfo(data, decoder.user, context);
-            }
-        } catch (JSONException | URISyntaxException e) {
-            Log.d(TAG, "Exception reading shortcut to add: " + e);
-        }
-        return null;
-    }
-
-    private static class Decoder extends JSONObject {
-        public final Intent launcherIntent;
-        public final UserHandle user;
-
-        private Decoder(String encoded, Context context) throws JSONException, URISyntaxException {
-            super(encoded);
-            launcherIntent = Intent.parseUri(getString(LAUNCH_INTENT_KEY), 0);
-            user = has(USER_HANDLE_KEY) ? UserCache.INSTANCE.get(context)
-                    .getUserForSerialNumber(getLong(USER_HANDLE_KEY))
-                    : Process.myUserHandle();
-            if (user == null) {
-                throw new JSONException("Invalid user");
-            }
-        }
-    }
-
-    /**
-     * Tries to create a new PendingInstallShortcutInfo which represents the same target,
-     * but is an app target and not a shortcut.
-     * @return the newly created info or the original one.
-     */
-    private static PendingInstallShortcutInfo convertToLauncherActivityIfPossible(
-            PendingInstallShortcutInfo original) {
-        if (original.isLauncherActivity()) {
-            // Already an activity target
-            return original;
-        }
-        if (!PackageManagerHelper.isLauncherAppTarget(original.launchIntent)) {
-            return original;
-        }
-
-        LauncherActivityInfo info = original.mContext.getSystemService(LauncherApps.class)
-                .resolveActivity(original.launchIntent, original.user);
-        if (info == null) {
-            return original;
-        }
-        // Ignore any conflicts in the label name, as that can change based on locale.
-        return new PendingInstallShortcutInfo(info, original.mContext);
-    }
-
-    private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, UserHandle user,
-            LauncherAppState app) {
-        if (data == null) {
-            Log.e(TAG, "Can't construct WorkspaceItemInfo with null data");
-            return null;
-        }
-
-        Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
-        String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-        Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
-
-        if (intent == null) {
-            // If the intent is null, return null as we can't construct a valid WorkspaceItemInfo
-            Log.e(TAG, "Can't construct WorkspaceItemInfo with null intent");
-            return null;
-        }
-
-        final WorkspaceItemInfo info = new WorkspaceItemInfo();
-        info.user = user;
-
-        BitmapInfo iconInfo = null;
-        LauncherIcons li = LauncherIcons.obtain(app.getContext());
-        if (bitmap instanceof Bitmap) {
-            iconInfo = li.createIconBitmap((Bitmap) bitmap);
-        } else {
-            Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
-            if (extra instanceof Intent.ShortcutIconResource) {
-                info.iconResource = (Intent.ShortcutIconResource) extra;
-                iconInfo = li.createIconBitmap(info.iconResource);
-            }
-        }
-        li.recycle();
-
-        if (iconInfo == null) {
-            iconInfo = app.getIconCache().getDefaultIcon(info.user);
-        }
-        info.bitmap = iconInfo;
-
-        info.title = Utilities.trim(name);
-        info.contentDescription = app.getContext().getPackageManager()
-                .getUserBadgedLabel(info.title, info.user);
-        info.intent = intent;
-        return info;
-    }
-
-}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index e39e89c..29a0223 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,18 +16,17 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.Utilities.getPointString;
-import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
+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;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 
 import android.annotation.TargetApi;
 import android.appwidget.AppWidgetHostView;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -46,13 +45,13 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.util.ConfigMonitor;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.launcher3.util.DefaultDisplay.Info;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.WindowBounds;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -60,6 +59,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 public class InvariantDeviceProfile {
 
@@ -71,15 +71,13 @@
     public static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
     public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
 
+    private static final int DEFAULT_TRUE = -1;
+    private static final int DEFAULT_SPLIT_DISPLAY = 2;
+
     private static final String KEY_IDP_GRID_NAME = "idp_grid_name";
 
     private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
 
-    public static final int CHANGE_FLAG_GRID = 1 << 0;
-    public static final int CHANGE_FLAG_ICON_PARAMS = 1 << 1;
-
-    public static final String KEY_ICON_PATH_REF = "pref_icon_shape_path";
-
     // Constants that affects the interpolation curve between statically defined device profile
     // buckets.
     private static final float KNEARESTNEIGHBOR = 3;
@@ -88,9 +86,6 @@
     // used to offset float not being able to express extremely small weights in extreme cases.
     private static final float WEIGHT_EFFICIENT = 100000f;
 
-    private static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier(
-            "config_icon_mask", "string", "android");
-
     /**
      * Number of icons per row and column in the workspace.
      */
@@ -103,39 +98,59 @@
     public int numFolderRows;
     public int numFolderColumns;
     public float iconSize;
-    public String iconShapePath;
     public float landscapeIconSize;
+    public float landscapeIconTextSize;
     public int iconBitmapSize;
     public int fillResIconDpi;
     public float iconTextSize;
     public float allAppsIconSize;
     public float allAppsIconTextSize;
 
+    public float minCellHeight;
+    public float minCellWidth;
+    public float borderSpacing;
+
     private SparseArray<TypedValue> mExtraAttrs;
 
     /**
      * Number of icons inside the hotseat area.
      */
-    public int numHotseatIcons;
+    protected int numShownHotseatIcons;
+
+    /**
+     * Number of icons inside the hotseat area that is stored in the database. This is greater than
+     * or equal to numnShownHotseatIcons, allowing for a seamless transition between two hotseat
+     * sizes that share the same DB.
+     */
+    public int numDatabaseHotseatIcons;
 
     /**
      * Number of columns in the all apps list.
      */
     public int numAllAppsColumns;
+    public int numDatabaseAllAppsColumns;
+
+    /**
+     * Do not query directly. see {@link DeviceProfile#isScalableGrid}.
+     */
+    protected boolean isScalable;
+    public int devicePaddingId;
 
     public String dbFile;
     public int defaultLayoutId;
     int demoModeLayoutId;
 
-    public DeviceProfile landscapeProfile;
-    public DeviceProfile portraitProfile;
+    /**
+     * An immutable list of supported profiles.
+     */
+    public List<DeviceProfile> supportedProfiles = Collections.EMPTY_LIST;
+
+    @Nullable public DevicePaddings devicePaddings;
 
     public Point defaultWallpaperSize;
     public Rect defaultWidgetPadding;
 
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
-    private ConfigMonitor mConfigMonitor;
-    private OverlayMonitor mOverlayMonitor;
 
     @VisibleForTesting
     public InvariantDeviceProfile() {}
@@ -146,19 +161,26 @@
         numFolderRows = p.numFolderRows;
         numFolderColumns = p.numFolderColumns;
         iconSize = p.iconSize;
-        iconShapePath = p.iconShapePath;
         landscapeIconSize = p.landscapeIconSize;
         iconBitmapSize = p.iconBitmapSize;
         iconTextSize = p.iconTextSize;
-        numHotseatIcons = p.numHotseatIcons;
+        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;
-        mOverlayMonitor = p.mOverlayMonitor;
+        devicePaddings = p.devicePaddings;
     }
 
     @TargetApi(23)
@@ -169,13 +191,16 @@
             Utilities.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName).apply();
         }
         Utilities.getPrefs(context).edit()
-                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, numHotseatIcons)
+                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, numDatabaseHotseatIcons)
                 .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(numColumns, numRows))
                 .apply();
 
-        mConfigMonitor = new ConfigMonitor(context,
-                APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
-        mOverlayMonitor = new OverlayMonitor(context);
+        DisplayController.INSTANCE.get(context).addChangeListener(
+                (displayContext, info, flags) -> {
+                    if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS)) != 0) {
+                        onConfigChanged(displayContext);
+                    }
+                });
     }
 
     /**
@@ -193,25 +218,32 @@
      */
     public InvariantDeviceProfile(Context context, Display display) {
         // Ensure that the main device profile is initialized
-        InvariantDeviceProfile originalProfile = INSTANCE.get(context);
+        INSTANCE.get(context);
         String gridName = getCurrentGridName(context);
 
         // Get the display info based on default display and interpolate it to existing display
         DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
-                DefaultDisplay.INSTANCE.get(context).getInfo(),
-                getPredefinedDeviceProfiles(context, gridName));
+                DisplayController.INSTANCE.get(context).getInfo(),
+                getPredefinedDeviceProfiles(context, gridName, false), false);
 
         Info myInfo = new Info(context, display);
         DisplayOption myDisplayOption = invDistWeightedInterpolate(
-                myInfo, getPredefinedDeviceProfiles(context, gridName));
+                myInfo, getPredefinedDeviceProfiles(context, gridName, false), false);
 
         DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
                 .add(myDisplayOption);
         result.iconSize = defaultDisplayOption.iconSize;
         result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
-        result.allAppsIconSize = Math.min(
-                defaultDisplayOption.allAppsIconSize, myDisplayOption.allAppsIconSize);
-        initGrid(context, myInfo, result);
+        if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
+            result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
+        } else {
+            result.allAppsIconSize = myDisplayOption.allAppsIconSize;
+        }
+        result.minCellHeight = defaultDisplayOption.minCellHeight;
+        result.minCellWidth = defaultDisplayOption.minCellWidth;
+        result.borderSpacing = defaultDisplayOption.borderSpacing;
+
+        initGrid(context, myInfo, result, false);
     }
 
     public static String getCurrentGridName(Context context) {
@@ -219,48 +251,64 @@
                 ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
     }
 
-    /**
-     * Retrieve system defined or RRO overriden icon shape.
-     */
-    private static String getIconShapePath(Context context) {
-        if (CONFIG_ICON_MASK_RES_ID == 0) {
-            Log.e(TAG, "Icon mask res identifier failed to retrieve.");
-            return "";
-        }
-        return context.getResources().getString(CONFIG_ICON_MASK_RES_ID);
-    }
-
     private String initGrid(Context context, String gridName) {
-        DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(context).getInfo();
-        ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
+        Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
+        // Determine if we have split display
 
-        DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
-        initGrid(context, displayInfo, displayOption);
+        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();
+
+        ArrayList<DisplayOption> allOptions =
+                getPredefinedDeviceProfiles(context, gridName, isSplitDisplay);
+        DisplayOption displayOption =
+                invDistWeightedInterpolate(displayInfo, allOptions, isSplitDisplay);
+        initGrid(context, displayInfo, displayOption, isSplitDisplay);
         return displayOption.grid.name;
     }
 
     private void initGrid(
-            Context context, DefaultDisplay.Info displayInfo, DisplayOption displayOption) {
+            Context context, Info displayInfo, DisplayOption displayOption,
+            boolean isSplitDisplay) {
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         GridOption closestProfile = displayOption.grid;
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
-        numHotseatIcons = closestProfile.numHotseatIcons;
         dbFile = closestProfile.dbFile;
         defaultLayoutId = closestProfile.defaultLayoutId;
         demoModeLayoutId = closestProfile.demoModeLayoutId;
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
-        numAllAppsColumns = closestProfile.numAllAppsColumns;
+        isScalable = closestProfile.isScalable;
+        devicePaddingId = closestProfile.devicePaddingId;
 
         mExtraAttrs = closestProfile.extraAttrs;
 
         iconSize = displayOption.iconSize;
-        iconShapePath = getIconShapePath(context);
         landscapeIconSize = displayOption.landscapeIconSize;
-        iconBitmapSize = ResourceUtils.pxFromDp(iconSize, displayInfo.metrics);
+        iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
         iconTextSize = displayOption.iconTextSize;
+        landscapeIconTextSize = displayOption.landscapeIconTextSize;
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
+        minCellHeight = displayOption.minCellHeight;
+        minCellWidth = displayOption.minCellWidth;
+        borderSpacing = displayOption.borderSpacing;
+
+        numShownHotseatIcons = closestProfile.numHotseatIcons;
+        numDatabaseHotseatIcons = isSplitDisplay
+                ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
+
+        numAllAppsColumns = closestProfile.numAllAppsColumns;
+        numDatabaseAllAppsColumns = isSplitDisplay
+                ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
+
         if (Utilities.isGridOptionsEnabled(context)) {
             allAppsIconSize = displayOption.allAppsIconSize;
             allAppsIconTextSize = displayOption.allAppsIconTextSize;
@@ -269,32 +317,36 @@
             allAppsIconTextSize = iconTextSize;
         }
 
+        if (devicePaddingId != 0) {
+            devicePaddings = new DevicePaddings(context, devicePaddingId);
+        }
+
         // If the partner customization apk contains any grid overrides, apply them
         // Supported overrides: numRows, numColumns, iconSize
-        applyPartnerDeviceProfileOverrides(context, displayInfo.metrics);
+        applyPartnerDeviceProfileOverrides(context, metrics);
 
-        Point realSize = new Point(displayInfo.realSize);
-        // The real size never changes. smallSide and largeSide will remain the
-        // same in any orientation.
-        int smallSide = Math.min(realSize.x, realSize.y);
-        int largeSide = Math.max(realSize.x, realSize.y);
+        final List<DeviceProfile> localSupportedProfiles = new ArrayList<>();
+        defaultWallpaperSize = new Point(displayInfo.currentSize);
+        for (WindowBounds bounds : displayInfo.supportedBounds) {
+            localSupportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
+                    .setUseTwoPanels(isSplitDisplay)
+                    .setWindowBounds(bounds).build());
 
-        DeviceProfile.Builder builder = new DeviceProfile.Builder(context, this, displayInfo)
-                .setSizeRange(new Point(displayInfo.smallestSize),
-                        new Point(displayInfo.largestSize));
+            // Wallpaper size should be the maximum of the all possible sizes Launcher expects
+            int displayWidth = bounds.bounds.width();
+            int displayHeight = bounds.bounds.height();
+            defaultWallpaperSize.y = Math.max(defaultWallpaperSize.y, displayHeight);
 
-        landscapeProfile = builder.setSize(largeSide, smallSide).build();
-        portraitProfile = builder.setSize(smallSide, largeSide).build();
-
-        // We need to ensure that there is enough extra space in the wallpaper
-        // for the intended parallax effects
-        if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) {
-            defaultWallpaperSize = new Point(
-                    (int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)),
-                    largeSide);
-        } else {
-            defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
+            // We need to ensure that there is enough extra space in the wallpaper
+            // for the intended parallax effects
+            float parallaxFactor =
+                    dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.densityDpi) < 720
+                            ? 2
+                            : wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight);
+            defaultWallpaperSize.x =
+                    Math.max(defaultWallpaperSize.x, Math.round(parallaxFactor * displayWidth));
         }
+        supportedProfiles = Collections.unmodifiableList(localSupportedProfiles);
 
         ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
@@ -313,23 +365,6 @@
         mChangeListeners.remove(listener);
     }
 
-    private void killProcess(Context context) {
-        Log.e("ConfigMonitor", "restarting launcher");
-        android.os.Process.killProcess(android.os.Process.myPid());
-    }
-
-    public void verifyConfigChangedInBackground(final Context context) {
-        String savedIconMaskPath = getDevicePrefs(context).getString(KEY_ICON_PATH_REF, "");
-        // Good place to check if grid size changed in themepicker when launcher was dead.
-        if (savedIconMaskPath.isEmpty()) {
-            getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
-                    .apply();
-        } else if (!savedIconMaskPath.equals(getIconShapePath(context))) {
-            getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
-                    .apply();
-            apply(context, CHANGE_FLAG_ICON_PARAMS);
-        }
-    }
 
     public void setCurrentGrid(Context context, String gridName) {
         Context appContext = context.getApplicationContext();
@@ -338,44 +373,17 @@
     }
 
     private void onConfigChanged(Context context) {
-        // Config changes, what shall we do?
-        InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
-
         // Re-init grid
         String gridName = getCurrentGridName(context);
         initGrid(context, gridName);
 
-        int changeFlags = 0;
-        if (numRows != oldProfile.numRows ||
-                numColumns != oldProfile.numColumns ||
-                numFolderColumns != oldProfile.numFolderColumns ||
-                numFolderRows != oldProfile.numFolderRows ||
-                numHotseatIcons != oldProfile.numHotseatIcons) {
-            changeFlags |= CHANGE_FLAG_GRID;
-        }
-
-        if (iconSize != oldProfile.iconSize || iconBitmapSize != oldProfile.iconBitmapSize ||
-                !iconShapePath.equals(oldProfile.iconShapePath)) {
-            changeFlags |= CHANGE_FLAG_ICON_PARAMS;
-        }
-        if (!iconShapePath.equals(oldProfile.iconShapePath)) {
-            IconShape.init(context);
-        }
-
-        apply(context, changeFlags);
-    }
-
-    private void apply(Context context, int changeFlags) {
-        // Create a new config monitor
-        mConfigMonitor.unregister();
-        mConfigMonitor = new ConfigMonitor(context, this::onConfigChanged);
-
         for (OnIDPChangeListener listener : mChangeListeners) {
-            listener.onIdpChanged(changeFlags, this);
+            listener.onIdpChanged(this);
         }
     }
 
-    static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) {
+    private static ArrayList<DisplayOption> getPredefinedDeviceProfiles(
+            Context context, String gridName, boolean isSplitDisplay) {
         ArrayList<DisplayOption> profiles = new ArrayList<>();
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
             final int depth = parser.getDepth();
@@ -392,8 +400,9 @@
                             && type != XmlPullParser.END_DOCUMENT) {
                         if ((type == XmlPullParser.START_TAG) && "display-option".equals(
                                 parser.getName())) {
-                            profiles.add(new DisplayOption(
-                                    gridOption, context, Xml.asAttributeSet(parser)));
+                            profiles.add(new DisplayOption(gridOption, context,
+                                    Xml.asAttributeSet(parser),
+                                    isSplitDisplay ? DEFAULT_SPLIT_DISPLAY : DEFAULT_TRUE));
                         }
                     }
                 }
@@ -464,17 +473,29 @@
         return (float) Math.hypot(x1 - x0, y1 - y0);
     }
 
-    @VisibleForTesting
-    static DisplayOption invDistWeightedInterpolate(
-            DefaultDisplay.Info displayInfo, ArrayList<DisplayOption> points) {
-        Point smallestSize = new Point(displayInfo.smallestSize);
-        Point largestSize = new Point(displayInfo.largestSize);
+    private static DisplayOption invDistWeightedInterpolate(
+            Info displayInfo, ArrayList<DisplayOption> points, boolean isSplitDisplay) {
+        int minWidthPx = Integer.MAX_VALUE;
+        int minHeightPx = Integer.MAX_VALUE;
+        for (WindowBounds bounds : displayInfo.supportedBounds) {
+            boolean isTablet = displayInfo.isTablet(bounds);
+            if (isTablet && isSplitDisplay) {
+                // For split displays, take half width per page
+                minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2);
+                minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
 
-        // This guarantees that width < height
-        float width = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y),
-                displayInfo.metrics);
-        float height = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y),
-                displayInfo.metrics);
+            } else if (!isTablet && bounds.isLandscape()) {
+                // We will use transposed layout in this case
+                minWidthPx = Math.min(minWidthPx, bounds.availableSize.y);
+                minHeightPx = Math.min(minHeightPx, bounds.availableSize.x);
+            } else {
+                minWidthPx = Math.min(minWidthPx, bounds.availableSize.x);
+                minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
+            }
+        }
+
+        float width = dpiFromPx(minWidthPx, displayInfo.densityDpi);
+        float height = dpiFromPx(minHeightPx, displayInfo.densityDpi);
 
         // Sort the profiles based on the closeness to the device size
         Collections.sort(points, (a, b) ->
@@ -499,29 +520,25 @@
         return out.multiply(1.0f / weights);
     }
 
-    @VisibleForTesting
-    static DisplayOption invDistWeightedInterpolate(float width, float height,
-            ArrayList<DisplayOption> points) {
-        float weights = 0;
-
-        DisplayOption p = points.get(0);
-        if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
-            return p;
-        }
-
-        DisplayOption out = new DisplayOption();
-        for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
-            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);
-    }
-
     public DeviceProfile getDeviceProfile(Context context) {
-        return context.getResources().getConfiguration().orientation
-                == Configuration.ORIENTATION_LANDSCAPE ? landscapeProfile : portraitProfile;
+        Resources res = context.getResources();
+        Configuration config = context.getResources().getConfiguration();
+
+        float availableWidth = config.screenWidthDp * res.getDisplayMetrics().density;
+        float availableHeight = config.screenHeightDp * res.getDisplayMetrics().density;
+
+        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);
+            if (diff < minDiff) {
+                minDiff = diff;
+                bestMatch = profile;
+            }
+        }
+        return bestMatch;
     }
 
     private static float weight(float x0, float y0, float x1, float y1, float pow) {
@@ -563,7 +580,10 @@
 
     public interface OnIDPChangeListener {
 
-        void onIdpChanged(int changeFlags, InvariantDeviceProfile profile);
+        /**
+         * Called when the device provide changes
+         */
+        void onIdpChanged(InvariantDeviceProfile profile);
     }
 
 
@@ -578,14 +598,19 @@
         private final int numFolderRows;
         private final int numFolderColumns;
 
+        private final int numAllAppsColumns;
+        private final int numDatabaseAllAppsColumns;
         private final int numHotseatIcons;
+        private final int numDatabaseHotseatIcons;
 
         private final String dbFile;
-        private final int numAllAppsColumns;
 
         private final int defaultLayoutId;
         private final int demoModeLayoutId;
 
+        private final boolean isScalable;
+        private final int devicePaddingId;
+
         private final SparseArray<TypedValue> extraAttrs;
 
         public GridOption(Context context, AttributeSet attrs) {
@@ -600,36 +625,54 @@
                     R.styleable.GridDisplayOption_defaultLayoutId, 0);
             demoModeLayoutId = a.getResourceId(
                     R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
+
+            numAllAppsColumns = a.getInt(
+                    R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
+            numDatabaseAllAppsColumns = a.getInt(
+                    R.styleable.GridDisplayOption_numExtendedAllAppsColumns, 2 * numAllAppsColumns);
+
             numHotseatIcons = a.getInt(
                     R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
+            numDatabaseHotseatIcons = a.getInt(
+                    R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
+
             numFolderRows = a.getInt(
                     R.styleable.GridDisplayOption_numFolderRows, numRows);
             numFolderColumns = a.getInt(
                     R.styleable.GridDisplayOption_numFolderColumns, numColumns);
-            numAllAppsColumns = a.getInt(
-                    R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
+
+            isScalable = a.getBoolean(
+                    R.styleable.GridDisplayOption_isScalable, false);
+            devicePaddingId = a.getResourceId(
+                    R.styleable.GridDisplayOption_devicePaddingId, 0);
 
             a.recycle();
-
             extraAttrs = Themes.createValueMap(context, attrs,
                     IntArray.wrap(R.styleable.GridDisplayOption));
         }
     }
 
-    private static final class DisplayOption {
-        private final GridOption grid;
+    @VisibleForTesting
+    static final class DisplayOption {
+
+        public final GridOption grid;
 
         private final float minWidthDps;
         private final float minHeightDps;
         private final boolean canBeDefault;
 
+        private float minCellHeight;
+        private float minCellWidth;
+        private float borderSpacing;
+
         private float iconSize;
         private float iconTextSize;
         private float landscapeIconSize;
+        private float landscapeIconTextSize;
         private float allAppsIconSize;
         private float allAppsIconTextSize;
 
-        DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
+        DisplayOption(GridOption grid, Context context, AttributeSet attrs, int defaultFlagValue) {
             this.grid = grid;
 
             TypedArray a = context.obtainStyledAttributes(
@@ -637,13 +680,20 @@
 
             minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0);
             minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
-            canBeDefault = a.getBoolean(
-                    R.styleable.ProfileDisplayOption_canBeDefault, false);
+
+            canBeDefault = a.getInt(R.styleable.ProfileDisplayOption_canBeDefault, 0)
+                    == defaultFlagValue;
+
+            minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
+            minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
+            borderSpacing = a.getFloat(R.styleable.ProfileDisplayOption_borderSpacingDps, 0);
 
             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);
 
             allAppsIconSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
                     iconSize);
@@ -661,6 +711,9 @@
             minWidthDps = 0;
             minHeightDps = 0;
             canBeDefault = false;
+            minCellHeight = 0;
+            minCellWidth = 0;
+            borderSpacing = 0;
         }
 
         private DisplayOption multiply(float w) {
@@ -668,7 +721,11 @@
             landscapeIconSize *= w;
             allAppsIconSize *= w;
             iconTextSize *= w;
+            landscapeIconTextSize *= w;
             allAppsIconTextSize *= w;
+            minCellHeight *= w;
+            minCellWidth *= w;
+            borderSpacing *= w;
             return this;
         }
 
@@ -677,22 +734,12 @@
             landscapeIconSize += p.landscapeIconSize;
             allAppsIconSize += p.allAppsIconSize;
             iconTextSize += p.iconTextSize;
+            landscapeIconTextSize += p.landscapeIconTextSize;
             allAppsIconTextSize += p.allAppsIconTextSize;
+            minCellHeight += p.minCellHeight;
+            minCellWidth += p.minCellWidth;
+            borderSpacing += p.borderSpacing;
             return this;
         }
     }
-
-    private class OverlayMonitor extends BroadcastReceiver {
-
-        private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
-
-        OverlayMonitor(Context context) {
-            context.registerReceiver(this, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            onConfigChanged(context);
-        }
-    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ae89ded..099f256 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -19,13 +19,13 @@
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
 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_ICON_SURFACE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
-import static com.android.launcher3.InstallShortcutReceiver.FLAG_DRAG_AND_DROP;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
@@ -34,16 +34,21 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.NO_OFFSET;
 import static com.android.launcher3.LauncherState.NO_SCALE;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
 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.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
-import static com.android.launcher3.logging.StatsLogManager.containerTypeToAtomState;
+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;
@@ -56,7 +61,10 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
-import android.app.ActivityOptions;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.content.ActivityNotFoundException;
@@ -70,12 +78,15 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.StrictMode;
+import android.os.SystemClock;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.Log;
@@ -88,8 +99,10 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
+import android.widget.ImageView;
 import android.widget.Toast;
 
 import androidx.annotation.CallSuper;
@@ -99,6 +112,7 @@
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.LauncherAction;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AllAppsTransitionController;
@@ -109,25 +123,28 @@
 import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.folder.Folder;
+import com.android.launcher3.dragndrop.LauncherDragController;
 import com.android.launcher3.folder.FolderGridOrganizer;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.keyboard.CustomActionsPopup;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.ItemInstallQueue;
+import com.android.launcher3.model.ModelUtils;
 import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.WidgetsModel;
 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.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.PinRequestHelper;
@@ -138,7 +155,6 @@
 import com.android.launcher3.qsb.QsbContainerView;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
-import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.testing.TestLogging;
@@ -146,9 +162,6 @@
 import com.android.launcher3.touch.AllAppsSwipeController;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.ComponentKey;
@@ -161,7 +174,6 @@
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
@@ -173,16 +185,17 @@
 import com.android.launcher3.views.FloatingSurfaceView;
 import com.android.launcher3.views.OptionsPopupView;
 import com.android.launcher3.views.ScrimView;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
-import com.android.launcher3.widget.WidgetHostViewLoader;
-import com.android.launcher3.widget.WidgetListRowEntry;
 import com.android.launcher3.widget.WidgetManagerHelper;
-import com.android.launcher3.widget.WidgetsFullSheet;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.shared.LauncherExterns;
@@ -260,10 +273,8 @@
     private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
     @Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500;
 
-    private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
-    private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
+    private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
 
-    private LauncherAppTransitionManager mAppTransitionManager;
     private Configuration mOldConfig;
 
     @Thunk
@@ -297,8 +308,6 @@
     @Thunk
     boolean mWorkspaceLoading = true;
 
-    private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
-
     // Used to notify when an activity launch has been deferred because launcher is not yet resumed
     // TODO: See if we can remove this later
     private Runnable mOnDeferredActivityLaunchCallback;
@@ -334,18 +343,23 @@
 
     private RotationHelper mRotationHelper;
 
-    private float mCurrentAssistantVisibility = 0f;
-
     protected LauncherOverlayManager mOverlayManager;
     // If true, overlay callbacks are deferred
     private boolean mDeferOverlayCallbacks;
     private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
 
-    private long mLastTouchUpTime = -1;
+    protected long mLastTouchUpTime = -1;
     private boolean mTouchInProgress;
 
     private SafeCloseable mUserChangedCallbackCloseable;
 
+    // New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions.
+    // When Launcher is not in AllApps state mAllAppsSessionLogId will be null.
+    // User actions within AllApps state are logged with this InstanceId, to recreate AllApps
+    // session on the server side.
+    protected InstanceId mAllAppsSessionLogId;
+    private LauncherState mPrevLauncherState;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
@@ -365,6 +379,44 @@
                     .build());
         }
 
+        if (Utilities.IS_DEBUG_DEVICE && FeatureFlags.NOTIFY_CRASHES.get()) {
+            final String notificationChannelId = "com.android.launcher3.Debug";
+            final String notificationChannelName = "Debug";
+            final String notificationTag = "Debug";
+            final int notificationId = 0;
+
+            NotificationManager notificationManager = getSystemService(NotificationManager.class);
+            notificationManager.createNotificationChannel(new NotificationChannel(
+                    notificationChannelId, notificationChannelName,
+                    NotificationManager.IMPORTANCE_HIGH));
+
+            Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
+                String stackTrace = Log.getStackTraceString(throwable);
+
+                Intent shareIntent = new Intent(Intent.ACTION_SEND);
+                shareIntent.setType("text/plain");
+                shareIntent.putExtra(Intent.EXTRA_TEXT, stackTrace);
+                shareIntent = Intent.createChooser(shareIntent, null);
+                PendingIntent sharePendingIntent = PendingIntent.getActivity(
+                        this, 0, shareIntent, PendingIntent.FLAG_UPDATE_CURRENT
+                );
+
+                Notification notification = new Notification.Builder(this, notificationChannelId)
+                        .setSmallIcon(android.R.drawable.ic_menu_close_clear_cancel)
+                        .setContentTitle("Launcher crash detected!")
+                        .setStyle(new Notification.BigTextStyle().bigText(stackTrace))
+                        .addAction(android.R.drawable.ic_menu_share, "Share", sharePendingIntent)
+                        .build();
+                notificationManager.notify(notificationTag, notificationId, notification);
+
+                Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler =
+                        Thread.getDefaultUncaughtExceptionHandler();
+                if (defaultUncaughtExceptionHandler != null) {
+                    defaultUncaughtExceptionHandler.uncaughtException(thread, throwable);
+                }
+            });
+        }
+
         super.onCreate(savedInstanceState);
 
         LauncherAppState app = LauncherAppState.getInstance(this);
@@ -377,26 +429,23 @@
         idp.addOnChangeListener(this);
         mSharedPrefs = Utilities.getPrefs(this);
         mIconCache = app.getIconCache();
-        mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
+        mAccessibilityDelegate = createAccessibilityDelegate();
 
-        mDragController = new DragController(this);
+        mDragController = new LauncherDragController(this);
         mAllAppsController = new AllAppsTransitionController(this);
         mStateManager = new StateManager<>(this, NORMAL);
 
         mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
 
         mAppWidgetManager = new WidgetManagerHelper(this);
-        mAppWidgetHost = new LauncherAppWidgetHost(this,
-                appWidgetId -> getWorkspace().removeWidget(appWidgetId));
+        mAppWidgetHost = createAppWidgetHost();
         mAppWidgetHost.startListening();
 
         inflateRootView(R.layout.launcher);
         setupViews();
+        crossFadeWithPreviousAppearance();
         mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
 
-        mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
-        mAppTransitionManager.registerRemoteAnimations();
-
         boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
         if (internalStateHandled) {
             if (savedInstanceState != null) {
@@ -444,28 +493,14 @@
                 OverlayPlugin.class, false /* allowedMultiple */);
 
         mRotationHelper.initialize();
-
-        mStateManager.addStateListener(new StateListener<LauncherState>() {
-
-            @Override
-            public void onStateTransitionComplete(LauncherState finalState) {
-                float alpha = 1f - mCurrentAssistantVisibility;
-                if (finalState == NORMAL) {
-                    mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
-                } else if (finalState == OVERVIEW || finalState == OVERVIEW_PEEK) {
-                    mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
-                    mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
-                } else {
-                    mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
-                    mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
-                }
-            }
-        });
-
         TraceHelper.INSTANCE.endSection(traceToken);
 
         mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener(
                 () -> getStateManager().goToState(NORMAL));
+
+        if (Utilities.ATLEAST_R) {
+            getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+        }
     }
 
     protected LauncherOverlayManager getDefaultOverlay() {
@@ -518,7 +553,6 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         int diff = newConfig.diff(mOldConfig);
-
         if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
             onIdpChanged(mDeviceProfile.inv);
         }
@@ -528,13 +562,7 @@
     }
 
     @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
-        onIdpChanged(idp);
-    }
-
-    private void onIdpChanged(InvariantDeviceProfile idp) {
-        mUserEventDispatcher = null;
-
+    public void onIdpChanged(InvariantDeviceProfile idp) {
         initDeviceProfile(idp);
         dispatchDeviceProfileChanged();
         reapplyUi();
@@ -547,15 +575,15 @@
     }
 
     public void onAssistantVisibilityChanged(float visibility) {
-        mCurrentAssistantVisibility = visibility;
-        float alpha = 1f - visibility;
-        LauncherState state = mStateManager.getState();
-        if (state == NORMAL) {
-            mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
-        } else if (state == OVERVIEW || state == OVERVIEW_PEEK) {
-            mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
-            mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
-        }
+        mHotseat.getQsb().setAlpha(1f - visibility);
+    }
+
+    /**
+     * Called when one handed mode activated and deactivated.
+     * @param activated true if one handed mode activated, false otherwise.
+     */
+    public void onOneHandedStateChanged(boolean activated) {
+        mDragLayer.onOneHandedModeStateChanged(activated);
     }
 
     private void initDeviceProfile(InvariantDeviceProfile idp) {
@@ -657,6 +685,7 @@
                 completeAddAppWidget(appWidgetId, info, null, null);
                 break;
             case REQUEST_RECONFIGURE_APPWIDGET:
+                mStatsLogManager.logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
                 completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
                 break;
             case REQUEST_BIND_PENDING_APPWIDGET: {
@@ -818,7 +847,7 @@
 
             if (grantResults.length > 0
                     && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                startActivitySafely(v, intent, null, null);
+                startActivitySafely(v, intent, null);
             } else {
                 // TODO: Show a snack bar with link to settings
                 Toast.makeText(this, getString(R.string.msg_no_phone_permission,
@@ -887,9 +916,9 @@
         } else {
             mOverlayManager.onActivityStopped(this);
         }
-
-        logStopAndResume(Action.Command.STOP);
-        mAppWidgetHost.setListenIfResumed(false);
+        hideKeyboard();
+        logStopAndResume(false /* isResume */);
+        mAppWidgetHost.setActivityStarted(false);
         NotificationListener.removeNotificationsChangedListener();
     }
 
@@ -902,59 +931,51 @@
             mOverlayManager.onActivityStarted(this);
         }
 
-        mAppWidgetHost.setListenIfResumed(true);
+        mAppWidgetHost.setActivityStarted(true);
         TraceHelper.INSTANCE.endSection(traceToken);
     }
 
     @Override
     @CallSuper
     protected void onDeferredResumed() {
-        logStopAndResume(Action.Command.RESUME);
-        getUserEventDispatcher().startSession();
-
-        AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
+        logStopAndResume(true /* isResume */);
 
         // Process any items that were added while Launcher was away.
-        InstallShortcutReceiver.disableAndFlushInstallQueue(
-                InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
+        ItemInstallQueue.INSTANCE.get(this)
+                .resumeModelPush(FLAG_ACTIVITY_PAUSED);
 
         // Refresh shortcuts if the permission changed.
-        mModel.refreshShortcutsIfRequired();
+        mModel.validateModelDataOnResume();
 
         // Set the notification listener and fetch updated notifications when we resume
         NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
 
         DiscoveryBounce.showForHomeIfNeeded(this);
+        mAppWidgetHost.setActivityResumed(true);
     }
 
-    protected void handlePendingActivityRequest() { }
-
-    private void logStopAndResume(int command) {
+    private void logStopAndResume(boolean isResume) {
         if (mPendingExecutor != null) return;
         int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
-        int containerType = mStateManager.getState().containerType;
+        int statsLogOrdinal = mStateManager.getState().statsLogOrdinal;
 
         StatsLogManager.EventEnum event;
         StatsLogManager.StatsLogger logger = getStatsLogManager().logger();
-        if (command == Action.Command.RESUME) {
+        if (isResume) {
             logger.withSrcState(LAUNCHER_STATE_BACKGROUND)
-                .withDstState(containerTypeToAtomState(mStateManager.getState().containerType));
+                .withDstState(mStateManager.getState().statsLogOrdinal);
             event = LAUNCHER_ONRESUME;
         } else { /* command == Action.Command.STOP */
-            logger.withSrcState(containerTypeToAtomState(mStateManager.getState().containerType))
+            logger.withSrcState(mStateManager.getState().statsLogOrdinal)
                     .withDstState(LAUNCHER_STATE_BACKGROUND);
             event = LAUNCHER_ONSTOP;
         }
 
-        if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
-            getUserEventDispatcher().logActionCommand(command,
-                    containerType, -1, pageIndex);
+        if (statsLogOrdinal == LAUNCHER_STATE_HOME && mWorkspace != null) {
             logger.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
                     .setWorkspace(
                             LauncherAtom.WorkspaceContainer.newBuilder()
                                     .setPageIndex(pageIndex)).build());
-        } else {
-            getUserEventDispatcher().logActionCommand(command, containerType, -1);
         }
         logger.log(event);
     }
@@ -1011,7 +1032,7 @@
         if (state == SPRING_LOADED) {
             // Prevent any Un/InstallShortcutReceivers from updating the db while we are
             // not on homescreen
-            InstallShortcutReceiver.enableInstallQueue(FLAG_DRAG_AND_DROP);
+            ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_DRAG_AND_DROP);
             getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
 
             mWorkspace.showPageIndicatorAtCurrentScroll();
@@ -1019,12 +1040,25 @@
         }
         // When multiple pages are visible, show persistent page indicator
         mWorkspace.getPageIndicator().setShouldAutoHide(!state.hasFlag(FLAG_MULTI_PAGE));
+
+        mPrevLauncherState = mStateManager.getCurrentStableState();
+        if (mPrevLauncherState != state && ALL_APPS.equals(state)
+                // Making sure mAllAppsSessionLogId is null to avoid double logging.
+                && mAllAppsSessionLogId == null) {
+            // creates new instance ID since new all apps session is started.
+            mAllAppsSessionLogId = new InstanceIdSequence().newInstanceId();
+            getStatsLogManager()
+                    .logger()
+                    .log(FeatureFlags.ENABLE_DEVICE_SEARCH.get()
+                            ? LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH
+                            : LAUNCHER_ALLAPPS_ENTRY);
+        }
     }
 
     @Override
     public void onStateSetEnd(LauncherState state) {
-        super.onStateSetStart(state);
-        getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+        super.onStateSetEnd(state);
+        getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL);
         getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
 
         finishAutoCancelActionMode();
@@ -1036,11 +1070,26 @@
 
         if (state == NORMAL) {
             // Re-enable any Un/InstallShortcutReceiver and now process any queued items
-            InstallShortcutReceiver.disableAndFlushInstallQueue(FLAG_DRAG_AND_DROP, this);
+            ItemInstallQueue.INSTANCE.get(this)
+                    .resumeModelPush(FLAG_DRAG_AND_DROP);
 
             // Clear any rotation locks when going to normal state
             getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
         }
+
+        if (ALL_APPS.equals(mPrevLauncherState) && !ALL_APPS.equals(state)
+                // Making sure mAllAppsSessionLogId is not null to avoid double logging.
+                && mAllAppsSessionLogId != null) {
+            getAppsView().reset(false);
+            getStatsLogManager().logger()
+                    .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                            .setWorkspace(
+                                    LauncherAtom.WorkspaceContainer.newBuilder()
+                                            .setPageIndex(getWorkspace().getCurrentPage()))
+                            .build())
+                    .log(LAUNCHER_ALLAPPS_EXIT);
+            mAllAppsSessionLogId = null;
+        }
     }
 
     @Override
@@ -1049,15 +1098,6 @@
                 TraceHelper.FLAG_UI_EVENT);
         super.onResume();
 
-        if (!mOnResumeCallbacks.isEmpty()) {
-            final ArrayList<OnResumeCallback> resumeCallbacks = new ArrayList<>(mOnResumeCallbacks);
-            mOnResumeCallbacks.clear();
-            for (int i = resumeCallbacks.size() - 1; i >= 0; i--) {
-                resumeCallbacks.get(i).onLauncherResume();
-            }
-            resumeCallbacks.clear();
-        }
-
         if (mDeferOverlayCallbacks) {
             scheduleDeferredCheck();
         } else {
@@ -1070,7 +1110,7 @@
     @Override
     protected void onPause() {
         // Ensure that items added to Launcher are queued until Launcher returns
-        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
+        ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
 
         super.onPause();
         mDragController.cancelDrag();
@@ -1080,6 +1120,7 @@
         if (!mDeferOverlayCallbacks) {
             mOverlayManager.onActivityPaused(this);
         }
+        mAppWidgetHost.setActivityResumed(false);
     }
 
     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1161,8 +1202,7 @@
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         mDropTargetBar.setup(mDragController);
-
-        mAllAppsController.setupViews(mAppsView, mScrimView);
+        mAllAppsController.setupViews(mScrimView, mAppsView);
     }
 
     /**
@@ -1205,16 +1245,13 @@
         int[] cellXY = mTmpAddItemCellCoordinates;
         CellLayout layout = getCellLayout(container, screenId);
 
-        WorkspaceItemInfo info = null;
-        if (Utilities.ATLEAST_OREO) {
-            info = PinRequestHelper.createWorkspaceItemFromPinItemRequest(
+        WorkspaceItemInfo info = PinRequestHelper.createWorkspaceItemFromPinItemRequest(
                     this, PinRequestHelper.getPinItemRequest(data), 0);
-        }
 
         if (info == null) {
             // Legacy shortcuts are only supported for primary profile.
             info = Process.myUserHandle().equals(args.user)
-                    ? InstallShortcutReceiver.fromShortcutIntent(this, data) : null;
+                    ? ModelUtils.fromLegacyShortcutIntent(this, data) : null;
 
             if (info == null) {
                 Log.e(TAG, "Unable to parse a valid custom shortcut result");
@@ -1288,25 +1325,68 @@
             appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
         }
 
+        if (hostView == null) {
+            // Perform actual inflation because we're live
+            hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+        }
+
         LauncherAppWidgetInfo launcherInfo;
-        launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider);
+        launcherInfo =
+                new LauncherAppWidgetInfo(
+                        appWidgetId, appWidgetInfo.provider, appWidgetInfo, hostView);
         launcherInfo.spanX = itemInfo.spanX;
         launcherInfo.spanY = itemInfo.spanY;
         launcherInfo.minSpanX = itemInfo.minSpanX;
         launcherInfo.minSpanY = itemInfo.minSpanY;
         launcherInfo.user = appWidgetInfo.getProfile();
+        if (itemInfo instanceof PendingAddWidgetInfo) {
+            launcherInfo.sourceContainer = ((PendingAddWidgetInfo) itemInfo).sourceContainer;
+        } else if (itemInfo instanceof PendingRequestArgs) {
+            launcherInfo.sourceContainer =
+                    ((PendingRequestArgs) itemInfo).getWidgetSourceContainer();
+        }
 
         getModelWriter().addItemToDatabase(launcherInfo,
                 itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
 
-        if (hostView == null) {
-            // Perform actual inflation because we're live
-            hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
-        }
         hostView.setVisibility(View.VISIBLE);
         prepareAppWidget(hostView, launcherInfo);
         mWorkspace.addInScreen(hostView, launcherInfo);
         announceForAccessibility(R.string.item_added_to_workspace);
+
+        // Show the widget resize frame.
+        if (hostView instanceof LauncherAppWidgetHostView) {
+            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();
+            } else {
+                mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
+                    @Override
+                    public void onStateTransitionComplete(LauncherState finalState) {
+                        if (mPrevLauncherState == SPRING_LOADED && finalState == NORMAL) {
+                            AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
+                            mStateManager.removeStateListener(this);
+                        }
+                    }
+                });
+            }
+        }
     }
 
     private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) {
@@ -1319,14 +1399,7 @@
     private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // Reset AllApps to its initial state only if we are not in the middle of
-            // processing a multi-step drop
-            if (mPendingRequestArgs == null) {
-                if (!isInState(NORMAL)) {
-                    onUiChangedWhileSleeping();
-                }
-                mStateManager.goToState(NORMAL);
-            }
+            onScreenOff();
         }
     };
 
@@ -1352,6 +1425,16 @@
     public Object onRetainNonConfigurationInstance() {
         NonConfigInstance instance = new NonConfigInstance();
         instance.config = new Configuration(mOldConfig);
+
+        int width = mDragLayer.getWidth();
+        int height = mDragLayer.getHeight();
+
+        if (FeatureFlags.ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE.get()
+                && width > 0
+                && height > 0) {
+            instance.snapshot =
+                    BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw);
+        }
         return instance;
     }
 
@@ -1384,6 +1467,7 @@
         return mDropTargetBar;
     }
 
+    @Override
     public ScrimView getScrimView() {
         return mScrimView;
     }
@@ -1392,6 +1476,11 @@
         return mAppWidgetHost;
     }
 
+    protected LauncherAppWidgetHost createAppWidgetHost() {
+        return new LauncherAppWidgetHost(this,
+                appWidgetId -> getWorkspace().removeWidget(appWidgetId));
+    }
+
     public LauncherModel getModel() {
         return mModel;
     }
@@ -1430,8 +1519,8 @@
         boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL)
                 && AbstractFloatingView.getTopOpenView(this) == null;
         boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
-        boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this, intent);
-
+        boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this);
+        hideKeyboard();
         if (isActionMain) {
             if (!internalStateHandled) {
                 // In all these cases, only animate if we're already on home
@@ -1440,8 +1529,7 @@
                 if (!isInState(NORMAL)) {
                     // Only change state, if not already the same. This prevents cancelling any
                     // animations running as part of resume
-                    mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange(),
-                            this::handlePendingActivityRequest);
+                    mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange());
                 }
 
                 // Reset the apps view
@@ -1454,26 +1542,23 @@
                 }
             }
 
-            // Handle HOME_INTENT
-            UserEventDispatcher ued = getUserEventDispatcher();
-            Target target = newContainerTarget(mStateManager.getState().containerType);
-            target.pageIndex = mWorkspace.getCurrentPage();
-            ued.logActionCommand(Action.Command.HOME_INTENT, target,
-                    newContainerTarget(ContainerType.WORKSPACE));
-            hideKeyboard();
-
             if (mLauncherCallbacks != null) {
                 mLauncherCallbacks.onHomeIntent(internalStateHandled);
             }
             mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
             handleGestureContract(intent);
         } else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
-            getStateManager().goToState(ALL_APPS, alreadyOnHome);
+            showAllAppsFromIntent(alreadyOnHome);
         }
 
         TraceHelper.INSTANCE.endSection(traceToken);
     }
 
+    protected void showAllAppsFromIntent(boolean alreadyOnHome) {
+        AbstractFloatingView.closeAllOpenViews(this);
+        getStateManager().goToState(ALL_APPS, alreadyOnHome);
+    }
+
     /**
      * Handles gesture nav contract
      */
@@ -1561,9 +1646,7 @@
         LauncherAppState.getIDP(this).removeOnChangeListener(this);
 
         mOverlayManager.onActivityDestroyed(this);
-        mAppTransitionManager.unregisterRemoteAnimations();
         mUserChangedCallbackCloseable.close();
-        mAllAppsController.onActivityDestroyed();
     }
 
     public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -1757,15 +1840,40 @@
         return newFolder;
     }
 
-    /**
-     * Called when a workspace item is converted into a folder
-     */
-    public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo){}
+    @Override
+    public Rect getFolderBoundingBox() {
+        // We need to bound the folder to the currently visible workspace area
+        return getWorkspace().getPageAreaRelativeToDragLayer();
+    }
 
-    /**
-     * Called when a folder is converted into a workspace item
-     */
-    public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {}
+    @Override
+    public void updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height) {
+        int left = inOutPosition[0];
+        int top = inOutPosition[1];
+        DeviceProfile grid = getDeviceProfile();
+        int distFromEdgeOfScreen = getWorkspace().getPaddingLeft();
+        if (grid.isPhone && (grid.availableWidthPx - width) < 4 * distFromEdgeOfScreen) {
+            // Center the folder if it is very close to being centered anyway, by virtue of
+            // filling the majority of the viewport. ie. remove it from the uncanny valley
+            // of centeredness.
+            left = (grid.availableWidthPx - width) / 2;
+        } else if (width >= bounds.width()) {
+            // If the folder doesn't fit within the bounds, center it about the desired bounds
+            left = bounds.left + (bounds.width() - width) / 2;
+        }
+        if (height >= bounds.height()) {
+            // Folder height is greater than page height, center on page
+            top = bounds.top + (bounds.height() - height) / 2;
+        } else {
+            // Folder height is less than page height, so bound it to the absolute open folder
+            // bounds if necessary
+            Rect folderBounds = grid.getAbsoluteOpenFolderBounds();
+            left = Math.max(folderBounds.left, Math.min(left, folderBounds.right - width));
+            top = Math.max(folderBounds.top, Math.min(top, folderBounds.bottom - height));
+        }
+        inOutPosition[0] = left;
+        inOutPosition[1] = top;
+    }
 
     /**
      * Unbinds the view for the specified item, and removes the item and all its children.
@@ -1820,7 +1928,7 @@
                 mTouchInProgress = true;
                 break;
             case MotionEvent.ACTION_UP:
-                mLastTouchUpTime = System.currentTimeMillis();
+                mLastTouchUpTime = SystemClock.uptimeMillis();
                 // Follow through
             case MotionEvent.ACTION_CANCEL:
                 mTouchInProgress = false;
@@ -1858,14 +1966,15 @@
         }
     }
 
-    @TargetApi(Build.VERSION_CODES.M)
-    @Override
-    public ActivityOptions getActivityLaunchOptions(View v) {
-        return mAppTransitionManager.getActivityLaunchOptions(this, v);
-    }
-
-    public LauncherAppTransitionManager getAppTransitionManager() {
-        return mAppTransitionManager;
+    protected void onScreenOff() {
+        // Reset AllApps to its initial state only if we are not in the middle of
+        // processing a multi-step drop
+        if (mPendingRequestArgs == null) {
+            if (!isInState(NORMAL)) {
+                onUiChangedWhileSleeping();
+            }
+            mStateManager.goToState(NORMAL);
+        }
     }
 
     @TargetApi(Build.VERSION_CODES.M)
@@ -1890,13 +1999,12 @@
     }
 
     @Override
-    public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
-            @Nullable String sourceContainer) {
+    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
         if (!hasBeenResumed()) {
             // Workaround an issue where the WM launch animation is clobbered when finishing the
             // recents animation into launcher. Defer launching the activity until Launcher is
             // next resumed.
-            addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
+            addOnResumeCallback(() -> startActivitySafely(v, intent, item));
             if (mOnDeferredActivityLaunchCallback != null) {
                 mOnDeferredActivityLaunchCallback.run();
                 mOnDeferredActivityLaunchCallback = null;
@@ -1904,7 +2012,7 @@
             return true;
         }
 
-        boolean success = super.startActivitySafely(v, intent, item, sourceContainer);
+        boolean success = super.startActivitySafely(v, intent, item);
         if (success && v instanceof BubbleTextView) {
             // This is set to the view that launched the activity that navigated the user away
             // from launcher. Since there is no callback for when the activity has finished
@@ -1912,7 +2020,7 @@
             // state when we return to launcher.
             BubbleTextView btv = (BubbleTextView) v;
             btv.setStayPressed(true);
-            addOnResumeCallback(btv);
+            addOnResumeCallback(() -> btv.setStayPressed(false));
         }
         return success;
     }
@@ -1951,15 +2059,11 @@
         // Populate event with a fake title based on the current state.
         // TODO: When can workspace be null?
         text.add(mWorkspace == null
-                ? getString(R.string.all_apps_home_button_label)
+                ? getString(R.string.home_screen)
                 : mStateManager.getState().getDescription(this));
         return result;
     }
 
-    public void addOnResumeCallback(OnResumeCallback callback) {
-        mOnResumeCallbacks.add(callback);
-    }
-
     /**
      * Persistant callback which notifies when an activity launch is deferred because the activity
      * was not yet resumed.
@@ -2102,12 +2206,29 @@
      */
     @Override
     public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
+        bindItems(items, forceAnimateIcons, /* focusFirstItemForAccessibility= */ false);
+    }
+
+
+    /**
+     * Bind the items start-end from the list.
+     *
+     * Implementation of the method from LauncherModel.Callbacks.
+     *
+     * @param focusFirstItemForAccessibility true iff the first item to be added to the workspace
+     *                                       should be focused for accessibility.
+     */
+    public void bindItems(
+            final List<ItemInfo> items,
+            final boolean forceAnimateIcons,
+            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();
         Workspace workspace = mWorkspace;
         int newItemsScreenId = -1;
         int end = items.size();
+        View newView = null;
         for (int i = 0; i < end; i++) {
             final ItemInfo item = items.get(i);
 
@@ -2172,12 +2293,25 @@
                 bounceAnims.add(createNewAppBounceAnimation(view, i));
                 newItemsScreenId = item.screenId;
             }
+
+            if (newView == null) {
+                newView = view;
+            }
         }
 
-        // Animate to the correct page
+        View viewToFocus = newView;
+        // Animate to the correct pager
         if (animateIcons && newItemsScreenId > -1) {
             AnimatorSet anim = new AnimatorSet();
             anim.playTogether(bounceAnims);
+            if (focusFirstItemForAccessibility && viewToFocus != null) {
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                    }
+                });
+            }
 
             int currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
             final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
@@ -2200,13 +2334,12 @@
             } else {
                 mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
             }
+        } else if (focusFirstItemForAccessibility && viewToFocus != null) {
+            viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
         }
         workspace.requestLayout();
     }
 
-    @Override
-    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
-
     /**
      * Add the views for a widget to the workspace.
      */
@@ -2237,24 +2370,45 @@
 
         try {
             final LauncherAppWidgetProviderInfo appWidgetInfo;
+            String removalReason = "";
 
             if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
                 // If the provider is not ready, bind as a pending widget.
                 appWidgetInfo = null;
+                removalReason = "the provider isn't ready.";
             } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
                 // The widget id is not valid. Try to find the widget based on the provider info.
                 appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
+                if (appWidgetInfo == null) {
+                    if (WidgetsModel.GO_DISABLE_WIDGETS) {
+                        removalReason = "widgets are disabled on go device.";
+                    } else {
+                        removalReason =
+                                "WidgetManagerHelper cannot find a provider from provider info.";
+                    }
+                }
             } else {
                 appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
+                if (appWidgetInfo == null) {
+                    if (item.appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) {
+                        removalReason =
+                                "CustomWidgetManager cannot find provider from that widget id.";
+                    } else {
+                        removalReason = "AppWidgetManager cannot find provider for that widget id."
+                                + " It could be because AppWidgetService is not available, or the"
+                                + " appWidgetId has not been bound to a the provider yet, or you"
+                                + " don't have access to that appWidgetId.";
+                    }
+                }
             }
 
             // If the provider is ready, but the width is not yet restored, try to restore it.
             if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
                     && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
                 if (appWidgetInfo == null) {
-                    Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
-                            + " belongs to component " + item.providerName
-                            + ", as the provider is null");
+                    FileLog.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+                            + " belongs to component " + item.providerName + " user " + item.user
+                            + ", as the provider is null and " + removalReason);
                     getModelWriter().deleteItemFromDatabase(item);
                     return null;
                 }
@@ -2268,13 +2422,13 @@
 
                         // Also try to bind the widget. If the bind fails, the user will be shown
                         // a click to setup UI, which will ask for the bind permission.
-                        PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo);
+                        PendingAddWidgetInfo pendingInfo =
+                                new PendingAddWidgetInfo(appWidgetInfo, item.sourceContainer);
                         pendingInfo.spanX = item.spanX;
                         pendingInfo.spanY = item.spanY;
                         pendingInfo.minSpanX = item.minSpanX;
                         pendingInfo.minSpanY = item.minSpanY;
-                        Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this,
-                                pendingInfo);
+                        Bundle options = pendingInfo.getDefaultSizeOptions(this);
 
                         boolean isDirectConfig =
                                 item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
@@ -2435,8 +2589,8 @@
             mPendingActivityResult = null;
         }
 
-        InstallShortcutReceiver.disableAndFlushInstallQueue(
-                InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
+        ItemInstallQueue.INSTANCE.get(this)
+                .resumeModelPush(FLAG_LOADER_RUNNING);
 
         // 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,
@@ -2456,15 +2610,15 @@
         if (mDragController.isDragging()) {
             return false;
         } else {
-            return (System.currentTimeMillis() - mLastTouchUpTime)
+            return (SystemClock.uptimeMillis() - mLastTouchUpTime)
                     > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
         }
     }
 
     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
         ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
-                .setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
-        bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
+                .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION);
+        bounceAnim.setStartDelay(i * ItemInstallQueue.NEW_SHORTCUT_STAGGER_DELAY);
         bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION));
         return bounceAnim;
     }
@@ -2481,6 +2635,7 @@
     @Override
     public void bindAllApplications(AppInfo[] apps, int flags) {
         mAppsView.getAppsStore().setApps(apps, flags);
+        PopupContainerWithArrow.dismissInvalidPopup(this);
     }
 
     /**
@@ -2493,8 +2648,8 @@
     }
 
     @Override
-    public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
-        mAppsView.getAppsStore().updatePromiseAppProgress(app);
+    public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
+        mAppsView.getAppsStore().updateProgressBar(app);
     }
 
     @Override
@@ -2509,9 +2664,10 @@
      * @param updated list of shortcuts which have changed.
      */
     @Override
-    public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) {
+    public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
         if (!updated.isEmpty()) {
             mWorkspace.updateShortcuts(updated);
+            PopupContainerWithArrow.dismissInvalidPopup(this);
         }
     }
 
@@ -2536,10 +2692,11 @@
     public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
         mWorkspace.removeItemsByMatcher(matcher);
         mDragController.onAppsRemoved(matcher);
+        PopupContainerWithArrow.dismissInvalidPopup(this);
     }
 
     @Override
-    public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
+    public void bindAllWidgets(final List<WidgetsListBaseEntry> allWidgets) {
         mPopupDataProvider.setAllWidgets(allWidgets);
     }
 
@@ -2594,6 +2751,7 @@
         mDragLayer.dump(prefix, writer);
         mStateManager.dump(prefix, writer);
         mPopupDataProvider.dump(prefix, writer);
+        mDeviceProfile.dump(prefix, writer);
 
         try {
             FileLog.flushAll(writer);
@@ -2620,19 +2778,9 @@
             shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.widget_button_text),
                     KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON));
         }
-        final View currentFocus = getCurrentFocus();
-        if (currentFocus != null) {
-            if (new CustomActionsPopup(this, currentFocus).canShow()) {
-                shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.custom_actions),
-                        KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON));
-            }
-            if (currentFocus.getTag() instanceof ItemInfo
-                    && ShortcutUtil.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
+        getSupportedActions(this,  getCurrentFocus()).forEach(la ->
                 shortcutInfos.add(new KeyboardShortcutInfo(
-                        getString(R.string.shortcuts_menu_with_notifications_description),
-                        KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON));
-            }
-        }
+                        la.accessibilityAction.getLabel(), la.keyCode, KeyEvent.META_CTRL_ON)));
         if (!shortcutInfos.isEmpty()) {
             data.add(new KeyboardShortcutGroup(getString(R.string.home_screen), shortcutInfos));
         }
@@ -2650,29 +2798,18 @@
                         return true;
                     }
                     break;
-                case KeyEvent.KEYCODE_S: {
-                    View focusedView = getCurrentFocus();
-                    if (focusedView instanceof BubbleTextView
-                            && focusedView.getTag() instanceof ItemInfo
-                            && mAccessibilityDelegate.performAction(focusedView,
-                            (ItemInfo) focusedView.getTag(),
-                            LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
-                        PopupContainerWithArrow.getOpen(this).requestFocus();
-                        return true;
-                    }
-                    break;
-                }
-                case KeyEvent.KEYCODE_O:
-                    if (new CustomActionsPopup(this, getCurrentFocus()).show()) {
-                        return true;
-                    }
-                    break;
                 case KeyEvent.KEYCODE_W:
                     if (isInState(NORMAL)) {
                         OptionsPopupView.openWidgets(this);
                         return true;
                     }
                     break;
+                default:
+                    for (LauncherAction la : getSupportedActions(this, getCurrentFocus())) {
+                        if (la.keyCode == keyCode) {
+                            return la.invokeFromKeyboard(getCurrentFocus());
+                        }
+                    }
             }
         }
         return super.onKeyShortcut(keyCode, event);
@@ -2700,8 +2837,10 @@
         return super.onKeyUp(keyCode, event);
     }
 
-    protected StateHandler<LauncherState>[] createStateHandlers() {
-        return new StateHandler[] { getAllAppsController(), getWorkspace() };
+    @Override
+    protected void collectStateHandlers(List<StateHandler> out) {
+        out.add(getAllAppsController());
+        out.add(getWorkspace());
     }
 
     public TouchController[] createTouchControllers() {
@@ -2730,6 +2869,9 @@
         return Stream.of(APP_INFO, WIDGETS, INSTALL);
     }
 
+    protected LauncherAccessibilityDelegate createAccessibilityDelegate() {
+        return new LauncherAccessibilityDelegate(this);
+    }
 
     /**
      * @see LauncherState#getOverviewScaleAndOffset(Launcher)
@@ -2738,6 +2880,13 @@
         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);
     }
@@ -2749,16 +2898,56 @@
         return (T) activityContext;
     }
 
-
     /**
-     * Callback for listening for onResume
+     * Cross-fades the launcher's updated appearance with its previous appearance.
+     *
+     * This method is used to cross-fade UI updates on activity creation, specifically dark mode
+     * updates.
      */
-    public interface OnResumeCallback {
+    private void crossFadeWithPreviousAppearance() {
+        NonConfigInstance lastInstance = (NonConfigInstance) getLastNonConfigurationInstance();
 
-        void onLauncherResume();
+        if (lastInstance == null || lastInstance.snapshot == null) {
+            return;
+        }
+
+        ImageView crossFadeHelper = new ImageView(this);
+        crossFadeHelper.setImageBitmap(lastInstance.snapshot);
+        crossFadeHelper.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+
+        InsettableFrameLayout.LayoutParams layoutParams = new InsettableFrameLayout.LayoutParams(
+                InsettableFrameLayout.LayoutParams.MATCH_PARENT,
+                InsettableFrameLayout.LayoutParams.MATCH_PARENT);
+
+        layoutParams.ignoreInsets = true;
+
+        crossFadeHelper.setLayoutParams(layoutParams);
+
+        getRootView().addView(crossFadeHelper);
+
+        crossFadeHelper
+                .animate()
+                .setDuration(THEME_CROSS_FADE_ANIMATION_DURATION)
+                .alpha(0f)
+                .withEndAction(() -> getRootView().removeView(crossFadeHelper))
+                .start();
+    }
+
+    public boolean supportsAdaptiveIconAnimation(View clickedView) {
+        return false;
+    }
+
+    public DragOptions getDefaultWorkspaceDragOptions() {
+        return new DragOptions();
     }
 
     private static class NonConfigInstance {
         public Configuration config;
+        public Bitmap snapshot;
+    }
+
+    @Override
+    public StatsLogManager getStatsLogManager() {
+        return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId);
     }
 }
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index d9cf7f1..b56c012 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -16,6 +16,11 @@
 
 package com.android.launcher3;
 
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.FloatProperty;
 import android.util.IntProperty;
@@ -29,8 +34,8 @@
      */
     public static final int SPRING_LOADED_EXIT_DELAY = 500;
 
-    // The progress of an animation to all apps must be at least this far along to snap to all apps.
-    public static final float MIN_PROGRESS_TO_ALL_APPS = 0.5f;
+    // Progress after which the transition is assumed to be a success
+    public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
 
     public static final IntProperty<Drawable> DRAWABLE_ALPHA =
             new IntProperty<Drawable>("drawableAlpha") {
@@ -131,4 +136,39 @@
                             return view.getAlpha();
                         }
                     };
+
+    public static final IntProperty<View> VIEW_BACKGROUND_COLOR =
+            new IntProperty<View>("backgroundColor") {
+                @Override
+                public void setValue(View view, int color) {
+                    view.setBackgroundColor(color);
+                }
+
+                @Override
+                public Integer get(View view) {
+                    if (!(view.getBackground() instanceof ColorDrawable)) {
+                        return Color.TRANSPARENT;
+                    }
+                    return ((ColorDrawable) view.getBackground()).getColor();
+                }
+            };
+
+    /**
+     * Utility method to create an {@link AnimatorListener} which executes a callback on animation
+     * cancel.
+     */
+    public static AnimatorListener newCancelListener(Runnable callback) {
+        return new AnimatorListenerAdapter() {
+
+            boolean mDispatched = false;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                if (!mDispatched) {
+                    mDispatched = true;
+                    callback.run();
+                }
+            }
+        };
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 53e5274..3d6be69 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,38 +16,45 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_THEMED_ICONS;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.content.pm.LauncherApps;
-import android.os.Handler;
+import android.os.UserHandle;
 import android.util.Log;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.model.PredictionModel;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.InstallSessionTracker;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.util.SecureSettingsObserver;
+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 static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
+    private static final String KEY_ICON_STATE = "pref_icon_shape_path";
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
@@ -55,16 +62,11 @@
 
     private final Context mContext;
     private final LauncherModel mModel;
+    private final IconProvider mIconProvider;
     private final IconCache mIconCache;
-    private final WidgetPreviewLoader mWidgetCache;
+    private final DatabaseWidgetPreviewLoader mWidgetCache;
     private final InvariantDeviceProfile mInvariantDeviceProfile;
-    private final PredictionModel mPredictionModel;
-
-    private SecureSettingsObserver mNotificationDotsObserver;
-    private InstallSessionTracker mInstallSessionTracker;
-    private SimpleBroadcastReceiver mModelChangeReceiver;
-    private SafeCloseable mCalendarChangeTracker;
-    private SafeCloseable mUserChangeListener;
+    private final RunnableList mOnTerminateCallback = new RunnableList();
 
     public static LauncherAppState getInstance(final Context context) {
         return INSTANCE.get(context);
@@ -80,76 +82,80 @@
 
     public LauncherAppState(Context context) {
         this(context, LauncherFiles.APP_ICONS_DB);
+        Log.v(Launcher.TAG, "LauncherAppState initiated");
+        Preconditions.assertUIThread();
 
-        mModelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
+        mInvariantDeviceProfile.addOnChangeListener(idp -> refreshAndReloadLauncher());
 
         mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
-        mModelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
+
+        SimpleBroadcastReceiver modelChangeReceiver =
+                new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
+        modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
                 Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
         if (FeatureFlags.IS_STUDIO_BUILD) {
-            mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
+            modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
         }
+        mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
 
-        mCalendarChangeTracker = IconProvider.registerIconChangeListener(mContext,
-                mModel::onAppIconChanged, MODEL_EXECUTOR.getHandler());
-
-        // TODO: remove listener on terminate
-        FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
         CustomWidgetManager.INSTANCE.get(mContext)
                 .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
 
-        mUserChangeListener = UserCache.INSTANCE.get(mContext)
+        SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext)
                 .addUserChangeListener(mModel::forceReload);
+        mOnTerminateCallback.add(userChangeListener::close);
 
-        mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
-        new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
-
-        mInstallSessionTracker = InstallSessionHelper.INSTANCE.get(context)
-                .registerInstallTracker(mModel, MODEL_EXECUTOR);
-
-        if (!mContext.getResources().getBoolean(R.bool.notification_dots_enabled)) {
-            mNotificationDotsObserver = null;
-        } else {
-            // Register an observer to rebind the notification listener when dots are re-enabled.
-            mNotificationDotsObserver =
-                    newNotificationSettingsObserver(mContext, this::onNotificationSettingsChanged);
-            mNotificationDotsObserver.register();
-            mNotificationDotsObserver.dispatchOnChange();
+        IconObserver observer = new IconObserver();
+        SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
+                observer, MODEL_EXECUTOR.getHandler());
+        mOnTerminateCallback.add(iconChangeTracker::close);
+        MODEL_EXECUTOR.execute(observer::verifyIconChanged);
+        if (ENABLE_THEMED_ICONS.get()) {
+            SharedPreferences prefs = Utilities.getPrefs(mContext);
+            prefs.registerOnSharedPreferenceChangeListener(observer);
+            mOnTerminateCallback.add(
+                    () -> prefs.unregisterOnSharedPreferenceChangeListener(observer));
         }
+
+        InstallSessionTracker installSessionTracker =
+                InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
+        mOnTerminateCallback.add(installSessionTracker::unregister);
+
+        // Register an observer to rebind the notification listener when dots are re-enabled.
+        SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
+        SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged;
+        settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister);
+        onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI));
+        mOnTerminateCallback.add(() ->
+                settingsCache.unregister(NOTIFICATION_BADGING_URI, notificationLister));
     }
 
     public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
-        Log.v(Launcher.TAG, "LauncherAppState initiated");
-        Preconditions.assertUIThread();
         mContext = context;
 
         mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
-        mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
-        mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
-        mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
-        mPredictionModel = PredictionModel.newInstance(mContext);
+        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));
+        mOnTerminateCallback.add(mIconCache::close);
     }
 
-    protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
+    private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
         if (areNotificationDotsEnabled) {
             NotificationListener.requestRebind(new ComponentName(
                     mContext, NotificationListener.class));
         }
     }
 
-    private void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
-        if (changeFlags == 0) {
-            return;
-        }
-
-        if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) != 0) {
-            LauncherIcons.clearPool();
-            mIconCache.updateIconParams(idp.fillResIconDpi, idp.iconBitmapSize);
-            mWidgetCache.refresh();
-        }
-
+    private void refreshAndReloadLauncher() {
+        LauncherIcons.clearPool();
+        mIconCache.updateIconParams(
+                mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize);
+        mWidgetCache.refresh();
         mModel.forceReload();
     }
 
@@ -157,24 +163,14 @@
      * Call from Application.onTerminate(), which is not guaranteed to ever be called.
      */
     public void onTerminate() {
-        if (mModelChangeReceiver != null) {
-            mContext.unregisterReceiver(mModelChangeReceiver);
-        }
+        mModel.destroy();
         mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
-        if (mInstallSessionTracker != null) {
-            mInstallSessionTracker.unregister();
-        }
-        if (mCalendarChangeTracker != null) {
-            mCalendarChangeTracker.close();
-        }
-        if (mUserChangeListener != null) {
-            mUserChangeListener.close();
-        }
         CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
+        mOnTerminateCallback.executeAllAndDestroy();
+    }
 
-        if (mNotificationDotsObserver != null) {
-            mNotificationDotsObserver.unregister();
-        }
+    public IconProvider getIconProvider() {
+        return mIconProvider;
     }
 
     public IconCache getIconCache() {
@@ -185,11 +181,7 @@
         return mModel;
     }
 
-    public PredictionModel getPredictionModel() {
-        return mPredictionModel;
-    }
-
-    public WidgetPreviewLoader getWidgetCache() {
+    public DatabaseWidgetPreviewLoader getWidgetCache() {
         return mWidgetCache;
     }
 
@@ -203,4 +195,35 @@
     public static InvariantDeviceProfile getIDP(Context context) {
         return InvariantDeviceProfile.INSTANCE.get(context);
     }
+
+    private class IconObserver
+            implements IconProvider.IconChangeListener, OnSharedPreferenceChangeListener {
+
+        @Override
+        public void onAppIconChanged(String packageName, UserHandle user) {
+            mModel.onAppIconChanged(packageName, user);
+        }
+
+        @Override
+        public void onSystemIconStateChanged(String iconState) {
+            IconShape.init(mContext);
+            refreshAndReloadLauncher();
+            getDevicePrefs(mContext).edit().putString(KEY_ICON_STATE, iconState).apply();
+        }
+
+        void verifyIconChanged() {
+            String iconState = mIconProvider.getSystemIconState();
+            if (!iconState.equals(getDevicePrefs(mContext).getString(KEY_ICON_STATE, ""))) {
+                onSystemIconStateChanged(iconState);
+            }
+        }
+
+        @Override
+        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+            if (Themes.KEY_THEMED_ICONS.equals(key)) {
+                mIconProvider.setIconThemeSupported(Themes.isThemedIconEnabled(mContext));
+                verifyIconChanged();
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
deleted file mode 100644
index 24e0d14..0000000
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ /dev/null
@@ -1,70 +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;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/**
- * Manages the opening and closing app transitions from Launcher.
- */
-public class LauncherAppTransitionManager implements ResourceBasedOverride {
-
-    public static LauncherAppTransitionManager newInstance(Context context) {
-        return Overrides.getObject(LauncherAppTransitionManager.class,
-                context, R.string.app_transition_manager_class);
-    }
-
-    public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
-        int left = 0, top = 0;
-        int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
-        if (v instanceof BubbleTextView) {
-            // Launch from center of icon, not entire view
-            Drawable icon = ((BubbleTextView) v).getIcon();
-            if (icon != null) {
-                Rect bounds = icon.getBounds();
-                left = (width - bounds.width()) / 2;
-                top = v.getPaddingTop();
-                width = bounds.width();
-                height = bounds.height();
-            }
-        }
-        return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
-    }
-
-    public boolean supportsAdaptiveIconAnimation() {
-        return false;
-    }
-
-    /**
-     * Registers remote animations for certain system transitions.
-     */
-    public void registerRemoteAnimations() {
-        // Do nothing
-    }
-
-    /**
-     * Unregisters all remote animations.
-     */
-    public void unregisterRemoteAnimations() {
-        // Do nothing
-    }
-}
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
deleted file mode 100644
index 618b5de..0000000
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package com.android.launcher3;
-
-import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.UserHandle;
-
-import com.android.launcher3.icons.ComponentWithLabelAndIcon;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-
-/**
- * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
- * a common object for describing both framework provided AppWidgets as well as custom widgets
- * (who's implementation is owned by the launcher). This object represents a widget type / class,
- * as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
- */
-public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
-        implements ComponentWithLabelAndIcon {
-
-    public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
-
-    public int spanX;
-    public int spanY;
-    public int minSpanX;
-    public int minSpanY;
-
-    public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
-            AppWidgetProviderInfo info) {
-        final LauncherAppWidgetProviderInfo launcherInfo;
-        if (info instanceof LauncherAppWidgetProviderInfo) {
-            launcherInfo = (LauncherAppWidgetProviderInfo) info;
-        } else {
-
-            // In lieu of a public super copy constructor, we first write the AppWidgetProviderInfo
-            // into a parcel, and then construct a new LauncherAppWidgetProvider info from the
-            // associated super parcel constructor. This allows us to copy non-public members without
-            // using reflection.
-            Parcel p = Parcel.obtain();
-            info.writeToParcel(p, 0);
-            p.setDataPosition(0);
-            launcherInfo = new LauncherAppWidgetProviderInfo(p);
-            p.recycle();
-        }
-        launcherInfo.initSpans(context);
-        return launcherInfo;
-    }
-
-    protected LauncherAppWidgetProviderInfo() {}
-
-    protected LauncherAppWidgetProviderInfo(Parcel in) {
-        super(in);
-    }
-
-    public void initSpans(Context context) {
-        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-
-        Point landCellSize = idp.landscapeProfile.getCellSize();
-        Point portCellSize = idp.portraitProfile.getCellSize();
-
-        // Always assume we're working with the smallest span to make sure we
-        // reserve enough space in both orientations.
-        float smallestCellWidth = Math.min(landCellSize.x, portCellSize.x);
-        float smallestCellHeight = Math.min(landCellSize.y, portCellSize.y);
-
-        // We want to account for the extra amount of padding that we are adding to the widget
-        // to ensure that it gets the full amount of space that it has requested.
-        Rect widgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(
-                context, provider, null);
-        spanX = Math.max(1, (int) Math.ceil(
-                        (minWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
-        spanY = Math.max(1, (int) Math.ceil(
-                (minHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
-
-        minSpanX = Math.max(1, (int) Math.ceil(
-                (minResizeWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
-        minSpanY = Math.max(1, (int) Math.ceil(
-                (minResizeHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
-    }
-
-    public String getLabel(PackageManager packageManager) {
-        return super.loadLabel(packageManager);
-    }
-
-    public Point getMinSpans() {
-        return new Point((resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1,
-                (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1);
-    }
-
-    public boolean isCustomWidget() {
-        return provider.getClassName().startsWith(CLS_CUSTOM_WIDGET_PREFIX);
-    }
-
-    public int getWidgetFeatures() {
-        if (Utilities.ATLEAST_P) {
-            return widgetFeatures;
-        } else {
-            return 0;
-        }
-    }
-
-    @Override
-    public final ComponentName getComponent() {
-        return provider;
-    }
-
-    @Override
-    public final UserHandle getUser() {
-        return getProfile();
-    }
-
-    @Override
-    public Drawable getFullResIcon(IconCache cache) {
-        return cache.getFullResIcon(provider.getPackageName(), icon);
-    }
-}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 25afb55..6c0daa4 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -15,6 +15,7 @@
     private static final String XML = ".xml";
 
     public static final String LAUNCHER_DB = "launcher.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";
     public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
@@ -30,6 +31,7 @@
 
     public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
             LAUNCHER_DB,
+            LAUNCHER_4_BY_5_DB,
             LAUNCHER_4_BY_4_DB,
             LAUNCHER_3_BY_3_DB,
             LAUNCHER_2_BY_2_DB,
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f434c91..eef3980 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -20,7 +20,6 @@
 import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
 
 import android.content.Context;
 import android.content.Intent;
@@ -44,9 +43,12 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.CacheDataUpdatedTask;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.LoaderResults;
 import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDelegate;
 import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.PackageIncrementalDownloadUpdatedTask;
 import com.android.launcher3.model.PackageInstallStateChangedTask;
 import com.android.launcher3.model.PackageUpdatedTask;
 import com.android.launcher3.model.ShortcutsChangedTask;
@@ -58,9 +60,8 @@
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
 
@@ -85,7 +86,6 @@
 
     private final LauncherAppState mApp;
     private final Object mLock = new Object();
-    private final LooperExecutor mMainExecutor = MAIN_EXECUTOR;
 
     private LoaderTask mLoaderTask;
     private boolean mIsLoaderTaskRunning;
@@ -112,20 +112,26 @@
      */
     private final BgDataModel mBgDataModel = new BgDataModel();
 
+    private final ModelDelegate mModelDelegate;
+
     // Runnable to check if the shortcuts permission has changed.
-    private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
+    private final Runnable mDataValidationCheck = new Runnable() {
         @Override
         public void run() {
-            if (mModelLoaded && hasShortcutsPermission(mApp.getContext())
-                    != mBgAllAppsList.hasShortcutHostPermission()) {
-                forceReload();
+            if (mModelLoaded) {
+                mModelDelegate.validateData();
             }
         }
     };
 
-    LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
+    LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
+        mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel);
+    }
+
+    public ModelDelegate getModelDelegate() {
+        return mModelDelegate;
     }
 
     /**
@@ -195,6 +201,15 @@
     }
 
     @Override
+    public void onPackageLoadingProgressChanged(
+                String packageName, UserHandle user, float progress) {
+        if (Utilities.ATLEAST_S) {
+            enqueueModelUpdateTask(new PackageIncrementalDownloadUpdatedTask(
+                    packageName, user, progress));
+        }
+    }
+
+    @Override
     public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user) {
         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
@@ -217,6 +232,20 @@
         }
     }
 
+    /**
+     * Called when the workspace items have drastically changed
+     */
+    public void onWorkspaceUiChanged() {
+        MODEL_EXECUTOR.execute(mModelDelegate::workspaceLoadComplete);
+    }
+
+    /**
+     * Called when the model is destroyed
+     */
+    public void destroy() {
+        MODEL_EXECUTOR.execute(mModelDelegate::destroy);
+    }
+
     public void onBroadcastIntent(Intent intent) {
         if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
         final String action = intent.getAction();
@@ -321,20 +350,21 @@
      */
     public boolean startLoader() {
         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
-        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
+        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 (callbacksList.length > 0) {
                 // Clear any pending bind-runnables from the synchronized load process.
                 for (Callbacks cb : callbacksList) {
-                    mMainExecutor.execute(cb::clearPendingBinds);
+                    MAIN_EXECUTOR.execute(cb::clearPendingBinds);
                 }
 
                 // If there is already one running, tell it to stop.
                 stopLoader();
                 LoaderResults loaderResults = new LoaderResults(
-                        mApp, mBgDataModel, mBgAllAppsList, callbacksList, mMainExecutor);
+                        mApp, mBgDataModel, mBgAllAppsList, callbacksList);
                 if (mModelLoaded && !mIsLoaderTaskRunning) {
                     // Divide the set of loaded items into those that we are binding synchronously,
                     // and everything else that is to be bound normally (asynchronously).
@@ -372,7 +402,8 @@
     public void startLoaderForResults(LoaderResults results) {
         synchronized (mLock) {
             stopLoader();
-            mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, mBgDataModel, results);
+            mLoaderTask = new LoaderTask(
+                    mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, results);
 
             // Always post the loader task, instead of running directly (even on same thread) so
             // that we exit any nested synchronized blocks
@@ -410,7 +441,7 @@
         enqueueModelUpdateTask(new BaseModelUpdateTask() {
             @Override
             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-                final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
+                final IntSet removedIds = new IntSet();
                 synchronized (dataModel) {
                     for (ItemInfo info : dataModel.itemsIdMap) {
                         if (info instanceof WorkspaceItemInfo
@@ -418,13 +449,13 @@
                                 && user.equals(info.user)
                                 && info.getIntent() != null
                                 && TextUtils.equals(packageName, info.getIntent().getPackage())) {
-                            removedIds.put(info.id, true /* remove */);
+                            removedIds.add(info.id);
                         }
                     }
                 }
 
                 if (!removedIds.isEmpty()) {
-                    deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
+                    deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds));
                 }
             }
         });
@@ -491,9 +522,9 @@
      * Current implementation simply reloads the workspace, but it can be optimized to
      * use partial updates similar to {@link UserCache}
      */
-    public void refreshShortcutsIfRequired() {
-        MODEL_EXECUTOR.getHandler().removeCallbacks(mShortcutPermissionCheckRunnable);
-        MODEL_EXECUTOR.post(mShortcutPermissionCheckRunnable);
+    public void validateModelDataOnResume() {
+        MODEL_EXECUTOR.getHandler().removeCallbacks(mDataValidationCheck);
+        MODEL_EXECUTOR.post(mDataValidationCheck);
     }
 
     /**
@@ -520,7 +551,7 @@
     }
 
     public void enqueueModelUpdateTask(ModelUpdateTask task) {
-        task.init(mApp, this, mBgDataModel, mBgAllAppsList, mMainExecutor);
+        task.init(mApp, this, mBgDataModel, mBgAllAppsList, MAIN_EXECUTOR);
         MODEL_EXECUTOR.execute(task);
     }
 
@@ -588,7 +619,9 @@
                         + "\" bitmapIcon=" + info.bitmap.icon
                         + " componentName=" + info.componentName.getPackageName());
             }
+            writer.println();
         }
+        mModelDelegate.dump(prefix, fd, writer, args);
         mBgDataModel.dump(prefix, fd, writer, args);
     }
 
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 19a314f..440e9e3 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -61,6 +61,7 @@
 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;
@@ -71,6 +72,7 @@
 import com.android.launcher3.util.NoLocaleSQLiteHelper;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
 
 import org.xmlpull.v1.XmlPullParser;
 
@@ -97,7 +99,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 = 28;
+    public static final int SCHEMA_VERSION = 29;
 
     public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
     public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
@@ -188,6 +190,9 @@
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
+        final Bundle extra = new Bundle();
+        extra.putString(LauncherSettings.Settings.EXTRA_DB_NAME, mOpenHelper.getDatabaseName());
+        result.setExtras(extra);
         result.setNotificationUri(getContext().getContentResolver(), uri);
 
         return result;
@@ -614,7 +619,7 @@
                 .appendQueryParameter("version", "1")
                 .appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
                 .appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
-                .appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
+                .appendQueryParameter("hotseatSize", Integer.toString(grid.numDatabaseHotseatIcons))
                 .build();
     }
 
@@ -875,9 +880,18 @@
                     }
                     dropTable(db, "workspaceScreens");
                 }
-                case 28:
+                case 28: {
+                    boolean columnAdded = addIntegerColumn(
+                            db, Favorites.APPWIDGET_SOURCE, Favorites.CONTAINER_UNKNOWN);
+                    if (!columnAdded) {
+                        // Old version remains, which means we wipe old data
+                        break;
+                    }
+                }
+                case 29: {
                     // DB Upgraded successfully
                     return;
+                }
             }
 
             // DB was not upgraded
@@ -913,7 +927,6 @@
          * Removes widgets which are registered to the Launcher's host, but are not present
          * in our model.
          */
-        @TargetApi(Build.VERSION_CODES.O)
         public void removeGhostWidgets(SQLiteDatabase db) {
             // Get all existing widget ids.
             final AppWidgetHost host = newLauncherWidgetHost();
@@ -1097,11 +1110,20 @@
      * @return the max _id in the provided table.
      */
     @Thunk static int getMaxId(SQLiteDatabase db, String query, Object... args) {
-        int max = (int) DatabaseUtils.longForQuery(db,
-                String.format(Locale.ENGLISH, query, args),
-                null);
-        if (max < 0) {
-            throw new RuntimeException("Error: could not query max id");
+        int max = 0;
+        try (SQLiteStatement prog = db.compileStatement(
+                String.format(Locale.ENGLISH, query, args))) {
+            max = (int) DatabaseUtils.longForQuery(prog, null);
+            if (max < 0) {
+                throw new RuntimeException("Error: could not query max id");
+            }
+        } catch (IllegalArgumentException exception) {
+            String message = exception.getMessage();
+            if (message.contains("re-open") && message.contains("already-closed")) {
+                // Don't crash trying to end a transaction an an already closed DB. See b/173162852.
+            } else {
+                throw exception;
+            }
         }
         return max;
     }
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 51504ce..f26cfe8 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -4,12 +4,14 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.ViewDebug;
 import android.view.WindowInsets;
 
+import com.android.launcher3.graphics.SysUiScrim;
 import com.android.launcher3.statemanager.StatefulActivity;
 
 import java.util.Collections;
@@ -31,14 +33,24 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mForceHideBackArrow;
 
+    private final SysUiScrim mSysUiScrim;
+
     public LauncherRootView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mActivity = StatefulActivity.fromContext(context);
+        mSysUiScrim = new SysUiScrim(this);
     }
 
     private void handleSystemWindowInsets(Rect insets) {
-        // Update device profile before notifying th children.
-        mActivity.getDeviceProfile().updateInsets(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);
         boolean resetState = !insets.equals(mInsets);
         setInsets(insets);
 
@@ -61,6 +73,7 @@
         // modifying child layout params.
         if (!insets.equals(mInsets)) {
             super.setInsets(insets);
+            mSysUiScrim.onInsetsChanged(insets);
         }
     }
 
@@ -90,10 +103,17 @@
     }
 
     @Override
+    protected void dispatchDraw(Canvas canvas) {
+        mSysUiScrim.draw(canvas);
+        super.dispatchDraw(canvas);
+    }
+
+    @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
         SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(l, t, r, b);
         setDisallowBackGesture(mDisallowBackGesture);
+        mSysUiScrim.setSize(r - l, b - t);
     }
 
     @TargetApi(Build.VERSION_CODES.Q)
@@ -113,10 +133,14 @@
                 : Collections.emptyList());
     }
 
+    public SysUiScrim getSysUiScrim() {
+        return mSysUiScrim;
+    }
+
     public interface WindowStateListener {
 
         void onWindowFocusChanged(boolean hasFocus);
 
         void onWindowVisibilityChanged(int visibility);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index a1ac229..d663480 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -75,6 +75,37 @@
         public static final int ITEM_TYPE_SHORTCUT = 1;
 
         /**
+         * The favorite is a user created folder
+         */
+        public static final int ITEM_TYPE_FOLDER = 2;
+
+        /**
+         * The favorite is a widget
+         */
+        public static final int ITEM_TYPE_APPWIDGET = 4;
+
+        /**
+         * The favorite is a custom widget provided by the launcher
+         */
+        public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
+
+        /**
+         * The gesture is an application created deep shortcut
+         */
+        public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
+
+        /**
+         * Type of the item is recents task.
+         * TODO(hyunyoungs): move constants not related to Favorites DB to a better location.
+         */
+        public static final int ITEM_TYPE_TASK = 7;
+
+        /**
+         * The item is QSB
+         */
+        public static final int ITEM_TYPE_QSB = 8;
+
+        /**
          * The icon package name in Intent.ShortcutIconResource
          * <P>Type: TEXT</P>
          */
@@ -121,6 +152,12 @@
                 + LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
 
         /**
+         * The content:// style URL for "favorites_bakup" table
+         */
+        public static final Uri BACKUP_CONTENT_URI = Uri.parse("content://"
+                + LauncherProvider.AUTHORITY + "/" + BACKUP_TABLE_NAME);
+
+        /**
          * The content:// style URL for "favorites_preview" table
          */
         public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
@@ -156,15 +193,23 @@
         public static final int CONTAINER_DESKTOP = -100;
         public static final int CONTAINER_HOTSEAT = -101;
         public static final int CONTAINER_PREDICTION = -102;
+        public static final int CONTAINER_WIDGETS_PREDICTION = -111;
         public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
         public static final int CONTAINER_ALL_APPS = -104;
         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;
         // Represents search results view.
         public static final int CONTAINER_SEARCH_RESULTS = -106;
         public static final int CONTAINER_SHORTCUTS = -107;
         public static final int CONTAINER_SETTINGS = -108;
         public static final int CONTAINER_TASKSWITCHER = -109;
-        public static final int CONTAINER_TASKFOREGROUND = -110;
+        public static final int CONTAINER_QSB = -110;
+
+        // Represents any of the extended containers implemented in non-AOSP variants.
+        public static final int EXTENDED_CONTAINERS = -200;
+
+        public static final int CONTAINER_UNKNOWN = -1;
 
         public static final String containerToString(int container) {
             switch (container) {
@@ -187,6 +232,8 @@
                 case ITEM_TYPE_APPWIDGET: return "WIDGET";
                 case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET";
                 case ITEM_TYPE_DEEP_SHORTCUT: return "DEEPSHORTCUT";
+                case ITEM_TYPE_TASK: return "TASK";
+                case ITEM_TYPE_QSB: return "QSB";
                 default: return String.valueOf(type);
             }
         }
@@ -232,32 +279,6 @@
         public static final String PROFILE_ID = "profileId";
 
         /**
-         * The favorite is a user created folder
-         */
-        public static final int ITEM_TYPE_FOLDER = 2;
-
-        /**
-         * The favorite is a widget
-         */
-        public static final int ITEM_TYPE_APPWIDGET = 4;
-
-        /**
-         * The favorite is a custom widget provided by the launcher
-         */
-        public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
-
-        /**
-         * The gesture is an application created deep shortcut
-         */
-        public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
-
-        /**
-         * Type of the item is recents task.
-         * TODO(hyunyoungs): move constants not related to Favorites DB to a better location.
-         */
-        public static final int ITEM_TYPE_TASK = 7;
-
-        /**
          * The appWidgetId of the widget
          *
          * <P>Type: INTEGER</P>
@@ -289,6 +310,12 @@
          */
         public static final String OPTIONS = "options";
 
+        /**
+         * Stores the source container that the widget was added from.
+         * <p>Type: INTEGER</p>
+         */
+        public static final String APPWIDGET_SOURCE = "appWidgetSource";
+
         public static void addTableToDb(SQLiteDatabase db, long myProfileId, boolean optional) {
             addTableToDb(db, myProfileId, optional, TABLE_NAME);
         }
@@ -316,7 +343,8 @@
                     "restored INTEGER NOT NULL DEFAULT 0," +
                     "profileId INTEGER DEFAULT " + myProfileId + "," +
                     "rank INTEGER NOT NULL DEFAULT 0," +
-                    "options INTEGER NOT NULL DEFAULT 0" +
+                    "options INTEGER NOT NULL DEFAULT 0," +
+                    APPWIDGET_SOURCE + " INTEGER NOT NULL DEFAULT " + CONTAINER_UNKNOWN +
                     ");");
         }
     }
@@ -359,6 +387,8 @@
 
         public static final String EXTRA_VALUE = "value";
 
+        public static final String EXTRA_DB_NAME = "db_name";
+
         public static Bundle call(ContentResolver cr, String method) {
             return call(cr, method, null /* arg */);
         }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 39b0f2f..3399ce9 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -16,17 +16,21 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_PEEK_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.statemanager.BaseState;
@@ -36,7 +40,6 @@
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.uioverrides.states.AllAppsState;
 import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 import java.util.Arrays;
 
@@ -51,16 +54,13 @@
      */
     public static final int NONE = 0;
     public static final int HOTSEAT_ICONS = 1 << 0;
-    public static final int HOTSEAT_SEARCH_BOX = 1 << 1;
-    public static final int ALL_APPS_HEADER = 1 << 2;
-    public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
-    public static final int ALL_APPS_CONTENT = 1 << 4;
-    public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
-    public static final int OVERVIEW_BUTTONS = 1 << 6;
-
-    /** Mask of all the items that are contained in the apps view. */
-    public static final int APPS_VIEW_ITEM_MASK =
-            HOTSEAT_SEARCH_BOX | ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
+    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;
 
     // Flag indicating workspace has multiple pages visible.
     public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
@@ -92,13 +92,13 @@
                 }
             };
 
-    private static final LauncherState[] sAllStates = new LauncherState[9];
+    private static final LauncherState[] sAllStates = new LauncherState[10];
 
     /**
      * TODO: Create a separate class for NORMAL state.
      */
     public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
-            ContainerType.WORKSPACE,
+            LAUNCHER_STATE_HOME,
             FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
                     FLAG_HAS_SYS_UI_SCRIM) {
         @Override
@@ -115,23 +115,25 @@
             SPRING_LOADED_STATE_ORDINAL);
     public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL);
     public static final LauncherState HINT_STATE = new HintState(HINT_STATE_ORDINAL);
+    public static final LauncherState HINT_STATE_TWO_BUTTON = new HintState(
+            HINT_STATE_TWO_BUTTON_ORDINAL, LAUNCHER_STATE_OVERVIEW);
 
     public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL);
-    public static final LauncherState OVERVIEW_PEEK =
-            OverviewState.newPeekState(OVERVIEW_PEEK_STATE_ORDINAL);
     public static final LauncherState OVERVIEW_MODAL_TASK = OverviewState.newModalTaskState(
             OVERVIEW_MODAL_TASK_STATE_ORDINAL);
     public static final LauncherState QUICK_SWITCH =
             OverviewState.newSwitchState(QUICK_SWITCH_STATE_ORDINAL);
     public static final LauncherState BACKGROUND_APP =
             OverviewState.newBackgroundState(BACKGROUND_APP_STATE_ORDINAL);
+    public static final LauncherState OVERVIEW_SPLIT_SELECT =
+            OverviewState.newSplitSelectState(OVERVIEW_SPLIT_SELECT_ORDINAL);
 
     public final int ordinal;
 
     /**
-     * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
+     * Used for {@link com.android.launcher3.logging.StatsLogManager}
      */
-    public final int containerType;
+    public final int statsLogOrdinal;
 
     /**
      * True if the state has overview panel visible.
@@ -140,8 +142,8 @@
 
     private final int mFlags;
 
-    public LauncherState(int id, int containerType, int flags) {
-        this.containerType = containerType;
+    public LauncherState(int id, int statsLogOrdinal, int flags) {
+        this.statsLogOrdinal = statsLogOrdinal;
         this.mFlags = flags;
         this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0;
         this.ordinal = id;
@@ -171,16 +173,20 @@
 
     /**
      * Returns an array of two elements.
-     *   The first specifies the scale for the overview
-     *   The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
-     *   should be shifted horizontally.
+     * The first specifies the scale for the overview
+     * The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
+     * should be shifted horizontally.
      */
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
         return launcher.getNormalOverviewScaleAndOffset();
     }
 
-    public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
-        return new ScaleAndTranslation(NO_SCALE, NO_OFFSET, NO_OFFSET);
+    public float getTaskbarScale(Launcher launcher) {
+        return launcher.getNormalTaskbarScale();
+    }
+
+    public float getTaskbarTranslationY(Launcher launcher) {
+        return -launcher.getHotseat().getTaskbarOffsetY();
     }
 
     public float getOverviewFullscreenProgress() {
@@ -188,10 +194,15 @@
     }
 
     public int getVisibleElements(Launcher launcher) {
-        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR;
-        }
-        return HOTSEAT_ICONS | HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR;
+        return HOTSEAT_ICONS | WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR;
+    }
+
+    /**
+     * A shorthand for checking getVisibleElements() & elements == elements.
+     * @return Whether all of the given elements are visible.
+     */
+    public boolean areElementsVisible(Launcher launcher, int elements) {
+        return (getVisibleElements(launcher) & elements) == elements;
     }
 
     /**
@@ -203,12 +214,16 @@
         return 1f;
     }
 
-    public float getWorkspaceScrimAlpha(Launcher launcher) {
+    public float getWorkspaceBackgroundAlpha(Launcher launcher) {
         return 0;
     }
 
-    public float getOverviewScrimAlpha(Launcher launcher) {
-        return 0;
+    /**
+     * What color should the workspace scrim be in when at rest in this state.
+     * Return {@link Color#TRANSPARENT} for no scrim.
+     */
+    public int getWorkspaceScrimColor(Launcher launcher) {
+        return Color.TRANSPARENT;
     }
 
     /**
@@ -220,6 +235,14 @@
     }
 
     /**
+     * For this state, 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(Launcher launcher) {
+        return 0;
+    }
+
+    /**
      * The amount of blur and wallpaper zoom to apply to the background of either the app
      * or Launcher surface in this state. Should be a number between 0 and 1, inclusive.
      *
@@ -232,6 +255,7 @@
 
     /**
      * Returns the amount of blur and wallpaper zoom for this state with {@param isMultiWindowMode}.
+     *
      * @see #getDepth(Context).
      */
     public final float getDepth(Context context, boolean isMultiWindowMode) {
@@ -258,7 +282,7 @@
         return new PageAlphaProvider(ACCEL_2) {
             @Override
             public float getPageAlpha(int pageIndex) {
-                return  pageIndex != centerPage ? 0 : 1f;
+                return pageIndex != centerPage ? 0 : 1f;
             }
         };
     }
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
index 5f6ecb5..f2a3de7 100644
--- a/src/com/android/launcher3/MainProcessInitializer.java
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -38,7 +38,6 @@
     protected void init(Context context) {
         FileLog.setDir(context.getApplicationContext().getFilesDir());
         FeatureFlags.initialize(context);
-        SessionCommitReceiver.applyDefaultUserPrefs(context);
         IconShape.init(context);
 
         if (BitmapCreationCheck.ENABLED) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index e13d89f..b423871 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -16,16 +16,14 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.anim.Interpolators.SCROLL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
-import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
 import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
 import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
 
 import android.animation.LayoutTransition;
-import android.animation.TimeInterpolator;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -46,23 +44,22 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.Interpolator;
+import android.widget.OverScroller;
 import android.widget.ScrollView;
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds;
-import com.android.launcher3.util.OverScroller;
+import com.android.launcher3.util.EdgeEffectCompat;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 /**
  * An abstraction of the original Workspace which supports browsing through a
@@ -79,9 +76,6 @@
 
     public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
 
-    // OverScroll constants
-    private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
-
     private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
     // The page is moved more than halfway, automatically move to the next page on touch up.
     private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
@@ -112,7 +106,6 @@
     protected int mMaxScroll;
     protected int mMinScroll;
     protected OverScroller mScroller;
-    private Interpolator mDefaultInterpolator;
     private VelocityTracker mVelocityTracker;
     protected int mPageSpacing = 0;
 
@@ -143,12 +136,6 @@
     protected boolean mIsPageInTransition = false;
     private Runnable mOnPageTransitionEndCallback;
 
-    protected float mSpringOverScroll;
-
-    protected boolean mWasInOverscroll = false;
-
-    protected int mUnboundedScroll;
-
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
     protected T mPageIndicator;
@@ -161,6 +148,9 @@
 
     private int[] mTmpIntPair = new int[2];
 
+    protected EdgeEffectCompat mEdgeGlowLeft;
+    protected EdgeEffectCompat mEdgeGlowRight;
+
     public PagedView(Context context) {
         this(context, null);
     }
@@ -180,8 +170,7 @@
         setHapticFeedbackEnabled(false);
         mIsRtl = Utilities.isRtl(getResources());
 
-        mScroller = new OverScroller(context);
-        setDefaultInterpolator(Interpolators.SCROLL);
+        mScroller = new OverScroller(context, SCROLL);
         mCurrentPage = 0;
 
         final ViewConfiguration configuration = ViewConfiguration.get(context);
@@ -195,14 +184,14 @@
         mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
         mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
 
-        if (Utilities.ATLEAST_OREO) {
-            setDefaultFocusHighlightEnabled(false);
-        }
+        initEdgeEffect();
+        setDefaultFocusHighlightEnabled(false);
+        setWillNotDraw(false);
     }
 
-    protected void setDefaultInterpolator(Interpolator interpolator) {
-        mDefaultInterpolator = interpolator;
-        mScroller.setInterpolator(mDefaultInterpolator);
+    protected void initEdgeEffect() {
+        mEdgeGlowLeft = new EdgeEffectCompat(getContext());
+        mEdgeGlowRight = new EdgeEffectCompat(getContext());
     }
 
     public void initParentViews(View parent) {
@@ -219,7 +208,7 @@
     /**
      * Returns the index of the currently displayed page. When in free scroll mode, this is the page
      * that the user was on before entering free scroll mode (e.g. the home screen page they
-     * long-pressed on to enter the overview). Try using {@link #getPageNearestToCenterOfScreen()}
+     * long-pressed on to enter the overview). Try using {@link #getDestinationPage()}
      * to get the page the user is currently scrolling over.
      */
     public int getCurrentPage() {
@@ -257,10 +246,19 @@
             newPosition = getScrollForPage(mCurrentPage);
         }
         mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition);
-        mOrientationHandler.scrollerStartScroll(mScroller, newPosition);
+        mScroller.startScroll(mScroller.getCurrX(), 0, newPosition - mScroller.getCurrX(), 0);
         forceFinishScroller(true);
     }
 
+    /**
+     *  Immediately finishes any overscroll effect and jumps to the end of the scroller animation.
+     */
+    public void abortScrollerAnimation() {
+        mEdgeGlowLeft.finish();
+        mEdgeGlowRight.finish();
+        abortScrollerAnimation(true);
+    }
+
     private void abortScrollerAnimation(boolean resetNextPage) {
         mScroller.abortAnimation();
         // We need to clean up the next page here to avoid computeScrollHelper from
@@ -284,7 +282,45 @@
     private int validateNewPage(int newPage) {
         newPage = ensureWithinScrollBounds(newPage);
         // Ensure that it is clamped by the actual set of children in all cases
-        return Utilities.boundToRange(newPage, 0, getPageCount() - 1);
+        newPage = Utilities.boundToRange(newPage, 0, getPageCount() - 1);
+
+        if (getPanelCount() > 1) {
+            // Always return left panel as new page
+            newPage = getLeftmostVisiblePageForIndex(newPage);
+        }
+        return newPage;
+    }
+
+    private int getLeftmostVisiblePageForIndex(int pageIndex) {
+        int panelCount = getPanelCount();
+        return (pageIndex / panelCount) * panelCount;
+    }
+
+    /**
+     * Returns the number of pages that are shown at the same time.
+     */
+    protected int getPanelCount() {
+        return 1;
+    }
+
+    /**
+     * 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);
+            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;
     }
 
     /**
@@ -359,12 +395,19 @@
     }
 
     protected void pageEndTransition() {
-        if (mIsPageInTransition) {
+        if (mIsPageInTransition && !mIsBeingDragged && mScroller.isFinished()
+                && (!isShown() || (mEdgeGlowLeft.isFinished() && mEdgeGlowRight.isFinished()))) {
             mIsPageInTransition = false;
             onPageEndTransition();
         }
     }
 
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        pageEndTransition();
+        super.onVisibilityAggregated(isVisible);
+    }
+
     protected boolean isPageInTransition() {
         return mIsPageInTransition;
     }
@@ -381,7 +424,6 @@
      * to provide custom behavior during animation.
      */
     protected void onPageEndTransition() {
-        mWasInOverscroll = false;
         AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
         AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
                 AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
@@ -403,56 +445,12 @@
         }
     }
 
-    protected int getUnboundedScroll() {
-        return mUnboundedScroll;
-    }
-
-    @Override
-    public void scrollBy(int x, int y) {
-        mOrientationHandler.delegateScrollBy(this, getUnboundedScroll(), x, y);
-    }
-
     @Override
     public void scrollTo(int x, int y) {
-        int primaryScroll = mOrientationHandler.getPrimaryValue(x, y);
-        int secondaryScroll = mOrientationHandler.getSecondaryValue(x, y);
-        mUnboundedScroll = primaryScroll;
-
-        boolean isBeforeFirstPage = mIsRtl ?
-            (primaryScroll > mMaxScroll) : (primaryScroll < mMinScroll);
-        boolean isAfterLastPage = mIsRtl ?
-            (primaryScroll < mMinScroll) : (primaryScroll > mMaxScroll);
-        if (!isBeforeFirstPage && !isAfterLastPage) {
-            mSpringOverScroll = 0;
-        }
-
-        if (isBeforeFirstPage) {
-            mOrientationHandler.delegateScrollTo(this,
-                secondaryScroll, mIsRtl ? mMaxScroll : mMinScroll);
-            if (mAllowOverScroll) {
-                mWasInOverscroll = true;
-                overScroll(primaryScroll - (mIsRtl ? mMaxScroll : mMinScroll));
-            }
-        } else if (isAfterLastPage) {
-            mOrientationHandler.delegateScrollTo(this,
-                secondaryScroll, mIsRtl ? mMinScroll : mMaxScroll);
-            if (mAllowOverScroll) {
-                mWasInOverscroll = true;
-                overScroll(primaryScroll - (mIsRtl ? mMinScroll : mMaxScroll));
-            }
-        } else {
-            if (mWasInOverscroll) {
-                overScroll(0);
-                mWasInOverscroll = false;
-            }
-            super.scrollTo(x, y);
-        }
-    }
-
-    /**
-     * Helper for {@link PagedOrientationHandler} to be able to call parent's scrollTo method
-     */
-    public void superScrollTo(int x, int y) {
+        x = Utilities.boundToRange(x,
+                mOrientationHandler.getPrimaryValue(mMinScroll, 0), mMaxScroll);
+        y = Utilities.boundToRange(y,
+                mOrientationHandler.getPrimaryValue(0, mMinScroll), mMaxScroll);
         super.scrollTo(x, y);
     }
 
@@ -470,11 +468,6 @@
         }
     }
 
-    // we moved this functionality to a helper function so SmoothPagedView can reuse it
-    protected boolean computeScrollHelper() {
-        return computeScrollHelper(true);
-    }
-
     protected void announcePageForAccessibility() {
         if (isAccessibilityEnabled(getContext())) {
             // Notify the user when the page changes
@@ -482,21 +475,37 @@
         }
     }
 
-    protected boolean computeScrollHelper(boolean shouldInvalidate) {
+    protected boolean computeScrollHelper() {
         if (mScroller.computeScrollOffset()) {
             // Don't bother scrolling if the page does not need to be moved
-            int currentScroll = mOrientationHandler.getPrimaryScroll(this);
-            if (mUnboundedScroll != mScroller.getCurrPos()
-                || currentScroll != mScroller.getCurrPos()) {
-                mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrPos());
+            int oldPos = mOrientationHandler.getPrimaryScroll(this);
+            int newPos = mScroller.getCurrX();
+            if (oldPos != newPos) {
+                mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrX());
             }
-            if (shouldInvalidate) {
-                invalidate();
-            }
-            return true;
-        } else if (mNextPage != INVALID_PAGE && shouldInvalidate) {
-            sendScrollAccessibilityEvent();
 
+            if (mAllowOverScroll) {
+                if (newPos < mMinScroll && oldPos >= mMinScroll) {
+                    mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
+                    mScroller.abortAnimation();
+                } else if (newPos > mMaxScroll && oldPos <= mMaxScroll) {
+                    mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
+                    mScroller.abortAnimation();
+                }
+            }
+
+            // If the scroller has scrolled to the final position and there is no edge effect, then
+            // finish the scroller to skip waiting for additional settling
+            int finalPos = mOrientationHandler.getPrimaryValue(mScroller.getFinalX(),
+                    mScroller.getFinalY());
+            if (newPos == finalPos && mEdgeGlowLeft.isFinished() && mEdgeGlowRight.isFinished()) {
+                mScroller.abortAnimation();
+            }
+
+            invalidate();
+            return true;
+        } else if (mNextPage != INVALID_PAGE) {
+            sendScrollAccessibilityEvent();
             int prevPage = mCurrentPage;
             mCurrentPage = validateNewPage(mNextPage);
             mNextPage = INVALID_PAGE;
@@ -550,6 +559,10 @@
         super.forceLayout();
     }
 
+    private int getPageWidthSize(int widthSize) {
+        return (widthSize - mInsets.left - mInsets.right) / getPanelCount();
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (getChildCount() == 0) {
@@ -580,7 +593,7 @@
         if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
 
         int myWidthSpec = MeasureSpec.makeMeasureSpec(
-                widthSize - mInsets.left - mInsets.right, MeasureSpec.EXACTLY);
+                getPageWidthSize(widthSize), MeasureSpec.EXACTLY);
         int myHeightSpec = MeasureSpec.makeMeasureSpec(
                 heightSize - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY);
 
@@ -672,11 +685,10 @@
                 final int primaryDimension = bounds.primaryDimension;
                 final int childPrimaryEnd = bounds.childPrimaryEnd;
 
-                // In case the pages are of different width, align the page to left or right edge
-                // based on the orientation.
-                final int pageScroll = mIsRtl
-                    ? (childStart - scrollOffsetStart)
-                    : Math.max(0, childPrimaryEnd  - scrollOffsetEnd);
+                // In case the pages are of different width, align the page to left edge for non-RTL
+                // or right edge for RTL.
+                final int pageScroll =
+                        mIsRtl ? childPrimaryEnd - scrollOffsetEnd : childStart - scrollOffsetStart;
                 if (outPageScrolls[i] != pageScroll) {
                     pageScrollChanged = true;
                     outPageScrolls[i] = pageScroll;
@@ -684,6 +696,19 @@
                 childStart += primaryDimension + mPageSpacing + getChildGap();
             }
         }
+
+        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
+                // panels on the screen.
+                int adjustedScroll = outPageScrolls[getLeftmostVisiblePageForIndex(i)];
+                if (outPageScrolls[i] != adjustedScroll) {
+                    outPageScrolls[i] = adjustedScroll;
+                    pageScrollChanged = true;
+                }
+            }
+        }
         return pageScrollChanged;
     }
 
@@ -747,6 +772,11 @@
         return mOrientationHandler.getChildStart(pageAtIndex);
     }
 
+    protected int getChildVisibleSize(int index) {
+        View layout = getPageAt(index);
+        return mOrientationHandler.getMeasuredSize(layout);
+    }
+
     @Override
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
         int page = indexToPage(indexOfChild(child));
@@ -791,14 +821,16 @@
         }
         if (direction == View.FOCUS_LEFT) {
             if (getCurrentPage() > 0) {
-                snapToPage(getCurrentPage() - 1);
-                getChildAt(getCurrentPage() - 1).requestFocus(direction);
+                int nextPage = validateNewPage(getCurrentPage() - 1);
+                snapToPage(nextPage);
+                getChildAt(nextPage).requestFocus(direction);
                 return true;
             }
         } else if (direction == View.FOCUS_RIGHT) {
             if (getCurrentPage() < getPageCount() - 1) {
-                snapToPage(getCurrentPage() + 1);
-                getChildAt(getCurrentPage() + 1).requestFocus(direction);
+                int nextPage = validateNewPage(getCurrentPage() + 1);
+                snapToPage(nextPage);
+                getChildAt(nextPage).requestFocus(direction);
                 return true;
             }
         }
@@ -811,17 +843,26 @@
             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 (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
-            getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
+        if (nextPage >= 0 && nextPage < getPageCount()) {
+            getPageAt(nextPage).addFocusables(views, direction, focusableMode);
         }
         if (direction == View.FOCUS_LEFT) {
-            if (mCurrentPage > 0) {
-                getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode);
+            if (nextPage > 0) {
+                nextPage = validateNewPage(nextPage - 1);
+                getPageAt(nextPage).addFocusables(views, direction, focusableMode);
             }
-        } else if (direction == View.FOCUS_RIGHT){
-            if (mCurrentPage < getPageCount() - 1) {
-                getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode);
+        } else if (direction == View.FOCUS_RIGHT) {
+            if (nextPage < getPageCount() - 1) {
+                nextPage = validateNewPage(nextPage + 1);
+                getPageAt(nextPage).addFocusables(views, direction, focusableMode);
             }
         }
     }
@@ -862,8 +903,7 @@
         if (disallowIntercept) {
             // We need to make sure to cancel our long press if
             // a scrollable widget takes over touch events
-            final View currentPage = getPageAt(mCurrentPage);
-            currentPage.cancelLongPress();
+            cancelCurrentPageLongPress();
         }
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
@@ -919,9 +959,7 @@
                 mTotalMotion = 0;
                 mAllowEasyFling = false;
                 mActivePointerId = ev.getPointerId(0);
-
-                updateIsBeingDraggedOnTouchDown();
-
+                updateIsBeingDraggedOnTouchDown(ev);
                 break;
             }
 
@@ -946,9 +984,9 @@
     /**
      * If being flinged and user touches the screen, initiate drag; otherwise don't.
      */
-    private void updateIsBeingDraggedOnTouchDown() {
+    private void updateIsBeingDraggedOnTouchDown(MotionEvent ev) {
         // mScroller.isFinished should be false when being flinged.
-        final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos());
+        final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
         final boolean finishedScrolling = (mScroller.isFinished() || xDist < mPageSlop / 3);
 
         if (finishedScrolling) {
@@ -957,9 +995,20 @@
                 setCurrentPage(getNextPage());
                 pageEndTransition();
             }
+            mIsBeingDragged = !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished();
         } else {
             mIsBeingDragged = true;
         }
+
+        // Catch the edge effect if it is active.
+        float displacement = mOrientationHandler.getSecondaryValue(ev.getX(), ev.getY())
+                / mOrientationHandler.getSecondaryValue(getWidth(), getHeight());
+        if (!mEdgeGlowLeft.isFinished()) {
+            mEdgeGlowLeft.onPullDistance(0f, 1f - displacement);
+        }
+        if (!mEdgeGlowRight.isFinished()) {
+            mEdgeGlowRight.onPullDistance(0f, displacement);
+        }
     }
 
     public boolean isHandlingTouch() {
@@ -990,7 +1039,6 @@
             mTotalMotion += Math.abs(mLastMotion - primaryDirection);
             mLastMotion = primaryDirection;
             mLastMotionRemainder = 0;
-            onScrollInteractionBegin();
             pageBeginTransition();
             // Stop listening for things like pinches.
             requestDisallowInterceptTouchEvent(true);
@@ -1001,10 +1049,7 @@
         // Try canceling the long press. It could also have been scheduled
         // by a distant descendant, so use the mAllowLongPress flag to block
         // everything
-        final View currentPage = getPageAt(mCurrentPage);
-        if (currentPage != null) {
-            currentPage.cancelLongPress();
-        }
+        forEachVisiblePage(View::cancelLongPress);
     }
 
     protected float getScrollProgress(int screenCenter, View v, int page) {
@@ -1054,69 +1099,6 @@
         }
     }
 
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        if (mScroller.isSpringing() && mSpringOverScroll != 0) {
-            int saveCount = canvas.save();
-            mOrientationHandler.set(canvas, CANVAS_TRANSLATE, -mSpringOverScroll);
-            super.dispatchDraw(canvas);
-
-            canvas.restoreToCount(saveCount);
-        } else {
-            super.dispatchDraw(canvas);
-        }
-    }
-
-    /**
-     * Returns the amount of overscroll caused by the spring in {@link OverScroller}.
-     */
-    private int getSpringOverScroll(int amount) {
-        if (mScroller.isSpringing()) {
-            return amount < 0
-                    ? mScroller.getCurrPos() - mMinScroll
-                    : Math.max(0, mScroller.getCurrPos() - mMaxScroll);
-        } else {
-            return 0;
-        }
-    }
-
-    protected void dampedOverScroll(int amount) {
-        if (amount == 0) {
-            return;
-        }
-
-        int size = mOrientationHandler.getMeasuredSize(this);
-        int overScrollAmount = OverScroll.dampedScroll(amount, size);
-        if (mScroller.isSpringing()) {
-            mSpringOverScroll = getSpringOverScroll(amount);
-            invalidate();
-            return;
-        }
-
-        int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
-        int boundedScroll = Utilities.boundToRange(primaryScroll, mMinScroll, mMaxScroll);
-        mOrientationHandler.delegateScrollTo(this, boundedScroll + overScrollAmount);
-        invalidate();
-    }
-
-    protected void overScroll(int amount) {
-        if (mScroller.isSpringing()) {
-            mSpringOverScroll = getSpringOverScroll(amount);
-            invalidate();
-            return;
-        }
-
-        if (amount == 0) return;
-
-        if (mFreeScroll && !mScroller.isFinished()) {
-            int scrollAmount = amount < 0 ? mMinScroll + amount : mMaxScroll + amount;
-            mOrientationHandler.delegateScrollTo(this, scrollAmount);
-        } else {
-            dampedOverScroll(amount);
-        }
-    }
-
-
     public void setEnableFreeScroll(boolean freeScroll) {
         if (mFreeScroll == freeScroll) {
             return;
@@ -1149,7 +1131,7 @@
 
         switch (action & MotionEvent.ACTION_MASK) {
         case MotionEvent.ACTION_DOWN:
-            updateIsBeingDraggedOnTouchDown();
+            updateIsBeingDraggedOnTouchDown(ev);
 
             /*
              * If being flinged and user touches, stop the fling. isFinished
@@ -1168,7 +1150,6 @@
             mAllowEasyFling = false;
             mActivePointerId = ev.getPointerId(0);
             if (mIsBeingDragged) {
-                onScrollInteractionBegin();
                 pageBeginTransition();
             }
             break;
@@ -1185,19 +1166,62 @@
                 final int pointerIndex = ev.findPointerIndex(mActivePointerId);
 
                 if (pointerIndex == -1) return true;
+                float oldScroll = mOrientationHandler.getPrimaryScroll(this);
+                float dx = ev.getX(pointerIndex);
+                float dy = ev.getY(pointerIndex);
 
-                float direction = mOrientationHandler.getPrimaryDirection(ev, pointerIndex);
+                float direction = mOrientationHandler.getPrimaryValue(dx, dy);
                 float delta = mLastMotion + mLastMotionRemainder - direction;
+
+                int width = getWidth();
+                int height = getHeight();
+                int size = mOrientationHandler.getPrimaryValue(width, height);
+
+                final float displacement = mOrientationHandler.getSecondaryValue(dx, dy)
+                        / mOrientationHandler.getSecondaryValue(width, height);
                 mTotalMotion += Math.abs(delta);
 
+                if (mAllowOverScroll) {
+                    float consumed = 0;
+                    if (delta < 0 && mEdgeGlowRight.getDistance() != 0f) {
+                        consumed = size * mEdgeGlowRight.onPullDistance(delta / size, displacement);
+                    } else if (delta > 0 && mEdgeGlowLeft.getDistance() != 0f) {
+                        consumed = -size * mEdgeGlowLeft.onPullDistance(
+                                -delta / size, 1 - displacement);
+                    }
+                    delta -= consumed;
+                }
+
                 // 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
                 // scrolled position (which is discrete).
-                if (Math.abs(delta) >= 1.0f) {
-                    mLastMotion = direction;
-                    mLastMotionRemainder = delta - (int) delta;
+                mLastMotion = direction;
+                int movedDelta = (int) delta;
+                mLastMotionRemainder = delta - movedDelta;
 
-                    mOrientationHandler.set(this, VIEW_SCROLL_BY, (int) delta);
+                if (delta != 0) {
+                    mOrientationHandler.set(this, VIEW_SCROLL_BY, movedDelta);
+
+                    if (mAllowOverScroll) {
+                        final float pulledToX = oldScroll + delta;
+
+                        if (pulledToX < mMinScroll) {
+                            mEdgeGlowLeft.onPullDistance(-delta / size, 1.f - displacement);
+                            if (!mEdgeGlowRight.isFinished()) {
+                                mEdgeGlowRight.onRelease();
+                            }
+                        } else if (pulledToX > mMaxScroll) {
+                            mEdgeGlowRight.onPullDistance(delta / size, displacement);
+                            if (!mEdgeGlowLeft.isFinished()) {
+                                mEdgeGlowLeft.onRelease();
+                            }
+                        }
+
+                        if (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished()) {
+                            postInvalidateOnAnimation();
+                        }
+                    }
+
                 } else {
                     awakenScrollBars();
                 }
@@ -1252,12 +1276,14 @@
 
                     if (((isSignificantMove && !isDeltaLeft && !isFling) ||
                             (isFling && !isVelocityLeft)) && mCurrentPage > 0) {
-                        finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
+                        finalPage = returnToOriginalPage
+                                ? mCurrentPage : mCurrentPage - getPanelCount();
                         snapToPageWithVelocity(finalPage, velocity);
                     } else if (((isSignificantMove && isDeltaLeft && !isFling) ||
                             (isFling && isVelocityLeft)) &&
                             mCurrentPage < getChildCount() - 1) {
-                        finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
+                        finalPage = returnToOriginalPage
+                                ? mCurrentPage : mCurrentPage + getPanelCount();
                         snapToPageWithVelocity(finalPage, velocity);
                     } else {
                         snapToDestination();
@@ -1273,44 +1299,24 @@
 
                     if (((initialScroll >= maxScroll) && (isVelocityLeft || !isFling)) ||
                         ((initialScroll <= minScroll) && (!isVelocityLeft || !isFling))) {
-                        mScroller.springBack(initialScroll, minScroll, maxScroll);
-                        mNextPage = getPageNearestToCenterOfScreen();
+                        mScroller.springBack(initialScroll, 0, minScroll, maxScroll, 0, 0);
+                        mNextPage = getDestinationPage();
                     } else {
-                        mScroller.setInterpolator(mDefaultInterpolator);
-                        mScroller.fling(initialScroll, -velocity,
-                            minScroll, maxScroll,
-                            Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR));
+                        int velocity1 = -velocity;
+                        // Continue a scroll or fling in progress
+                        mScroller.fling(initialScroll, 0, velocity1, 0, minScroll, maxScroll, 0, 0,
+                                Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR), 0);
 
-                        int finalPos = mScroller.getFinalPos();
-                        mNextPage = getPageNearestToCenterOfScreen(finalPos);
-
-                        int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
-                        int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
-                        if (finalPos > minScroll && finalPos < maxScroll) {
-                            // If scrolling ends in the half of the added space that is closer to
-                            // the end, settle to the end. Otherwise snap to the nearest page.
-                            // If flinging past one of the ends, don't change the velocity as it
-                            // will get stopped at the end anyway.
-                            int pageSnapped = finalPos < (firstPageScroll + minScroll) / 2
-                                ? minScroll
-                                : finalPos > (lastPageScroll + maxScroll) / 2
-                                    ? maxScroll
-                                    : getScrollForPage(mNextPage);
-
-                            mScroller.setFinalPos(pageSnapped);
-                            // Ensure the scroll/snap doesn't happen too fast;
-                            int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
-                                - mScroller.getDuration();
-                            if (extraScrollDuration > 0) {
-                                mScroller.extendDuration(extraScrollDuration);
-                            }
-                        }
+                        int finalPos = mScroller.getFinalX();
+                        mNextPage = getDestinationPage(finalPos);
+                        onNotSnappingToPageInFreeScroll();
                     }
                     invalidate();
                 }
-                onScrollInteractionEnd();
             }
 
+            mEdgeGlowLeft.onRelease();
+            mEdgeGlowRight.onRelease();
             // End any intermediate reordering states
             resetTouchState();
             break;
@@ -1318,8 +1324,9 @@
         case MotionEvent.ACTION_CANCEL:
             if (mIsBeingDragged) {
                 snapToDestination();
-                onScrollInteractionEnd();
             }
+            mEdgeGlowLeft.onRelease();
+            mEdgeGlowRight.onRelease();
             resetTouchState();
             break;
 
@@ -1332,6 +1339,8 @@
         return true;
     }
 
+    protected void onNotSnappingToPageInFreeScroll() { }
+
     protected boolean shouldFlingForVelocity(int velocity) {
         float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity;
         return Math.abs(velocity) > threshold;
@@ -1343,15 +1352,6 @@
         mActivePointerId = INVALID_POINTER;
     }
 
-    /**
-     * Triggered by scrolling via touch
-     */
-    protected void onScrollInteractionBegin() {
-    }
-
-    protected void onScrollInteractionEnd() {
-    }
-
     @Override
     public boolean onGenericMotionEvent(MotionEvent event) {
         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -1431,19 +1431,34 @@
     @Override
     public void requestChildFocus(View child, View focused) {
         super.requestChildFocus(child, focused);
+
+        // In case the device is controlled by a controller, mCurrentPage isn't updated properly
+        // which results in incorrect navigation
+        int nextPage = getNextPage();
+        if (nextPage != mCurrentPage) {
+            setCurrentPage(nextPage);
+        }
+
         int page = indexToPage(indexOfChild(child));
         if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) {
             snapToPage(page);
         }
     }
 
+    public int getDestinationPage() {
+        return getDestinationPage(mOrientationHandler.getPrimaryScroll(this));
+    }
+
+    protected int getDestinationPage(int primaryScroll) {
+        return getPageNearestToCenterOfScreen(primaryScroll);
+    }
+
     public int getPageNearestToCenterOfScreen() {
         return getPageNearestToCenterOfScreen(mOrientationHandler.getPrimaryScroll(this));
     }
 
-    private int getPageNearestToCenterOfScreen(int scaledScroll) {
-        int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
-        int screenCenter = scaledScroll + (pageOrientationSize / 2);
+    private int getPageNearestToCenterOfScreen(int primaryScroll) {
+        int screenCenter = getScreenCenter(primaryScroll);
         int minDistanceFromScreenCenter = Integer.MAX_VALUE;
         int minDistanceFromScreenCenterIndex = -1;
         final int childCount = getChildCount();
@@ -1459,33 +1474,28 @@
     }
 
     private int getDisplacementFromScreenCenter(int childIndex, int screenCenter) {
-        View layout = getPageAt(childIndex);
-        int childSize = mOrientationHandler.getMeasuredSize(layout);
+        int childSize = Math.round(getChildVisibleSize(childIndex));
         int halfChildSize = (childSize / 2);
         int childCenter = getChildOffset(childIndex) + halfChildSize;
         return childCenter - screenCenter;
     }
 
     protected int getDisplacementFromScreenCenter(int childIndex) {
-        int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
-        int screenCenter = mOrientationHandler.getPrimaryScroll(this) + (pageOrientationSize / 2);
+        int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+        int screenCenter = getScreenCenter(primaryScroll);
         return getDisplacementFromScreenCenter(childIndex, screenCenter);
     }
 
+    private int getScreenCenter(int primaryScroll) {
+        float primaryScale = mOrientationHandler.getPrimaryScale(this);
+        float primaryPivot =  mOrientationHandler.getPrimaryValue(getPivotX(), getPivotY());
+        int pageOrientationSize = mOrientationHandler.getMeasuredSize(this);
+        return Math.round(primaryScroll + (pageOrientationSize / 2f - primaryPivot) / primaryScale
+                + primaryPivot);
+    }
+
     protected void snapToDestination() {
-        snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
-    }
-
-    protected boolean isInOverScroll() {
-        int scroll = mOrientationHandler.getPrimaryScroll(this);
-        return scroll > mMaxScroll || scroll < mMinScroll;
-    }
-
-    protected int getPageSnapDuration() {
-        if (isInOverScroll()) {
-            return OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION;
-        }
-        return PAGE_SNAP_ANIMATION_DURATION;
+        snapToPage(getDestinationPage(), PAGE_SNAP_ANIMATION_DURATION);
     }
 
     // We want the duration of the page snap animation to be influenced by the distance that
@@ -1503,7 +1513,7 @@
         int halfScreenSize = mOrientationHandler.getMeasuredSize(this) / 2;
 
         final int newLoc = getScrollForPage(whichPage);
-        int delta = newLoc - getUnboundedScroll();
+        int delta = newLoc - mOrientationHandler.getPrimaryScroll(this);
         int duration = 0;
 
         if (Math.abs(velocity) < mMinFlingVelocity) {
@@ -1528,12 +1538,7 @@
         // interpolator at zero, ie. 5. We use 4 to make it a little slower.
         duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
 
-        if (QUICKSTEP_SPRINGS.get() && mCurrentPage != whichPage) {
-            return snapToPage(whichPage, delta, duration, false, null,
-                    velocity * Math.signum(delta), true);
-        } else {
-            return snapToPage(whichPage, delta, duration);
-        }
+        return snapToPage(whichPage, delta, duration);
     }
 
     public boolean snapToPage(int whichPage) {
@@ -1541,32 +1546,26 @@
     }
 
     public boolean snapToPageImmediately(int whichPage) {
-        return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
+        return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
     }
 
     public boolean snapToPage(int whichPage, int duration) {
-        return snapToPage(whichPage, duration, false, null);
+        return snapToPage(whichPage, duration, false);
     }
 
-    public boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
-        return snapToPage(whichPage, duration, false, interpolator);
-    }
-
-    protected boolean snapToPage(int whichPage, int duration, boolean immediate,
-            TimeInterpolator interpolator) {
+    protected boolean snapToPage(int whichPage, int duration, boolean immediate) {
         whichPage = validateNewPage(whichPage);
 
         int newLoc = getScrollForPage(whichPage);
-        final int delta = newLoc - getUnboundedScroll();
-        return snapToPage(whichPage, delta, duration, immediate, interpolator, 0, false);
+        final int delta = newLoc - mOrientationHandler.getPrimaryScroll(this);
+        return snapToPage(whichPage, delta, duration, immediate);
     }
 
     protected boolean snapToPage(int whichPage, int delta, int duration) {
-        return snapToPage(whichPage, delta, duration, false, null, 0, false);
+        return snapToPage(whichPage, delta, duration, false);
     }
 
-    protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
-            TimeInterpolator interpolator, float velocity, boolean spring) {
+    protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate) {
         if (mFirstLayout) {
             setCurrentPage(whichPage);
             return false;
@@ -1596,18 +1595,7 @@
             abortScrollerAnimation(false);
         }
 
-        if (interpolator != null) {
-            mScroller.setInterpolator(interpolator);
-        } else {
-            mScroller.setInterpolator(mDefaultInterpolator);
-        }
-
-        if (spring && QUICKSTEP_SPRINGS.get()) {
-            mScroller.startScrollSpring(getUnboundedScroll(), delta, duration, velocity);
-        } else {
-            mScroller.startScroll(getUnboundedScroll(), delta, duration);
-        }
-
+        mScroller.startScroll(mOrientationHandler.getPrimaryScroll(this), 0, delta, 0, duration);
         updatePageIndicator();
 
         // Trigger a compute() to finish switching pages if necessary
@@ -1625,7 +1613,7 @@
             snapToPage(getNextPage() - 1);
             return true;
         }
-        return onOverscroll(-getMeasuredWidth());
+        return mAllowOverScroll;
     }
 
     public boolean scrollRight() {
@@ -1633,15 +1621,7 @@
             snapToPage(getNextPage() + 1);
             return true;
         }
-        return onOverscroll(getMeasuredWidth());
-    }
-
-    protected boolean onOverscroll(int amount) {
-        if (!mAllowOverScroll) return false;
-        onScrollInteractionBegin();
-        overScroll(amount);
-        onScrollInteractionEnd();
-        return true;
+        return mAllowOverScroll;
     }
 
     @Override
@@ -1785,4 +1765,39 @@
         mTmpIntPair[1] = rightChild;
         return mTmpIntPair;
     }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        drawEdgeEffect(canvas);
+        pageEndTransition();
+    }
+
+    protected void drawEdgeEffect(Canvas canvas) {
+        if (mAllowOverScroll && (!mEdgeGlowRight.isFinished() || !mEdgeGlowLeft.isFinished())) {
+            final int width = getWidth();
+            final int height = getHeight();
+            if (!mEdgeGlowLeft.isFinished()) {
+                final int restoreCount = canvas.save();
+                canvas.rotate(-90);
+                canvas.translate(-height, Math.min(mMinScroll, getScrollX()));
+                mEdgeGlowLeft.setSize(height, width);
+                if (mEdgeGlowLeft.draw(canvas)) {
+                    postInvalidateOnAnimation();
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+            if (!mEdgeGlowRight.isFinished()) {
+                final int restoreCount = canvas.save();
+                canvas.rotate(90, width, 0);
+                canvas.translate(width, -(Math.max(mMaxScroll, getScrollX())));
+
+                mEdgeGlowRight.setSize(height, width);
+                if (mEdgeGlowRight.draw(canvas)) {
+                    postInvalidateOnAnimation();
+                }
+                canvas.restoreToCount(restoreCount);
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
index d79f62d..0bdb37c 100644
--- a/src/com/android/launcher3/Partner.java
+++ b/src/com/android/launcher3/Partner.java
@@ -129,7 +129,7 @@
                     "dimen", getPackageName());
             if (resId > 0) {
                 int px = getResources().getDimensionPixelSize(resId);
-                iconSize = Utilities.dpiFromPx(px, dm);
+                iconSize = Utilities.dpiFromPx((float) px, dm.densityDpi);
             }
         } catch (Resources.NotFoundException ex) {
             Log.e(TAG, "Invalid Partner grid resource!", ex);
diff --git a/src/com/android/launcher3/ResourceUtils.java b/src/com/android/launcher3/ResourceUtils.java
index 403d779..ece123d 100644
--- a/src/com/android/launcher3/ResourceUtils.java
+++ b/src/com/android/launcher3/ResourceUtils.java
@@ -22,8 +22,11 @@
 
 public class ResourceUtils {
     public static final int DEFAULT_NAVBAR_VALUE = 48;
+    public static final int INVALID_RESOURCE_HANDLE = -1;
     public static final String NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE = "navigation_bar_width";
     public static final String NAVBAR_BOTTOM_GESTURE_SIZE = "navigation_bar_gesture_height";
+    public static final String NAVBAR_BOTTOM_GESTURE_LARGER_SIZE =
+            "navigation_bar_gesture_larger_height";
 
     public static int getNavbarSize(String resName, Resources res) {
         return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE);
@@ -51,7 +54,17 @@
         return val;
     }
 
+    public static int getIntegerByName(String resName, Resources res, int defaultValue) {
+        int resId = res.getIdentifier(resName, "integer", "android");
+        return resId != 0 ? res.getInteger(resId) : defaultValue;
+    }
+
     public static int pxFromDp(float size, DisplayMetrics metrics) {
-        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, metrics));
+        return pxFromDp(size, metrics, 1f);
+    }
+
+    public static int pxFromDp(float size, DisplayMetrics metrics, float scale) {
+        return size < 0 ? INVALID_RESOURCE_HANDLE : Math.round(scale
+                * TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, metrics));
     }
 }
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 499b54f..cd06414 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -3,6 +3,7 @@
 import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
 import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE;
 
+import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
@@ -33,23 +34,21 @@
 import android.view.View;
 import android.widget.Toast;
 
-import com.android.launcher3.Launcher.OnResumeCallback;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 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.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
 import java.net.URISyntaxException;
-import java.util.ArrayList;
 
 /**
  * Drop target which provides a secondary option for an item.
@@ -111,16 +110,13 @@
         mCurrentAccessibilityAction = action;
 
         if (action == UNINSTALL) {
-            mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
-            setDrawable(R.drawable.ic_uninstall_shadow);
+            setDrawable(R.drawable.ic_uninstall_no_shadow);
             updateText(R.string.uninstall_drop_target_label);
         } else if (action == DISMISS_PREDICTION) {
-            mHoverColor = Themes.getColorAccent(getContext());
-            setDrawable(R.drawable.ic_block_shadow);
+            setDrawable(R.drawable.ic_block_no_shadow);
             updateText(R.string.dismiss_prediction_label);
         } else if (action == RECONFIGURE) {
-            mHoverColor = Themes.getColorAccent(getContext());
-            setDrawable(R.drawable.ic_setup_shadow);
+            setDrawable(R.drawable.ic_setting);
             updateText(R.string.gadget_setup_text);
         }
     }
@@ -136,19 +132,6 @@
     }
 
     @Override
-    public Target getDropTargetForLogging() {
-        Target t = LoggerUtils.newTarget(Target.Type.CONTROL);
-        if (mCurrentAccessibilityAction == UNINSTALL) {
-            t.controlType = ControlType.UNINSTALL_TARGET;
-        } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
-            t.controlType = ControlType.DISMISS_PREDICTION;
-        } else {
-            t.controlType = ControlType.SETTINGS_BUTTON;
-        }
-        return t;
-    }
-
-    @Override
     protected boolean supportsDrop(ItemInfo info) {
         return supportsAccessibilityDrop(info, getViewUnderDrag(info));
     }
@@ -218,13 +201,20 @@
     public void onDrop(DragObject d, DragOptions options) {
         // Defer onComplete
         d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
+
         super.onDrop(d, options);
+        doLog(d.logInstanceId, d.originalDragInfo);
+    }
+
+    private void doLog(InstanceId logInstanceId, ItemInfo itemInfo) {
+        StatsLogger logger = mStatsLogManager.logger().withInstanceId(logInstanceId);
+        if (itemInfo != null) {
+            logger.withItemInfo(itemInfo);
+        }
         if (mCurrentAccessibilityAction == UNINSTALL) {
-            mStatsLogManager.logger().withInstanceId(d.logInstanceId)
-                    .log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
+            logger.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
         } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
-            mStatsLogManager.logger().withInstanceId(d.logInstanceId)
-                    .log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
+            logger.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
         }
     }
 
@@ -235,7 +225,7 @@
             DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
             if (target != null) {
                 deferred.mPackageName = target.getPackageName();
-                mLauncher.addOnResumeCallback(deferred);
+                mLauncher.addOnResumeCallback(deferred::onLauncherResume);
             } else {
                 deferred.sendFailure();
             }
@@ -278,13 +268,15 @@
         if (mCurrentAccessibilityAction == RECONFIGURE) {
             int widgetId = getReconfigurableWidgetId(view);
             if (widgetId != INVALID_APPWIDGET_ID) {
-                mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId, -1);
+                mLauncher.setWaitingForResult(
+                        PendingRequestArgs.forWidgetInfo(widgetId, null, info));
+                mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId,
+                        REQUEST_RECONFIGURE_APPWIDGET);
             }
             return null;
         }
         if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
-            AppLaunchTracker.INSTANCE.get(getContext()).onDismissApp(info.getTargetComponent(),
-                    info.user, AppLaunchTracker.CONTAINER_PREDICTIONS);
+            // We sent the log event, nothing else left to do
             return null;
         }
         // else: mCurrentAccessibilityAction == UNINSTALL
@@ -311,6 +303,7 @@
 
     @Override
     public void onAccessibilityDrop(View view, ItemInfo item) {
+        doLog(new InstanceIdSequence().newInstanceId(), item);
         performDropAction(view, item);
     }
 
@@ -318,7 +311,7 @@
      * A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until
      * {@link #onLauncherResume}
      */
-    private class DeferredOnComplete implements DragSource, OnResumeCallback {
+    private class DeferredOnComplete implements DragSource {
 
         private final DragSource mOriginal;
         private final Context mContext;
@@ -337,13 +330,6 @@
             mDragObject = d;
         }
 
-        @Override
-        public void fillInLogContainerData(ItemInfo childInfo, Target child,
-                ArrayList<Target> parents) {
-            mOriginal.fillInLogContainerData(childInfo, child, parents);
-        }
-
-        @Override
         public void onLauncherResume() {
             // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
             if (new PackageManagerHelper(mContext).getApplicationInfo(mPackageName,
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 89f0a3d..558538c 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -16,55 +16,40 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
-
-import android.annotation.TargetApi;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.Log;
 
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.util.Executors;
 
-import java.util.List;
-
 /**
  * BroadcastReceiver to handle session commit intent.
  */
-@TargetApi(Build.VERSION_CODES.O)
 public class SessionCommitReceiver extends BroadcastReceiver {
 
-    private static final String TAG = "SessionCommitReceiver";
-
-    // The content provider for the add to home screen setting. It should be of the format:
-    // <package name>.addtohomescreen
-    private static final String MARKER_PROVIDER_PREFIX = ".addtohomescreen";
+    private static final String LOG = "SessionCommitReceiver";
 
     // Preference key for automatically adding icon to homescreen.
     public static final String ADD_ICON_PREFERENCE_KEY = "pref_add_icon_to_home";
-    public static final String ADD_ICON_PREFERENCE_INITIALIZED_KEY =
-            "pref_add_icon_to_home_initialized";
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!isEnabled(context) || !Utilities.ATLEAST_OREO) {
+        Executors.MODEL_EXECUTOR.execute(() -> processIntent(context, intent));
+    }
+
+    @WorkerThread
+    private static void processIntent(Context context, Intent intent) {
+        if (!isEnabled(context)) {
             // User has decided to not add icons on homescreen.
             return;
         }
@@ -86,105 +71,16 @@
             return;
         }
 
-        queueAppIconAddition(context, info.getAppPackageName(), user);
-    }
+        FileLog.d(LOG,
+                "Adding package name to install queue. Package name: " + info.getAppPackageName()
+                        + ", has app icon: " + (info.getAppIcon() != null)
+                        + ", has app label: " + !TextUtils.isEmpty(info.getAppLabel()));
 
-    public static void queuePromiseAppIconAddition(Context context, SessionInfo sessionInfo) {
-        String packageName = sessionInfo.getAppPackageName();
-        if (context.getSystemService(LauncherApps.class)
-                .getActivityList(packageName, getUserHandle(sessionInfo)).isEmpty()) {
-            // Ensure application isn't already installed.
-            queueAppIconAddition(context, packageName, sessionInfo.getAppLabel(),
-                    sessionInfo.getAppIcon(), getUserHandle(sessionInfo));
-        }
-    }
-
-    public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
-        List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
-                .getActivityList(packageName, user);
-        if (activities.isEmpty()) {
-            // no activity found
-            return;
-        }
-        queueAppIconAddition(context, packageName, activities.get(0).getLabel(), null, user);
-    }
-
-    private static void queueAppIconAddition(Context context, String packageName,
-            CharSequence label, Bitmap icon, UserHandle user) {
-        Intent data = new Intent();
-        data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
-                new ComponentName(packageName, "")).setPackage(packageName));
-        data.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
-        data.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
-
-        InstallShortcutReceiver.queueApplication(data, user, context);
+        ItemInstallQueue.INSTANCE.get(context)
+                .queueItem(info.getAppPackageName(), user);
     }
 
     public static boolean isEnabled(Context context) {
         return Utilities.getPrefs(context).getBoolean(ADD_ICON_PREFERENCE_KEY, true);
     }
-
-    public static void applyDefaultUserPrefs(final Context context) {
-        if (!Utilities.ATLEAST_OREO) {
-            return;
-        }
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        if (prefs.getAll().isEmpty()) {
-            // This logic assumes that the code is the first thing that is executed (before any
-            // shared preference is written).
-            // TODO: Move this logic to DB upgrade once we have proper support for db downgrade
-            // If it is a fresh start, just apply the default value. We use prefs.isEmpty() to infer
-            // a fresh start as put preferences always contain some values corresponding to current
-            // grid.
-            prefs.edit().putBoolean(ADD_ICON_PREFERENCE_KEY, true).apply();
-        } else if (!prefs.contains(ADD_ICON_PREFERENCE_INITIALIZED_KEY)) {
-            new PrefInitTask(context).executeOnExecutor(Executors.THREAD_POOL_EXECUTOR);
-        }
-    }
-
-    private static class PrefInitTask extends AsyncTask<Void, Void, Void> {
-        private final Context mContext;
-
-        PrefInitTask(Context context) {
-            mContext = context;
-        }
-
-        @Override
-        protected Void doInBackground(Void... voids) {
-            boolean addIconToHomeScreenEnabled = readValueFromMarketApp();
-            Utilities.getPrefs(mContext).edit()
-                    .putBoolean(ADD_ICON_PREFERENCE_KEY, addIconToHomeScreenEnabled)
-                    .putBoolean(ADD_ICON_PREFERENCE_INITIALIZED_KEY, true)
-                    .apply();
-            return null;
-        }
-
-        public boolean readValueFromMarketApp() {
-            // Get the marget package
-            ResolveInfo ri = mContext.getPackageManager().resolveActivity(
-                    new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET),
-                    PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_SYSTEM_ONLY);
-            if (ri == null) {
-                return true;
-            }
-
-            Cursor c = null;
-            try {
-                c = mContext.getContentResolver().query(
-                        Uri.parse("content://" + ri.activityInfo.packageName
-                                + MARKER_PROVIDER_PREFIX),
-                        null, null, null, null);
-                if (c.moveToNext()) {
-                    return c.getInt(c.getColumnIndexOrThrow(Settings.NameValueTable.VALUE)) != 0;
-                }
-            } catch (Exception e) {
-                Log.d(TAG, "Error reading add to homescreen preference", e);
-            } finally {
-                if (c != null) {
-                    c.close();
-                }
-            }
-            return true;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 6326b7a..519b63d 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,6 +18,9 @@
 
 import static android.view.MotionEvent.ACTION_DOWN;
 
+import static com.android.launcher3.CellLayout.FOLDER;
+import static com.android.launcher3.CellLayout.WORKSPACE;
+
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Rect;
@@ -26,25 +29,31 @@
 import android.view.ViewGroup;
 
 import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
-public class ShortcutAndWidgetContainer extends ViewGroup {
+public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent {
     static final String TAG = "ShortcutAndWidgetContainer";
 
     // These are temporary variables to prevent having to allocate a new object just to
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
     private final int[] mTmpCellXY = new int[2];
 
-    @ContainerType private final int mContainerType;
+    private final Rect mTempRect = new Rect();
+
+    @ContainerType
+    private final int mContainerType;
     private final WallpaperManager mWallpaperManager;
 
     private int mCellWidth;
     private int mCellHeight;
+    private int mBorderSpacing;
 
     private int mCountX;
+    private int mCountY;
 
-    private ActivityContext mActivity;
+    private final ActivityContext mActivity;
     private boolean mInvertIfRtl = false;
 
     public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) {
@@ -54,20 +63,23 @@
         mContainerType = containerType;
     }
 
-    public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY) {
+    public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY,
+            int borderSpacing) {
         mCellWidth = cellWidth;
         mCellHeight = cellHeight;
         mCountX = countX;
+        mCountY = countY;
+        mBorderSpacing = borderSpacing;
     }
 
-    public View getChildAt(int x, int y) {
+    public View getChildAt(int cellX, int cellY) {
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
 
-            if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
-                    (lp.cellY <= y) && (y < lp.cellY + lp.cellVSpan)) {
+            if ((lp.cellX <= cellX) && (cellX < lp.cellX + lp.cellHSpan)
+                    && (lp.cellY <= cellY) && (cellY < lp.cellY + lp.cellVSpan)) {
                 return child;
             }
         }
@@ -79,7 +91,7 @@
         int count = getChildCount();
 
         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
-        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
+        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
         setMeasuredDimension(widthSpecSize, heightSpecSize);
 
         for (int i = 0; i < count; i++) {
@@ -94,10 +106,12 @@
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
         if (child instanceof LauncherAppWidgetHostView) {
             DeviceProfile profile = mActivity.getDeviceProfile();
-            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
-                    profile.appWidgetScale.x, profile.appWidgetScale.y);
+            ((LauncherAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
+            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
+                    profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpacing, mTempRect);
         } else {
-            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
+            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
+                    mBorderSpacing, null);
         }
     }
 
@@ -108,25 +122,34 @@
 
     public int getCellContentHeight() {
         return Math.min(getMeasuredHeight(),
-                mActivity.getDeviceProfile().getCellHeight(mContainerType));
+                mActivity.getDeviceProfile().getCellContentHeight(mContainerType));
     }
 
     public void measureChild(View child) {
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-        final DeviceProfile profile = mActivity.getDeviceProfile();
+        final DeviceProfile dp = mActivity.getDeviceProfile();
 
         if (child instanceof LauncherAppWidgetHostView) {
-            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
-                    profile.appWidgetScale.x, profile.appWidgetScale.y);
-            // Widgets have their own padding
+            ((LauncherAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
+            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
+                    dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpacing, mTempRect);
         } else {
-            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
+            lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
+                    mBorderSpacing, null);
             // Center the icon/folder
             int cHeight = getCellContentHeight();
-            int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
-            int cellPaddingX = mContainerType == CellLayout.WORKSPACE
-                    ? profile.workspaceCellPaddingXPx
-                    : (int) (profile.edgeMarginPx / 2f);
+            int cellPaddingY = dp.isScalableGrid && mContainerType == WORKSPACE
+                    ? dp.cellYPaddingPx
+                    : (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);
+            int cellPaddingX = noPaddingX
+                    ? 0
+                    : mContainerType == WORKSPACE
+                            ? dp.workspaceCellPaddingXPx
+                            : (int) (dp.edgeMarginPx / 2f);
             child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
         }
         int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
@@ -220,4 +243,24 @@
             child.cancelLongPress();
         }
     }
+
+    @Override
+    public void drawFolderLeaveBehindForIcon(FolderIcon child) {
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+        // While the folder is open, the position of the icon cannot change.
+        lp.canReorder = false;
+        if (mContainerType == CellLayout.HOTSEAT) {
+            CellLayout cl = (CellLayout) getParent();
+            cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
+        }
+    }
+
+    @Override
+    public void clearFolderLeaveBehind(FolderIcon child) {
+        ((CellLayout.LayoutParams) child.getLayoutParams()).canReorder = true;
+        if (mContainerType == CellLayout.HOTSEAT) {
+            CellLayout cl = (CellLayout) getParent();
+            cl.clearFolderLeaveBehind();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 292a808..75f6278 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,7 +18,6 @@
 
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ICON_BADGED;
 
-import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.app.Person;
@@ -26,6 +25,7 @@
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
@@ -33,9 +33,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.LightingColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Point;
@@ -44,11 +48,11 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
+import android.net.Uri;
 import android.os.Build;
 import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.Message;
-import android.os.PowerManager;
 import android.os.TransactionTooLargeException;
 import android.provider.Settings;
 import android.text.Spannable;
@@ -62,14 +66,16 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.animation.Interpolator;
+import android.widget.LinearLayout;
 
+import androidx.core.graphics.ColorUtils;
 import androidx.core.os.BuildCompat;
 
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.graphics.GridCustomizationsProvider;
 import com.android.launcher3.graphics.TintedDrawableSpan;
-import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.ShortcutCachingLogic;
 import com.android.launcher3.model.data.ItemInfo;
@@ -79,12 +85,14 @@
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
+import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -98,6 +106,8 @@
     private static final Pattern sTrimPattern =
             Pattern.compile("^[\\s|\\p{javaSpaceChar}]*(.*)[\\s|\\p{javaSpaceChar}]*$");
 
+    private static final float[] sTmpFloatArray = new float[4];
+
     private static final int[] sLoc0 = new int[2];
     private static final int[] sLoc1 = new int[2];
     private static final Matrix sMatrix = new Matrix();
@@ -106,18 +116,14 @@
     public static final String[] EMPTY_STRING_ARRAY = new String[0];
     public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
 
-    public static final boolean ATLEAST_R = BuildCompat.isAtLeastR();
+    public static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
 
     public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
-    public static final boolean ATLEAST_P =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
+    public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
 
-    public static final boolean ATLEAST_OREO_MR1 =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
-
-    public static final boolean ATLEAST_OREO =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+    public static final boolean ATLEAST_S = BuildCompat.isAtLeastS()
+            || Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
 
     /**
      * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
@@ -132,6 +138,15 @@
             Build.TYPE.toLowerCase(Locale.ROOT).contains("debug") ||
             Build.TYPE.toLowerCase(Locale.ROOT).equals("eng");
 
+    /**
+     * Returns true if theme is dark.
+     */
+    public static boolean isDarkTheme(Context context) {
+        Configuration configuration = context.getResources().getConfiguration();
+        int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        return nightMode == Configuration.UI_MODE_NIGHT_YES;
+    }
+
     public static boolean isDevelopersOptionsEnabled(Context context) {
         return Settings.Global.getInt(context.getApplicationContext().getContentResolver(),
                         Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
@@ -141,6 +156,10 @@
     public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
     public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
 
+    // An intent extra to indicate the launch source by launcher.
+    public static final String EXTRA_WALLPAPER_LAUNCH_SOURCE =
+            "com.android.wallpaper.LAUNCH_SOURCE";
+
     public static boolean IS_RUNNING_IN_TEST_HARNESS =
                     ActivityManager.isRunningInTestHarness();
 
@@ -214,6 +233,33 @@
     }
 
     /**
+     * Returns bounds for a child view of DragLayer, in drag layer coordinates.
+     *
+     * see {@link com.android.launcher3.dragndrop.DragLayer}.
+     *
+     * @param viewBounds Bounds of the view wanted in drag layer coordinates, relative to the view
+     *                   itself. eg. (0, 0, view.getWidth, view.getHeight)
+     * @param ignoreTransform If true, view transform is ignored
+     * @param outRect The out rect where we return the bounds of {@param view} in drag layer coords.
+     */
+    public static void getBoundsForViewInDragLayer(BaseDragLayer dragLayer, View view,
+            Rect viewBounds, boolean ignoreTransform, float[] recycle, RectF outRect) {
+        float[] points = recycle == null ? new float[4] : recycle;
+        points[0] = viewBounds.left;
+        points[1] = viewBounds.top;
+        points[2] = viewBounds.right;
+        points[3] = viewBounds.bottom;
+
+        Utilities.getDescendantCoordRelativeToAncestor(view, dragLayer, points,
+                false, ignoreTransform);
+        outRect.set(
+                Math.min(points[0], points[2]),
+                Math.min(points[1], points[3]),
+                Math.max(points[0], points[2]),
+                Math.max(points[1], points[3]));
+    }
+
+    /**
      * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}.
      */
     public static void mapCoordInSelfToDescendant(View descendant, View root, float[] coord) {
@@ -268,16 +314,28 @@
         return new int[] {sLoc1[0] - sLoc0[0], sLoc1[1] - sLoc0[1]};
     }
 
+    /**
+     * Helper method to set rectOut with rectFSrc.
+     */
+    public static void setRect(RectF rectFSrc, Rect rectOut) {
+        rectOut.left = (int) rectFSrc.left;
+        rectOut.top = (int) rectFSrc.top;
+        rectOut.right = (int) rectFSrc.right;
+        rectOut.bottom = (int) rectFSrc.bottom;
+    }
+
     public static void scaleRectFAboutCenter(RectF r, float scale) {
+        scaleRectFAboutPivot(r, scale, r.centerX(), r.centerY());
+    }
+
+    public static void scaleRectFAboutPivot(RectF r, float scale, float px, float py) {
         if (scale != 1.0f) {
-            float cx = r.centerX();
-            float cy = r.centerY();
-            r.offset(-cx, -cy);
+            r.offset(-px, -py);
             r.left = r.left * scale;
             r.top = r.top * scale ;
             r.right = r.right * scale;
             r.bottom = r.bottom * scale;
-            r.offset(cx, cy);
+            r.offset(px, py);
         }
     }
 
@@ -340,6 +398,13 @@
         return mapRange(interpolator.getInterpolation(progress), toMin, toMax);
     }
 
+    /** Bounds t between a lower and upper bound and maps the result to a range. */
+    public static float mapBoundToRange(float t, float lowerBound, float upperBound,
+            float toMin, float toMax, Interpolator interpolator) {
+        return mapToRange(boundToRange(t, lowerBound, upperBound), lowerBound, upperBound,
+                toMin, toMax, interpolator);
+    }
+
     public static float getProgress(float current, float min, float max) {
         return Math.abs(current - min) / Math.abs(max - min);
     }
@@ -400,14 +465,24 @@
         return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
     }
 
-    public static float dpiFromPx(float size, DisplayMetrics metrics) {
-        float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+    public static float dpiFromPx(float size, int densityDpi) {
+        float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
         return (size / densityRatio);
     }
 
+    /** Converts a dp value to pixels for the current device. */
+    public static int dpToPx(float dp) {
+        return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
+    }
+
+
     public static int pxFromSp(float size, DisplayMetrics metrics) {
-        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
-                size, metrics));
+        return pxFromSp(size, metrics, 1f);
+    }
+
+    public static int pxFromSp(float size, DisplayMetrics metrics, float scale) {
+        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                size, metrics) * scale);
     }
 
     public static String createDbSelectionQuery(String columnName, IntArray values) {
@@ -456,6 +531,15 @@
     }
 
     /**
+     * Returns an intent for starting the default home activity
+     */
+    public static Intent createHomeIntent() {
+        return new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+    }
+
+    /**
      * Wraps a message with a TTS span, so that a different message is spoken than
      * what is getting displayed.
      * @param msg original message
@@ -494,21 +578,6 @@
                 LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
     }
 
-    /**
-     * @return {@link SharedPreferences} that backs {@link FeatureFlags}
-     */
-    public static SharedPreferences getFeatureFlagsPrefs(Context context) {
-        // Use application context for shared preferences, so that we use a single cached instance
-        return context.getApplicationContext().getSharedPreferences(
-            FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
-    }
-
-    public static boolean areAnimationsEnabled(Context context) {
-        return ATLEAST_OREO
-                ? ValueAnimator.areAnimatorsEnabled()
-                : !context.getSystemService(PowerManager.class).isPowerSaveMode();
-    }
-
     public static boolean isWallpaperAllowed(Context context) {
         return context.getSystemService(WallpaperManager.class).isSetWallpaperAllowed();
     }
@@ -592,13 +661,23 @@
      */
     public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
             Object[] outObj) {
+        Drawable icon = loadFullDrawableWithoutTheme(launcher, info, width, height, outObj);
+        if (icon instanceof BitmapInfo.Extender) {
+            icon = ((BitmapInfo.Extender) icon).getThemedDrawable(launcher);
+        }
+        return icon;
+    }
+
+    private static Drawable loadFullDrawableWithoutTheme(Launcher launcher, ItemInfo info,
+            int width, int height, Object[] outObj) {
         LauncherAppState appState = LauncherAppState.getInstance(launcher);
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
             LauncherActivityInfo activityInfo = launcher.getSystemService(LauncherApps.class)
                     .resolveActivity(info.getIntent(), info.user);
             outObj[0] = activityInfo;
-            return activityInfo == null ? null : new IconProvider(launcher).getIconForUI(
-                    activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi);
+            return activityInfo == null ? null : LauncherAppState.getInstance(launcher)
+                    .getIconProvider().getIcon(
+                            activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi);
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             if (info instanceof PendingAddShortcutInfo) {
                 ShortcutConfigActivityInfo activityInfo =
@@ -606,7 +685,6 @@
                 outObj[0] = activityInfo;
                 return activityInfo.getFullResIcon(appState.getIconCache());
             }
-            if (info.getIntent() == null || info.getIntent().getPackage() == null) return null;
             List<ShortcutInfo> si = ShortcutKey.fromItemInfo(info)
                     .buildRequest(launcher)
                     .query(ShortcutRequest.ALL);
@@ -663,6 +741,14 @@
         }
     }
 
+    /**
+     * @return true is the extra is either null or is of type {@param type}
+     */
+    public static boolean isValidExtraType(Intent intent, String key, Class type) {
+        Object extra = intent.getParcelableExtra(key);
+        return extra == null || type.isInstance(extra);
+    }
+
     public static float squaredHypot(float x, float y) {
         return x * x + y * y;
     }
@@ -672,6 +758,86 @@
         return slop * slop;
     }
 
+    /**
+     * Helper method to create a content provider
+     */
+    public static ContentObserver newContentObserver(Handler handler, Consumer<Uri> command) {
+        return new ContentObserver(handler) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                command.accept(uri);
+            }
+        };
+    }
+
+    /**
+     * Compares the ratio of two quantities and returns whether that ratio is greater than the
+     * provided bound. Order of quantities does not matter. Bound should be a decimal representation
+     * of a percentage.
+     */
+    public static boolean isRelativePercentDifferenceGreaterThan(float first, float second,
+            float bound) {
+        return (Math.abs(first - second) / Math.abs((first + second) / 2.0f)) > bound;
+    }
+
+    /**
+     * Rotates `inOutBounds` by `delta` 90-degree increments. Rotation is visually CCW. Parent
+     * sizes represent the "space" that will rotate carrying inOutBounds along with it to determine
+     * the final bounds.
+     */
+    public static void rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight,
+            int delta) {
+        int rdelta = ((delta % 4) + 4) % 4;
+        int origLeft = inOutBounds.left;
+        switch (rdelta) {
+            case 0:
+                return;
+            case 1:
+                inOutBounds.left = inOutBounds.top;
+                inOutBounds.top = parentWidth - inOutBounds.right;
+                inOutBounds.right = inOutBounds.bottom;
+                inOutBounds.bottom = parentWidth - origLeft;
+                return;
+            case 2:
+                inOutBounds.left = parentWidth - inOutBounds.right;
+                inOutBounds.right = parentWidth - origLeft;
+                return;
+            case 3:
+                inOutBounds.left = parentHeight - inOutBounds.bottom;
+                inOutBounds.bottom = inOutBounds.right;
+                inOutBounds.right = parentHeight - inOutBounds.top;
+                inOutBounds.top = origLeft;
+                return;
+        }
+    }
+
+    /**
+     * Make a color filter that blends a color into the destination based on a scalable amout.
+     *
+     * @param color to blend in.
+     * @param tintAmount [0-1] 0 no tinting, 1 full color.
+     * @return ColorFilter for tinting, or {@code null} if no filter is needed.
+     */
+    public static ColorFilter makeColorTintingColorFilter(int color, float tintAmount) {
+        if (tintAmount == 0f) {
+            return null;
+        }
+        return new LightingColorFilter(
+                // This isn't blending in white, its making a multiplication mask for the base color
+                ColorUtils.blendARGB(Color.WHITE, 0, tintAmount),
+                ColorUtils.blendARGB(0, color, tintAmount));
+    }
+
+    /**
+     * Sets start margin on the provided {@param view} to be {@param margin}.
+     * Assumes {@param view} is a child of {@link LinearLayout}
+     */
+    public static void setStartMarginForView(View view, int margin) {
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) view.getLayoutParams();
+        lp.setMarginStart(margin);
+        view.setLayoutParams(lp);
+    }
+
     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 a6283ff..5bdc402 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -22,12 +22,12 @@
 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
 import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED;
 import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_INACCESSIBLE;
+import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
 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.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
@@ -63,7 +63,6 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Toast;
 
-import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.anim.Interpolators;
@@ -82,10 +81,10 @@
 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.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
-import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -93,29 +92,37 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.touch.WorkspaceTouchListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.EdgeEffectCompat;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.OverlayEdgeEffect;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.WallpaperOffsetInterpolator;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
+import com.android.launcher3.widget.util.WidgetSizes;
 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 /**
  * The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -141,6 +148,10 @@
 
     public static final int DEFAULT_PAGE = 0;
 
+    private static final int DEFAULT_SMARTSPACE_HEIGHT = 1;
+
+    private static final int EXPANDED_SMARTSPACE_HEIGHT = 2;
+
     private LayoutTransition mLayoutTransition;
     @Thunk final WallpaperManager mWallpaperManager;
 
@@ -180,7 +191,6 @@
     @Thunk final Launcher mLauncher;
     @Thunk DragController mDragController;
 
-    private final Rect mTempRect = new Rect();
     private final int[] mTempXY = new int[2];
     private final float[] mTempFXY = new float[2];
     @Thunk float[] mDragViewVisualCenter = new float[2];
@@ -195,6 +205,7 @@
     private boolean mStripScreensOnPageStopMoving = false;
 
     private DragPreviewProvider mOutlineProvider = null;
+
     private boolean mWorkspaceFadeInAdjacentScreens;
 
     final WallpaperOffsetInterpolator mWallpaperOffset;
@@ -238,10 +249,7 @@
     private float mTransitionProgress;
 
     // State related to Launcher Overlay
-    LauncherOverlay mLauncherOverlay;
-    boolean mScrollInteractionBegan;
-    boolean mStartedSendingScrollEvents;
-    float mLastOverlayScroll = 0;
+    private OverlayEdgeEffect mOverlayEdgeEffect;
     boolean mOverlayShown = false;
     private Runnable mOnOverlayHiddenCallback;
 
@@ -301,8 +309,12 @@
         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 (mWorkspaceFadeInAdjacentScreens) {
+        if (isTwoPanelEnabled()) {
+            setPageSpacing(0); // we have two pages and we don't want any spacing
+        } else if (mWorkspaceFadeInAdjacentScreens) {
             // In landscape mode the page spacing is set to the default.
             setPageSpacing(grid.edgeMarginPx);
         } else {
@@ -314,12 +326,30 @@
             setPageSpacing(Math.max(maxInsets, maxPadding));
         }
 
-
         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--) {
-            mWorkspaceScreens.valueAt(i)
-                    .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
+            int paddingLeft = paddingLeftRight;
+            int paddingRight = paddingLeftRight;
+            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;
+                } else { // middle panel
+                    paddingLeft = 0;
+                    paddingRight = 0;
+                }
+            }
+            mWorkspaceScreens.valueAt(i).setPadding(paddingLeft, 0, paddingRight, paddingBottom);
         }
     }
 
@@ -358,10 +388,19 @@
     }
 
     public float getWallpaperOffsetForCenterPage() {
-        int pageScroll = getScrollForPage(getPageNearestToCenterOfScreen());
+        return getWallpaperOffsetForPage(getPageNearestToCenterOfScreen());
+    }
+
+    private float getWallpaperOffsetForPage(int page) {
+        int pageScroll = getScrollForPage(page);
         return mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
     }
 
+    /** Returns the number of pages used for the wallpaper parallax. */
+    public int getNumPagesForWallpaperParallax() {
+        return mWallpaperOffset.getNumPagesForWallpaperParallax();
+    }
+
     public Rect estimateItemPosition(CellLayout cl, int hCell, int vCell, int hSpan, int vSpan) {
         Rect r = new Rect();
         cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
@@ -375,19 +414,12 @@
         }
 
         if (mDragInfo != null && mDragInfo.cell != null) {
-            CellLayout layout = (CellLayout) mDragInfo.cell.getParent().getParent();
+            CellLayout layout = (CellLayout) (mDragInfo.cell instanceof LauncherAppWidgetHostView
+                    ? dragObject.dragView.getContentViewParent().getParent()
+                    : mDragInfo.cell.getParent().getParent());
             layout.markCellsAsUnoccupiedForView(mDragInfo.cell);
         }
 
-        if (mOutlineProvider != null) {
-            if (dragObject.dragView != null) {
-                Bitmap preview = dragObject.dragView.getPreviewBitmap();
-
-                // The outline is used to visualize where the item will land if dropped
-                mOutlineProvider.generateDragOutline(preview);
-            }
-        }
-
         updateChildrenLayersEnabled();
 
         // Do not add a new page if it is a accessible drag which was not started by the workspace.
@@ -396,10 +428,9 @@
         // When a accessible drag is started by the folder, we only allow rearranging withing the
         // folder.
         boolean addNewPage = !(options.isAccessibleDrag && dragObject.dragSource != this);
-
         if (addNewPage) {
             mDeferRemoveExtraEmptyScreen = false;
-            addExtraEmptyScreenOnDrag();
+            addExtraEmptyScreenOnDrag(dragObject);
 
             if (dragObject.dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
                     && dragObject.dragSource != this) {
@@ -408,7 +439,7 @@
                 // widgets as they cannot be placed inside a folder.
                 // Start at the current page and search right (on LTR) until finding a page with
                 // enough space. Since an empty screen is the furthest right, a page must be found.
-                int currentPage = getPageNearestToCenterOfScreen();
+                int currentPage = getDestinationPage();
                 for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
                     CellLayout page = (CellLayout) getPageAt(pageIndex);
                     if (page.hasReorderSolution(dragObject.dragInfo)) {
@@ -426,6 +457,15 @@
                 .log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
     }
 
+    private boolean isTwoPanelEnabled() {
+        return mLauncher.mDeviceProfile.isTwoPanels;
+    }
+
+    @Override
+    protected int getPanelCount() {
+        return isTwoPanelEnabled() ? 2 : super.getPanelCount();
+    }
+
     public void deferRemoveExtraEmptyScreen() {
         mDeferRemoveExtraEmptyScreen = true;
     }
@@ -436,11 +476,20 @@
             enforceDragParity("onDragEnd", 0, 0);
         }
 
-        if (!mDeferRemoveExtraEmptyScreen) {
-            removeExtraEmptyScreen(mDragSourceInternal != null);
-        }
-
         updateChildrenLayersEnabled();
+        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
+        stateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
+            @Override
+            public void onStateTransitionComplete(LauncherState finalState) {
+                if (finalState == NORMAL) {
+                    if (!mDeferRemoveExtraEmptyScreen) {
+                        removeExtraEmptyScreen(true /* stripEmptyScreens */);
+                    }
+                    stateManager.removeStateListener(this);
+                }
+            }
+        });
+
         mDragInfo = null;
         mOutlineProvider = null;
         mDragSourceInternal = null;
@@ -513,7 +562,10 @@
                     .inflate(R.layout.search_container_workspace, firstPage, false);
         }
 
-        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
+        int cellVSpan = FeatureFlags.EXPANDED_SMARTSPACE.get()
+                ? EXPANDED_SMARTSPACE_HEIGHT : DEFAULT_SMARTSPACE_HEIGHT;
+        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)) {
             Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
@@ -580,15 +632,24 @@
         addView(newScreen, insertIndex);
         mStateTransitionAnimation.applyChildState(
                 mLauncher.getStateManager().getState(), newScreen, insertIndex);
+
+        updatePageScrollValues();
         return newScreen;
     }
 
-    public void addExtraEmptyScreenOnDrag() {
+    private void addExtraEmptyScreenOnDrag(DragObject dragObject) {
         boolean lastChildOnScreen = false;
         boolean childOnFinalScreen = false;
 
         if (mDragSourceInternal != null) {
-            if (mDragSourceInternal.getChildCount() == 1) {
+            // 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 (dragSourceChildCount == 1) {
                 lastChildOnScreen = true;
             }
             CellLayout cl = (CellLayout) mDragSourceInternal.getParent();
@@ -657,6 +718,7 @@
         convertFinalScreenToEmptyScreenIfNecessary();
         if (hasExtraEmptyScreen()) {
             removeView(mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID));
+            setCurrentPage(getNextPage());
             mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
             mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
 
@@ -800,7 +862,7 @@
 
     private boolean shouldConsumeTouch(View v) {
         return !workspaceIconsCanBeDragged()
-                || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
+                || (!workspaceInModalState() && !isVisible(v));
     }
 
     public boolean isSwitchingState() {
@@ -890,47 +952,25 @@
         }
     }
 
-    protected void onScrollInteractionBegin() {
-        super.onScrollInteractionBegin();
-        mScrollInteractionBegan = true;
-    }
-
-    protected void onScrollInteractionEnd() {
-        super.onScrollInteractionEnd();
-        mScrollInteractionBegan = false;
-        if (mStartedSendingScrollEvents) {
-            mStartedSendingScrollEvents = false;
-            mLauncherOverlay.onScrollInteractionEnd();
-        }
-    }
-
     public void setLauncherOverlay(LauncherOverlay overlay) {
-        mLauncherOverlay = overlay;
-        // A new overlay has been set. Reset event tracking
-        mStartedSendingScrollEvents = false;
+        mOverlayEdgeEffect = overlay == null ? null : new OverlayEdgeEffect(getContext(), overlay);
+        EdgeEffectCompat newEffect = overlay == null
+                ? new EdgeEffectCompat(getContext()) : mOverlayEdgeEffect;
+        if (mIsRtl) {
+            mEdgeGlowRight = newEffect;
+        } else {
+            mEdgeGlowLeft = newEffect;
+        }
         onOverlayScrollChanged(0);
     }
 
     public boolean hasOverlay() {
-        return mLauncherOverlay != null;
-    }
-
-    private boolean isScrollingOverlay() {
-        return mLauncherOverlay != null &&
-                ((mIsRtl && getUnboundedScroll() > mMaxScroll)
-                        || (!mIsRtl && getUnboundedScroll() < mMinScroll));
+        return mOverlayEdgeEffect != null;
     }
 
     @Override
     protected void snapToDestination() {
-        // If we're overscrolling the overlay, we make sure to immediately reset the PagedView
-        // to it's baseline position instead of letting the overscroll settle. The overlay handles
-        // it's own settling, and every gesture to the overlay should be self-contained and start
-        // from 0, so we zero it out here.
-        if (isScrollingOverlay()) {
-            // We reset mWasInOverscroll so that PagedView doesn't zero out the overscroll
-            // interaction when we call snapToPageImmediately.
-            mWasInOverscroll = false;
+        if (mOverlayEdgeEffect != null && !mOverlayEdgeEffect.isFinished()) {
             snapToPageImmediately(0);
         } else {
             super.snapToDestination();
@@ -942,13 +982,17 @@
         super.onScrollChanged(l, t, oldl, oldt);
 
         // Update the page indicator progress.
-        boolean isTransitioning = mIsSwitchingState
+        // Unlike from other states, we show the page indicator when transitioning from HINT_STATE.
+        boolean isSwitchingState = mIsSwitchingState
+                && mLauncher.getStateManager().getCurrentStableState() != HINT_STATE;
+        boolean isTransitioning = isSwitchingState
                 || (getLayoutTransition() != null && getLayoutTransition().isRunning());
         if (!isTransitioning) {
             showPageIndicatorAtCurrentScroll();
         }
 
         updatePageAlphaValues();
+        updatePageScrollValues();
         enableHwLayersOnVisiblePages();
     }
 
@@ -959,38 +1003,6 @@
     }
 
     @Override
-    protected void overScroll(int amount) {
-        boolean shouldScrollOverlay = mLauncherOverlay != null && !mScroller.isSpringing() &&
-                ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
-
-        boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&
-                ((amount >= 0 && !mIsRtl) || (amount <= 0 && mIsRtl));
-
-        if (shouldScrollOverlay) {
-            if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
-                mStartedSendingScrollEvents = true;
-                mLauncherOverlay.onScrollInteractionBegin();
-            }
-
-            mLastOverlayScroll = Math.abs(((float) amount) / getMeasuredWidth());
-            mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
-        } else {
-            dampedOverScroll(amount);
-        }
-
-        if (shouldZeroOverlay) {
-            mLauncherOverlay.onScrollChange(0, mIsRtl);
-        }
-    }
-
-    @Override
-    protected boolean onOverscroll(int amount) {
-        // Enforce overscroll on -1 direction
-        if ((amount > 0 && !mIsRtl) || (amount < 0 && mIsRtl)) return false;
-        return super.onOverscroll(amount);
-    }
-
-    @Override
     protected boolean shouldFlingForVelocity(int velocityX) {
         // When the overlay is moving, the fling or settle transition is controlled by the overlay.
         return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 &&
@@ -1003,8 +1015,6 @@
     public void onOverlayScrollChanged(float scroll) {
         if (Float.compare(scroll, 1f) == 0) {
             if (!mOverlayShown) {
-                mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
-                        Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
                 mLauncher.getStatsLogManager().logger()
                         .withSrcState(LAUNCHER_STATE_HOME)
                         .withDstState(LAUNCHER_STATE_HOME)
@@ -1019,20 +1029,16 @@
             // Not announcing the overlay page for accessibility since it announces itself.
         } else if (Float.compare(scroll, 0f) == 0) {
             if (mOverlayShown) {
-                UserEventDispatcher ued = mLauncher.getUserEventDispatcher();
-                if (!ued.isPreviousHomeGesture()) {
-                    mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
-                        Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
-                    mLauncher.getStatsLogManager().logger()
-                            .withSrcState(LAUNCHER_STATE_HOME)
-                            .withDstState(LAUNCHER_STATE_HOME)
-                            .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
-                                    .setWorkspace(
-                                            LauncherAtom.WorkspaceContainer.newBuilder()
-                                                    .setPageIndex(-1))
-                                    .build())
-                            .log(LAUNCHER_SWIPERIGHT);
-                }
+                // TODO: this is logged unnecessarily on home gesture.
+                mLauncher.getStatsLogManager().logger()
+                        .withSrcState(LAUNCHER_STATE_HOME)
+                        .withDstState(LAUNCHER_STATE_HOME)
+                        .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+                                .setWorkspace(
+                                        LauncherAtom.WorkspaceContainer.newBuilder()
+                                                .setPageIndex(-1))
+                                .build())
+                        .log(LAUNCHER_SWIPERIGHT);
             } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
                 // When arriving to 0 overscroll from non-zero overscroll, announce page for
                 // accessibility since default announcements were disabled while in overscroll
@@ -1123,12 +1129,8 @@
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
         if (prevPage != mCurrentPage) {
-            int swipeDirection = (prevPage < mCurrentPage)
-                    ? Action.Direction.RIGHT : Action.Direction.LEFT;
             StatsLogManager.EventEnum event = (prevPage < mCurrentPage)
                     ? LAUNCHER_SWIPERIGHT : LAUNCHER_SWIPELEFT;
-            mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
-                    swipeDirection, ContainerType.WORKSPACE, prevPage);
             mLauncher.getStatsLogManager().logger()
                     .withSrcState(LAUNCHER_STATE_HOME)
                     .withDstState(LAUNCHER_STATE_HOME)
@@ -1170,17 +1172,6 @@
         mWallpaperOffset.syncWithScroll();
     }
 
-    public void computeScrollWithoutInvalidation() {
-        computeScrollHelper(false);
-    }
-
-    @Override
-    protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
-        if (!isSwitchingState()) {
-            super.determineScrollingStart(ev, touchSlopScale);
-        }
-    }
-
     @Override
     public void announceForAccessibility(CharSequence text) {
         // Don't announce if apps is on top of us.
@@ -1212,6 +1203,17 @@
         }
     }
 
+    private void updatePageScrollValues() {
+        int screenCenter = getScrollX() + getMeasuredWidth() / 2;
+        for (int i = 0; i < getChildCount(); i++) {
+            CellLayout child = (CellLayout) getChildAt(i);
+            if (child != null) {
+                float scrollProgress = getScrollProgress(screenCenter, child, i);
+                child.setScrollProgress(scrollProgress);
+            }
+        }
+    }
+
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mWallpaperOffset.setWindowToken(getWindowToken());
@@ -1324,10 +1326,6 @@
         mOutlineProvider = outlineProvider;
     }
 
-    public void snapToPageFromOverView(int whichPage) {
-        snapToPage(whichPage, OVERVIEW.getTransitionDuration(mLauncher), Interpolators.ZOOM_IN);
-    }
-
     private void onStartStateTransition(LauncherState state) {
         mIsSwitchingState = true;
         mTransitionProgress = 0;
@@ -1402,7 +1400,7 @@
             // TAPL can work only if UIDevice is set up as setCompressedLayoutHeirarchy(false).
             // Hiding workspace from the tests when it's
             // IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS.
-            return null;
+            return AccessibilityNodeInfo.obtain();
         }
         return super.createAccessibilityNodeInfo();
     }
@@ -1475,10 +1473,19 @@
             draggableView = (DraggableView) child;
         }
 
-        // The drag bitmap follows the touch point around on the screen
-        final Bitmap b = previewProvider.createDragBitmap();
+        final View contentView = previewProvider.getContentView();
+        final float scale;
+        // The draggable drawable follows the touch point around on the screen
+        final Drawable drawable;
+        if (contentView == null) {
+            drawable = previewProvider.createDrawable();
+            scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
+        } else {
+            drawable = null;
+            scale = previewProvider.getScaleAndPosition(contentView, mTempXY);
+        }
+
         int halfPadding = previewProvider.previewPadding / 2;
-        float scale = previewProvider.getScaleAndPosition(b, mTempXY);
         int dragLayerX = mTempXY[0];
         int dragLayerY = mTempXY[1];
 
@@ -1501,14 +1508,40 @@
                     .showForIcon((BubbleTextView) child);
             if (popupContainer != null) {
                 dragOptions.preDragCondition = popupContainer.createPreDragCondition();
-                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
             }
         }
 
-        DragView dv = mDragController.startDrag(b, draggableView, dragLayerX, dragLayerY, source,
-                dragObject, dragVisualizeOffset, dragRect, scale * iconScale,
-                scale, dragOptions);
-        dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor);
+        final DragView dv;
+        if (contentView instanceof View) {
+            if (contentView instanceof LauncherAppWidgetHostView) {
+                mDragController.addDragListener(new AppWidgetHostViewDragListener(mLauncher));
+            }
+            dv = mDragController.startDrag(
+                    contentView,
+                    draggableView,
+                    dragLayerX,
+                    dragLayerY,
+                    source,
+                    dragObject,
+                    dragVisualizeOffset,
+                    dragRect,
+                    scale * iconScale,
+                    scale,
+                    dragOptions);
+        } else {
+            dv = mDragController.startDrag(
+                    drawable,
+                    draggableView,
+                    dragLayerX,
+                    dragLayerY,
+                    source,
+                    dragObject,
+                    dragVisualizeOffset,
+                    dragRect,
+                    scale * iconScale,
+                    scale,
+                    dragOptions);
+        }
         return dv;
     }
 
@@ -1701,7 +1734,6 @@
                 fi.addItem(destInfo);
                 fi.addItem(sourceInfo);
             }
-            mLauncher.folderCreatedFromItem(fi.getFolder(), destInfo);
             return true;
         }
         return false;
@@ -1719,9 +1751,8 @@
             FolderIcon fi = (FolderIcon) dropOverView;
             if (fi.acceptDrop(d.dragInfo)) {
                 mStatsLogManager.logger().withItemInfo(fi.mInfo).withInstanceId(d.logInstanceId)
-                        .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
+                        .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED_ON_FOLDER_ICON);
                 fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
-
                 // if the drag started here, we need to remove it from the workspace
                 if (!external) {
                     getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
@@ -1755,8 +1786,11 @@
             onDropExternal(touchXY, dropTargetLayout, d);
         } else {
             final View cell = mDragInfo.cell;
+            final DragView dragView = d.dragView;
             boolean droppedOnOriginalCellDuringTransition = false;
-            Runnable onCompleteRunnable = null;
+            Runnable onCompleteRunnable = dragView::resumeColorExtraction;
+
+            dragView.disableColorExtraction();
 
             if (dropTargetLayout != null && !d.cancelled) {
                 // Move internally
@@ -1824,7 +1858,7 @@
                     item.spanX = resultSpan[0];
                     item.spanY = resultSpan[1];
                     AppWidgetHostView awhv = (AppWidgetHostView) cell;
-                    AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
+                    WidgetSizes.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0],
                             resultSpan[1]);
                 }
 
@@ -1840,6 +1874,8 @@
                         CellLayout parentCell = getParentCellLayoutForView(cell);
                         if (parentCell != null) {
                             parentCell.removeView(cell);
+                        } else if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {
+                            d.dragView.detachContentView(/* reattachToPreviousParent= */ false);
                         } else if (FeatureFlags.IS_STUDIO_BUILD) {
                             throw new NullPointerException("mDragInfo.cell has null parent");
                         }
@@ -1865,20 +1901,24 @@
                         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);
                                 }
                             };
                         }
                     }
-
                     mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
                             lp.cellX, lp.cellY, item.spanX, item.spanY);
                 } else {
                     if (!returnToOriginalCellToPreventShuffling) {
                         onNoCellFound(dropTargetLayout);
                     }
+                    if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {
+                        d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
+                    }
 
                     // If we can't find a drop location, we return the item to its original position
                     CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
@@ -1887,6 +1927,11 @@
                     CellLayout layout = (CellLayout) cell.getParent().getParent();
                     layout.markCellsAsOccupiedForView(cell);
                 }
+            } else {
+                // When drag is cancelled, reattach content view back to its original parent.
+                if (mDragInfo.cell instanceof LauncherAppWidgetHostView) {
+                    d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
+                }
             }
 
             final CellLayout parent = (CellLayout) cell.getParent().getParent();
@@ -1894,10 +1939,16 @@
                 if (droppedOnOriginalCellDuringTransition) {
                     // Animate the item to its original position, while simultaneously exiting
                     // spring-loaded mode so the page meets the icon where it was picked up.
+                    final RunnableList callbackList = new RunnableList();
+                    final Runnable onCompleteCallback = onCompleteRunnable;
                     mLauncher.getDragController().animateDragViewToOriginalPosition(
-                            onCompleteRunnable, cell,
+                            /* onComplete= */ callbackList::executeAllAndDestroy, cell,
                             SPRING_LOADED.getTransitionDuration(mLauncher));
-                    mLauncher.getStateManager().goToState(NORMAL);
+                    mLauncher.getStateManager().goToState(NORMAL, /* delay= */ 0,
+                            onCompleteCallback == null
+                                    ? null
+                                    : forSuccessCallback(
+                                            () -> callbackList.add(onCompleteCallback)));
                     mLauncher.getDropTargetBar().onDragEnd();
                     parent.onDropChild(cell);
                     return;
@@ -1920,8 +1971,8 @@
             }
             parent.onDropChild(cell);
 
-            mLauncher.getStateManager().goToState(
-                    NORMAL, SPRING_LOADED_EXIT_DELAY, onCompleteRunnable);
+            mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY,
+                    forSuccessCallback(onCompleteRunnable));
             mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId)
                     .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
         }
@@ -1938,16 +1989,26 @@
     }
 
     /**
-     * Computes the area relative to dragLayer which is used to display a page.
+     * Computes and returns the area relative to dragLayer which is used to display a page.
+     * In case we have multiple pages displayed at the same time, we return the union of the areas.
      */
-    public void getPageAreaRelativeToDragLayer(Rect outArea) {
-        CellLayout child = (CellLayout) getChildAt(getNextPage());
-        if (child == null) {
-            return;
+    public Rect getPageAreaRelativeToDragLayer() {
+        Rect area = new Rect();
+        int nextPage = getNextPage();
+        int panelCount = getPanelCount();
+        for (int page = nextPage; page < nextPage + panelCount; page++) {
+            CellLayout child = (CellLayout) getChildAt(page);
+            if (child == null) {
+                break;
+            }
+
+            ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
+            Rect tmpRect = new Rect();
+            mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, tmpRect);
+            area.union(tmpRect);
         }
 
-        ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
-        mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, outArea);
+        return area;
     }
 
     @Override
@@ -2026,9 +2087,6 @@
         if (mDragOverlappingLayout != null) {
             mDragOverlappingLayout.setIsDragOverlapping(true);
         }
-        // Invalidating the scrim will also force this CellLayout
-        // to be invalidated so that it is highlighted if necessary.
-        mLauncher.getDragLayer().getScrim().invalidate();
     }
 
     public CellLayout getCurrentDragOverlappingLayout() {
@@ -2087,40 +2145,40 @@
         mLastReorderY = -1;
     }
 
-   /*
-    *
-    * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
-    * coordinate space. The argument xy is modified with the return result.
-    */
-   private void mapPointFromSelfToChild(View v, float[] xy) {
-       xy[0] = xy[0] - v.getLeft();
-       xy[1] = xy[1] - v.getTop();
-   }
+    /*
+     *
+     * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
+     * coordinate space. The argument xy is modified with the return result.
+     */
+    private void mapPointFromSelfToChild(View v, float[] xy) {
+        xy[0] = xy[0] - v.getLeft();
+        xy[1] = xy[1] - v.getTop();
+    }
 
-   boolean isPointInSelfOverHotseat(int x, int y) {
-       mTempFXY[0] = x;
-       mTempFXY[1] = y;
-       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
-       View hotseat = mLauncher.getHotseat();
-       return mTempFXY[0] >= hotseat.getLeft() &&
-               mTempFXY[0] <= hotseat.getRight() &&
-               mTempFXY[1] >= hotseat.getTop() &&
-               mTempFXY[1] <= hotseat.getBottom();
-   }
+    boolean isPointInSelfOverHotseat(int x, int y) {
+        mTempFXY[0] = x;
+        mTempFXY[1] = y;
+        mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
+        View hotseat = mLauncher.getHotseat();
+        return mTempFXY[0] >= hotseat.getLeft()
+                && mTempFXY[0] <= hotseat.getRight()
+                && mTempFXY[1] >= hotseat.getTop()
+                && mTempFXY[1] <= hotseat.getBottom();
+    }
 
     /**
      * Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
      * @param layout either hotseat of a page in workspace
      * @param xy the point location in workspace co-ordinate space
      */
-   private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
-       if (mLauncher.isHotseatLayout(layout)) {
-           mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
-           mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
-       } else {
-           mapPointFromSelfToChild(layout, xy);
-       }
-   }
+    private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
+        if (mLauncher.isHotseatLayout(layout)) {
+            mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
+            mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
+        } else {
+            mapPointFromSelfToChild(layout, xy);
+        }
+    }
 
     private boolean isDragWidget(DragObject d) {
         return (d.dragInfo instanceof LauncherAppWidgetInfo ||
@@ -2182,8 +2240,8 @@
                     item.spanY, child, mTargetCell);
 
             if (!nearestDropOccupied) {
-                mDragTargetLayout.visualizeDropLocation(d.originalView, mOutlineProvider,
-                        mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
+                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)) {
@@ -2232,19 +2290,27 @@
 
         int nextPage = getNextPage();
         if (layout == null && !isPageInTransition()) {
-            // Check if the item is dragged over left page
+            // 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);
         }
 
         if (layout == null && !isPageInTransition()) {
-            // Check if the item is dragged over right page
+            // 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);
         }
 
+        // 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 (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
             layout = (CellLayout) getChildAt(nextPage);
@@ -2365,8 +2431,8 @@
             }
 
             boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
-            mDragTargetLayout.visualizeDropLocation(dragObject.originalView, mOutlineProvider,
-                mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
+            mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1],
+                    resultSpan[0], resultSpan[1], dragObject);
         }
     }
 
@@ -2402,9 +2468,9 @@
             spanY = mDragInfo.spanY;
         }
 
-        final int container = mLauncher.isHotseatLayout(cellLayout) ?
-                LauncherSettings.Favorites.CONTAINER_HOTSEAT :
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP;
+        final int container = mLauncher.isHotseatLayout(cellLayout)
+                ? LauncherSettings.Favorites.CONTAINER_HOTSEAT
+                : LauncherSettings.Favorites.CONTAINER_DESKTOP;
         final int screenId = getIdForScreen(cellLayout);
         if (!mLauncher.isHotseatLayout(cellLayout)
                 && screenId != getScreenIdForPageIndex(mCurrentPage)
@@ -2452,7 +2518,7 @@
             Runnable onAnimationCompleteRunnable = new Runnable() {
                 @Override
                 public void run() {
-                    // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
+                    // Normally removeExtraEmptyScreen is called in Workspace#onDrop, but when
                     // adding an item that may not be dropped right away (due to a config activity)
                     // we defer the removal until the activity returns.
                     deferRemoveExtraEmptyScreen();
@@ -2473,8 +2539,7 @@
                     ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
 
             if (finalView != null && updateWidgetSize) {
-                AppWidgetResizeFrame.updateWidgetSizeRanges(finalView, mLauncher, item.spanX,
-                        item.spanY);
+                WidgetSizes.updateWidgetSizeRanges(finalView, mLauncher, item.spanX, item.spanY);
             }
 
             int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR;
@@ -2558,7 +2623,7 @@
 
     }
 
-    public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
+    private Drawable createWidgetDrawable(ItemInfo widgetInfo, View layout) {
         int[] unScaledSize = estimateItemSize(widgetInfo);
         int visibility = layout.getVisibility();
         layout.setVisibility(VISIBLE);
@@ -2570,7 +2635,7 @@
         Bitmap b = BitmapRenderer.createHardwareBitmap(
                 unScaledSize[0], unScaledSize[1], layout::draw);
         layout.setVisibility(visibility);
-        return b;
+        return new FastBitmapDrawable(b);
     }
 
     private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
@@ -2639,10 +2704,11 @@
 
         boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
                 info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
-        if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
-            Bitmap crossFadeBitmap = createWidgetBitmap(info, finalView);
-            dragView.setCrossFadeBitmap(crossFadeBitmap);
-            dragView.crossFade((int) (duration * 0.8f));
+        if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external)
+                && finalView != null
+                && dragView.getContentView() != finalView) {
+            Drawable crossFadeDrawable = createWidgetDrawable(info, finalView);
+            dragView.crossFadeContent(crossFadeDrawable, (int) (duration * 0.8f));
         } else if (isWidget && external) {
             scaleXY[0] = scaleXY[1] = Math.min(scaleXY[0],  scaleXY[1]);
         }
@@ -2730,6 +2796,10 @@
                 removeWorkspaceItem(mDragInfo.cell);
             }
         } else if (mDragInfo != null) {
+            // When drag is cancelled, reattach content view back to its original parent.
+            if (mDragInfo.cell instanceof LauncherAppWidgetHostView && d.dragView != null) {
+                d.dragView.detachContentView(/* reattachToPreviousParent= */ true);
+            }
             final CellLayout cellLayout = mLauncher.getCellLayout(
                     mDragInfo.container, mDragInfo.screenId);
             if (cellLayout != null) {
@@ -2890,18 +2960,31 @@
      * 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(String packageName, UserHandle user) {
-        final int curPage = getCurrentPage();
-        final CellLayout currentPage = (CellLayout) getPageAt(curPage);
-        final Workspace.ItemOperator packageAndUser = (ItemInfo info, View view) -> info != null
-                && info.getTargetComponent() != null
-                && TextUtils.equals(info.getTargetComponent().getPackageName(), packageName)
-                && info.user.equals(user);
+    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) ->
-                packageAndUser.evaluate(info, view) && info.itemType == ITEM_TYPE_APPLICATION;
+                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;
@@ -2914,15 +2997,18 @@
             return false;
         };
 
-        // Order: App icons, app in folder. Items in hotseat get returned first.
+        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(new CellLayout[] { getHotseat(), currentPage },
+            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(new CellLayout[] { getHotseat(), currentPage },
-                    packageAndUserAndApp);
+            return getFirstMatch(cellLayouts, preferredItem, packageAndUserAndApp);
         }
     }
 
@@ -2952,34 +3038,17 @@
     }
 
     /**
+     * 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.
-     * @return
      */
-    private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
-        // This array is filled with the first match for each operator.
-        final View[] matches = new View[operators.length];
-        // For efficiency, the outer loop should be CellLayout.
-        for (CellLayout cellLayout : cellLayouts) {
-            mapOverCellLayout(cellLayout, (info, v) -> {
-                for (int i = 0; i < operators.length; ++i) {
-                    if (matches[i] == null && operators[i].evaluate(info, v)) {
-                        matches[i] = v;
-                        if (i == 0) {
-                            // We can return since this is the best match possible.
-                            return true;
-                        }
-                    }
+    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 false;
-            });
-            if (matches[0] != null) {
-                break;
-            }
-        }
-        for (View match : matches) {
-            if (match != null) {
-                return match;
             }
         }
         return null;
@@ -3004,38 +3073,27 @@
      * shortcuts are not removed.
      */
     public void removeItemsByMatcher(final ItemInfoMatcher matcher) {
-        for (final CellLayout layoutParent: getWorkspaceAndHotseatCellLayouts()) {
-            final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
+        for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
+            ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
+            // Iterate in reverse order as we are removing items
+            for (int i = container.getChildCount() - 1; i >= 0; i--) {
+                View child = container.getChildAt(i);
+                ItemInfo info = (ItemInfo) child.getTag();
 
-            IntSparseArrayMap<View> idToViewMap = new IntSparseArrayMap<>();
-            ArrayList<ItemInfo> items = new ArrayList<>();
-            for (int j = 0; j < layout.getChildCount(); j++) {
-                final View view = layout.getChildAt(j);
-                if (view.getTag() instanceof ItemInfo) {
-                    ItemInfo item = (ItemInfo) view.getTag();
-                    items.add(item);
-                    idToViewMap.put(item.id, view);
-                }
-            }
-
-            for (ItemInfo itemToRemove : matcher.filterItemInfos(items)) {
-                View child = idToViewMap.get(itemToRemove.id);
-
-                if (child != null) {
-                    // Note: We can not remove the view directly from CellLayoutChildren as this
-                    // does not re-mark the spaces as unoccupied.
-                    layoutParent.removeViewInLayout(child);
+                if (matcher.matchesInfo(info)) {
+                    layout.removeViewInLayout(child);
                     if (child instanceof DropTarget) {
                         mDragController.removeDropTarget((DropTarget) child);
                     }
-                } else if (itemToRemove.container >= 0) {
-                    // The item may belong to a folder.
-                    View parent = idToViewMap.get(itemToRemove.container);
-                    if (parent instanceof FolderIcon) {
-                        FolderInfo folderInfo = (FolderInfo) parent.getTag();
-                        folderInfo.remove((WorkspaceItemInfo) itemToRemove, false);
-                        if (((FolderIcon) parent).getFolder().isOpen()) {
-                            ((FolderIcon) parent).getFolder().close(false /* animate */);
+                } else if (child instanceof FolderIcon) {
+                    FolderInfo folderInfo = (FolderInfo) info;
+                    List<WorkspaceItemInfo> matches = folderInfo.contents.stream()
+                            .filter(matcher::matchesInfo)
+                            .collect(Collectors.toList());
+                    if (!matches.isEmpty()) {
+                        folderInfo.removeAll(matches, false);
+                        if (((FolderIcon) child).getFolder().isOpen()) {
+                            ((FolderIcon) child).getFolder().close(false /* animate */);
                         }
                     }
                 }
@@ -3064,16 +3122,16 @@
      */
     public void mapOverItems(ItemOperator op) {
         for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
-            if (mapOverCellLayout(layout, op)) {
+            if (mapOverCellLayout(layout, op) != null) {
                 return;
             }
         }
     }
 
-    private boolean mapOverCellLayout(CellLayout layout, ItemOperator op) {
+    private View mapOverCellLayout(CellLayout layout, ItemOperator op) {
         // TODO(b/128460496) Potential race condition where layout is not yet loaded
         if (layout == null) {
-            return false;
+            return null;
         }
         ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
         // map over all the shortcuts on the workspace
@@ -3081,13 +3139,13 @@
         for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
             View item = container.getChildAt(itemIdx);
             if (op.evaluate((ItemInfo) item.getTag(), item)) {
-                return true;
+                return item;
             }
         }
-        return false;
+        return null;
     }
 
-    void updateShortcuts(ArrayList<WorkspaceItemInfo> shortcuts) {
+    void updateShortcuts(List<WorkspaceItemInfo> shortcuts) {
         final HashSet<WorkspaceItemInfo> updates = new HashSet<>(shortcuts);
         ItemOperator op = (info, v) -> {
             if (v instanceof BubbleTextView && updates.contains(info)) {
@@ -3145,9 +3203,8 @@
     }
 
     public void removeAbandonedPromise(String packageName, UserHandle user) {
-        HashSet<String> packages = new HashSet<>(1);
-        packages.add(packageName);
-        ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packages, user);
+        ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(
+                Collections.singleton(packageName), user);
         mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
         removeItemsByMatcher(matcher);
     }
@@ -3156,7 +3213,7 @@
         ItemOperator op = (info, v) -> {
             if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
                     && updates.contains(info)) {
-                ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
+                ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
             } else if (v instanceof PendingAppWidgetHostView
                     && info instanceof LauncherAppWidgetInfo
                     && updates.contains(info)) {
@@ -3226,6 +3283,18 @@
         }
     }
 
+    /**
+     * Set the given view's pivot point to match the workspace's, so that it scales together. Since
+     * both this view and workspace can move, transform the point manually instead of using
+     * dragLayer.getDescendantCoordRelativeToSelf and related methods.
+     */
+    public void setPivotToScaleWithSelf(View sibling) {
+        sibling.setPivotY(getPivotY() + getTop()
+                - sibling.getTop() - sibling.getTranslationY());
+        sibling.setPivotX(getPivotX() + getLeft()
+                - sibling.getLeft() - sibling.getTranslationX());
+    }
+
     @Override
     public int getExpectedHeight() {
         return getMeasuredHeight() <= 0 || !mIsLayoutValid
@@ -3262,29 +3331,11 @@
         }
         if (nScreens == 0) {
             // When the workspace is not loaded, we do not know how many screen will be bound.
-            return getContext().getString(R.string.all_apps_home_button_label);
+            return getContext().getString(R.string.home_screen);
         }
         return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
     }
 
-    @Override
-    public void fillInLogContainerData(ItemInfo childInfo, Target child,
-            ArrayList<Target> parents) {
-        if (childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
-                || childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
-            getHotseat().fillInLogContainerData(childInfo, child, parents);
-            return;
-        } else if (childInfo.container >= 0) {
-            FolderIcon icon = (FolderIcon) getHomescreenIconByItemId(childInfo.container);
-            icon.getFolder().fillInLogContainerData(childInfo, child, parents);
-            return;
-        }
-        child.gridX = childInfo.cellX;
-        child.gridY = childInfo.cellY;
-        child.pageIndex = getCurrentPage();
-        parents.add(newContainerTarget(ContainerType.WORKSPACE));
-    }
-
     /**
      * Used as a workaround to ensure that the AppWidgetService receives the
      * PACKAGE_ADDED broadcast before updating widgets.
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index c3d4aeb..d6302ce 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -28,7 +28,7 @@
 
     String TAG = "Launcher.Workspace";
 
-    // The screen id used for the empty screen always present to the right.
+    // The screen id used for the empty screen always present at the end.
     int EXTRA_EMPTY_SCREEN_ID = -201;
     // The is the first screen. It is always present, even if its empty.
     int FIRST_SCREEN_ID = 0;
@@ -130,12 +130,16 @@
         }
 
         child.setHapticFeedbackEnabled(false);
-        child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+        child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
         if (child instanceof DropTarget) {
             onAddDropTarget((DropTarget) child);
         }
     }
 
+    default View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
+        return ItemLongClickListener.INSTANCE_WORKSPACE;
+    }
+
     Hotseat getHotseat();
 
     CellLayout getScreenWithId(int screenId);
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index cd938e1..1b9647a 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,26 +18,28 @@
 
 import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE;
 
-import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.LauncherState.FLAG_HAS_SYS_UI_SCRIM;
-import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_HAS_BACKGROUNDS;
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.WORKSPACE_PAGE_INDICATOR;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
-import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS;
+import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 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.states.StateAnimationConfig.SKIP_SCRIM;
 
 import android.animation.ValueAnimator;
 import android.view.View;
@@ -45,11 +47,12 @@
 
 import com.android.launcher3.LauncherState.PageAlphaProvider;
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
+import com.android.launcher3.graphics.Scrim;
+import com.android.launcher3.graphics.SysUiScrim;
+import com.android.launcher3.states.SpringLoadedState;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.util.DynamicResource;
 import com.android.systemui.plugins.ResourceProvider;
@@ -93,7 +96,6 @@
         ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
         ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(
                 mLauncher);
-        ScaleAndTranslation qsbScaleAndTranslation = state.getQsbScaleAndTranslation(mLauncher);
         mNewScale = scaleAndTranslation.scale;
         PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
         final int childCount = mWorkspace.getChildCount();
@@ -105,54 +107,39 @@
         int elements = state.getVisibleElements(mLauncher);
         Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
                 pageAlphaProvider.interpolator);
-        boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
         Hotseat hotseat = mWorkspace.getHotseat();
-        // Since we set the pivot relative to mWorkspace, we need to scale a sibling of Workspace.
-        AllAppsContainerView qsbScaleView = mLauncher.getAppsView();
-        View qsbView = qsbScaleView.getSearchView();
-        if (playAtomicComponent) {
-            Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
-            LauncherState fromState = mLauncher.getStateManager().getState();
-            boolean shouldSpring = propertySetter instanceof PendingAnimation
-                    && fromState == HINT_STATE && state == NORMAL;
-            if (shouldSpring) {
-                ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
-                        mWorkspace, mNewScale));
-            } else {
-                propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
-            }
+        Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
+        LauncherState fromState = mLauncher.getStateManager().getState();
 
-            setPivotToScaleWithWorkspace(hotseat);
-            setPivotToScaleWithWorkspace(qsbScaleView);
-            float hotseatScale = hotseatScaleAndTranslation.scale;
-            if (shouldSpring) {
-                PendingAnimation pa = (PendingAnimation) propertySetter;
-                pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
-                pa.add(getSpringScaleAnimator(mLauncher, qsbScaleView,
-                        qsbScaleAndTranslation.scale));
-            } else {
-                Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
-                        scaleInterpolator);
-                propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
-                        hotseatScaleInterpolator);
-                propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
-                        hotseatScaleInterpolator);
-            }
-
-            float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
-            propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
-            propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
-                    hotseatIconsAlpha, fadeInterpolator);
+        boolean shouldSpring = propertySetter instanceof PendingAnimation
+                && fromState == HINT_STATE && state == NORMAL;
+        if (shouldSpring) {
+            ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher,
+                    mWorkspace, mNewScale));
+        } else {
+            propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
         }
 
-        if (config.onlyPlayAtomicComponent()) {
-            // Only the alpha and scale, handled above, are included in the atomic animation.
-            return;
+        mWorkspace.setPivotToScaleWithSelf(hotseat);
+        float hotseatScale = hotseatScaleAndTranslation.scale;
+        if (shouldSpring) {
+            PendingAnimation pa = (PendingAnimation) propertySetter;
+            pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
+        } else {
+            Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
+                    scaleInterpolator);
+            propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
+                    hotseatScaleInterpolator);
         }
 
-        Interpolator translationInterpolator = !playAtomicComponent
-                ? LINEAR
-                : config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
+        float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
+        propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
+        float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
+        propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
+                workspacePageIndicatorAlpha, fadeInterpolator);
+
+        Interpolator translationInterpolator =
+                config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
         propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_X,
                 scaleAndTranslation.translationX, translationInterpolator);
         propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_Y,
@@ -164,30 +151,25 @@
                 hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
         propertySetter.setFloat(mWorkspace.getPageIndicator(), VIEW_TRANSLATE_Y,
                 hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
-        propertySetter.setFloat(qsbView, VIEW_TRANSLATE_Y,
-                qsbScaleAndTranslation.translationY, hotseatTranslationInterpolator);
 
-        setScrim(propertySetter, state);
+        if (!config.hasAnimationFlag(SKIP_SCRIM)) {
+            setScrim(propertySetter, state, config);
+        }
     }
 
-    /**
-     * Set the given view's pivot point to match the workspace's, so that it scales together. Since
-     * both this view and workspace can move, transform the point manually instead of using
-     * dragLayer.getDescendantCoordRelativeToSelf and related methods.
-     */
-    private void setPivotToScaleWithWorkspace(View sibling) {
-        sibling.setPivotY(mWorkspace.getPivotY() + mWorkspace.getTop()
-                - sibling.getTop() - sibling.getTranslationY());
-        sibling.setPivotX(mWorkspace.getPivotX() + mWorkspace.getLeft()
-                - sibling.getLeft() - sibling.getTranslationX());
-    }
+    public void setScrim(PropertySetter propertySetter, LauncherState state,
+            StateAnimationConfig config) {
+        Scrim workspaceDragScrim = mLauncher.getDragLayer().getWorkspaceDragScrim();
+        propertySetter.setFloat(workspaceDragScrim, SCRIM_PROGRESS,
+                state.getWorkspaceBackgroundAlpha(mLauncher), LINEAR);
 
-    public void setScrim(PropertySetter propertySetter, LauncherState state) {
-        WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
-        propertySetter.setFloat(scrim, SCRIM_PROGRESS, state.getWorkspaceScrimAlpha(mLauncher),
-                LINEAR);
-        propertySetter.setFloat(scrim, SYSUI_PROGRESS,
+        SysUiScrim sysUiScrim = mLauncher.getRootView().getSysUiScrim();
+        propertySetter.setFloat(sysUiScrim, SYSUI_PROGRESS,
                 state.hasFlag(FLAG_HAS_SYS_UI_SCRIM) ? 1 : 0, LINEAR);
+
+        propertySetter.setViewBackgroundColor(mLauncher.getScrimView(),
+                state.getWorkspaceScrimColor(mLauncher),
+                config.getInterpolator(ANIM_SCRIM_FADE, ACCEL_2));
     }
 
     public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
@@ -199,20 +181,14 @@
             PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter,
             StateAnimationConfig config) {
         float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
-        int drawableAlpha = state.hasFlag(FLAG_WORKSPACE_HAS_BACKGROUNDS)
-                ? Math.round(pageAlpha * 255) : 0;
+        float springLoadedProgress = (state instanceof SpringLoadedState) ? 1.0f : 0f;
 
-        if (!config.onlyPlayAtomicComponent()) {
-            // Don't update the scrim during the atomic animation.
-            propertySetter.setInt(cl.getScrimBackground(),
-                    DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
-        }
-        if (config.playAtomicOverviewScaleComponent()) {
-            Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
-                    pageAlphaProvider.interpolator);
-            propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
-                    pageAlpha, fadeInterpolator);
-        }
+        propertySetter.setFloat(cl,
+                CellLayout.SPRING_LOADED_PROGRESS, springLoadedProgress, ZOOM_OUT);
+        Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
+                pageAlphaProvider.interpolator);
+        propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
+                pageAlpha, fadeInterpolator);
     }
 
     /**
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
index ddb547f..d0fc175 100644
--- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -31,6 +31,7 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragLayer;
 
 import java.util.List;
 
@@ -41,30 +42,32 @@
         implements OnClickListener, OnHoverListener {
     protected static final int INVALID_POSITION = -1;
 
-    private static final int[] sTempArray = new int[2];
+    protected final Rect mTempRect = new Rect();
+    protected final int[] mTempCords = new int[2];
 
     protected final CellLayout mView;
     protected final Context mContext;
     protected final LauncherAccessibilityDelegate mDelegate;
-
-    private final Rect mTempRect = new Rect();
+    protected final DragLayer mDragLayer;
 
     public DragAndDropAccessibilityDelegate(CellLayout forView) {
         super(forView);
         mView = forView;
         mContext = mView.getContext();
-        mDelegate = Launcher.getLauncher(mContext).getAccessibilityDelegate();
+        Launcher launcher = Launcher.getLauncher(mContext);
+        mDelegate = launcher.getAccessibilityDelegate();
+        mDragLayer = launcher.getDragLayer();
     }
 
     @Override
-    protected int getVirtualViewAt(float x, float y) {
+    public int getVirtualViewAt(float x, float y) {
         if (x < 0 || y < 0 || x > mView.getMeasuredWidth() || y > mView.getMeasuredHeight()) {
             return INVALID_ID;
         }
-        mView.pointToCellExact((int) x, (int) y, sTempArray);
+        mView.pointToCellExact((int) x, (int) y, mTempCords);
 
         // Map cell to id
-        int id = sTempArray[0] + sTempArray[1] * mView.getCountX();
+        int id = mTempCords[0] + mTempCords[1] * mView.getCountX();
         return intersectsValidDropTarget(id);
     }
 
@@ -75,7 +78,7 @@
     protected abstract int intersectsValidDropTarget(int id);
 
     @Override
-    protected void getVisibleVirtualViews(List<Integer> virtualViews) {
+    public void getVisibleVirtualViews(List<Integer> virtualViews) {
         // We create a virtual view for each cell of the grid
         // The cell ids correspond to cells in reading order.
         int nCells = mView.getCountX() * mView.getCountY();
@@ -88,7 +91,7 @@
     }
 
     @Override
-    protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
+    public boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
         if (action == AccessibilityNodeInfoCompat.ACTION_CLICK && viewId != INVALID_ID) {
             String confirmation = getConfirmationForIconDrop(viewId);
             mDelegate.handleAccessibleDrop(mView, getItemBounds(viewId), confirmation);
@@ -112,13 +115,25 @@
     }
 
     @Override
-    protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
+    public void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
         if (id == INVALID_ID) {
             throw new IllegalArgumentException("Invalid virtual view id");
         }
 
         node.setContentDescription(getLocationDescriptionForIconDrop(id));
-        node.setBoundsInParent(getItemBounds(id));
+
+        Rect itemBounds = getItemBounds(id);
+        node.setBoundsInParent(itemBounds);
+
+        // ExploreByTouchHelper does not currently handle view scale.
+        // Update BoundsInScreen to appropriate value.
+        mTempCords[0] = mTempCords[1] = 0;
+        float scale = mDragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
+        mTempRect.left = mTempCords[0] + (int) (itemBounds.left * scale);
+        mTempRect.right = mTempCords[0] + (int) (itemBounds.right * scale);
+        mTempRect.top = mTempCords[1] + (int) (itemBounds.top * scale);
+        mTempRect.bottom = mTempCords[1] + (int) (itemBounds.bottom * scale);
+        node.setBoundsInScreen(mTempRect);
 
         node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
         node.setClickable(true);
@@ -130,6 +145,13 @@
         return dispatchHoverEvent(motionEvent);
     }
 
+    /**
+     * Returns the target host container
+     */
+    public View getHost() {
+        return mView;
+    }
+
     protected abstract String getLocationDescriptionForIconDrop(int id);
 
     protected abstract String getConfirmationForIconDrop(int id);
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 136d43e..9faac5b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -3,51 +3,58 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
 
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
 
-import android.app.AlertDialog;
 import android.appwidget.AppWidgetProviderInfo;
-import android.content.DialogInterface;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Bundle;
 import android.os.Handler;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
-import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.ButtonDropTarget;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.folder.Folder;
-import com.android.launcher3.keyboard.CustomActionsPopup;
+import com.android.launcher3.keyboard.KeyboardDragAndDropView;
 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.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.views.OptionsPopupView.OptionItem;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.util.WidgetSizes;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 public class LauncherAccessibilityDelegate extends AccessibilityDelegate implements DragListener {
 
@@ -77,93 +84,105 @@
         public View item;
     }
 
-    protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
-    @Thunk final Launcher mLauncher;
+    protected final SparseArray<LauncherAction> mActions = new SparseArray<>();
+    protected final Launcher mLauncher;
 
     private DragInfo mDragInfo = null;
 
     public LauncherAccessibilityDelegate(Launcher launcher) {
         mLauncher = launcher;
 
-        mActions.put(REMOVE, new AccessibilityAction(REMOVE,
-                launcher.getText(R.string.remove_drop_target_label)));
-        mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
-                launcher.getText(R.string.uninstall_drop_target_label)));
-        mActions.put(DISMISS_PREDICTION, new AccessibilityAction(DISMISS_PREDICTION,
-                launcher.getText(R.string.dismiss_prediction_label)));
-        mActions.put(RECONFIGURE, new AccessibilityAction(RECONFIGURE,
-                launcher.getText(R.string.gadget_setup_text)));
-        mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE,
-                launcher.getText(R.string.action_add_to_workspace)));
-        mActions.put(MOVE, new AccessibilityAction(MOVE,
-                launcher.getText(R.string.action_move)));
-        mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE,
-                launcher.getText(R.string.action_move_to_workspace)));
-        mActions.put(RESIZE, new AccessibilityAction(RESIZE,
-                        launcher.getText(R.string.action_resize)));
-        mActions.put(DEEP_SHORTCUTS, new AccessibilityAction(DEEP_SHORTCUTS,
-                launcher.getText(R.string.action_deep_shortcut)));
-        mActions.put(SHORTCUTS_AND_NOTIFICATIONS, new AccessibilityAction(DEEP_SHORTCUTS,
-                launcher.getText(R.string.shortcuts_menu_with_notifications_description)));
-    }
-
-    public void addAccessibilityAction(int action, int actionLabel) {
-        mActions.put(action, new AccessibilityAction(action, mLauncher.getText(actionLabel)));
+        mActions.put(REMOVE, new LauncherAction(
+                REMOVE, R.string.remove_drop_target_label, KeyEvent.KEYCODE_X));
+        mActions.put(UNINSTALL, new LauncherAction(
+                UNINSTALL, R.string.uninstall_drop_target_label, KeyEvent.KEYCODE_U));
+        mActions.put(DISMISS_PREDICTION, new LauncherAction(DISMISS_PREDICTION,
+                R.string.dismiss_prediction_label, KeyEvent.KEYCODE_X));
+        mActions.put(RECONFIGURE, new LauncherAction(
+                RECONFIGURE, R.string.gadget_setup_text, KeyEvent.KEYCODE_E));
+        mActions.put(ADD_TO_WORKSPACE, new LauncherAction(
+                ADD_TO_WORKSPACE, R.string.action_add_to_workspace, KeyEvent.KEYCODE_P));
+        mActions.put(MOVE, new LauncherAction(
+                MOVE, R.string.action_move, KeyEvent.KEYCODE_M));
+        mActions.put(MOVE_TO_WORKSPACE, new LauncherAction(MOVE_TO_WORKSPACE,
+                R.string.action_move_to_workspace, KeyEvent.KEYCODE_P));
+        mActions.put(RESIZE, new LauncherAction(
+                RESIZE, R.string.action_resize, KeyEvent.KEYCODE_R));
+        mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
+                R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
+        mActions.put(SHORTCUTS_AND_NOTIFICATIONS, new LauncherAction(DEEP_SHORTCUTS,
+                R.string.shortcuts_menu_with_notifications_description, KeyEvent.KEYCODE_S));
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(host, info);
-        addSupportedActions(host, info, false);
+        if (host.getTag() instanceof ItemInfo) {
+            ItemInfo item = (ItemInfo) host.getTag();
+
+            List<LauncherAction> actions = new ArrayList<>();
+            getSupportedActions(host, item, actions);
+            actions.forEach(la -> info.addAction(la.accessibilityAction));
+
+            if (!itemSupportsLongClick(host, item)) {
+                info.setLongClickable(false);
+                info.removeAction(AccessibilityAction.ACTION_LONG_CLICK);
+            }
+        }
     }
 
-    public void addSupportedActions(View host, AccessibilityNodeInfo info, boolean fromKeyboard) {
-        if (!(host.getTag() instanceof ItemInfo)) return;
-        ItemInfo item = (ItemInfo) host.getTag();
-
-        if (host instanceof AccessibilityActionHandler) {
-            ((AccessibilityActionHandler) host).addSupportedAccessibilityActions(info);
-        }
-
+    /**
+     * Adds all the accessibility actions that can be handled.
+     */
+    protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
         // If the request came from keyboard, do not add custom shortcuts as that is already
         // exposed as a direct shortcut
-        if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) {
-            info.addAction(mActions.get(NotificationListener.getInstanceIfConnected() != null
+        if (ShortcutUtil.supportsShortcuts(item)) {
+            out.add(mActions.get(NotificationListener.getInstanceIfConnected() != null
                     ? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
         }
 
         for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
             if (target.supportsAccessibilityDrop(item, host)) {
-                info.addAction(mActions.get(target.getAccessibilityAction()));
+                out.add(mActions.get(target.getAccessibilityAction()));
             }
         }
 
         // Do not add move actions for keyboard request as this uses virtual nodes.
-        if (!fromKeyboard && itemSupportsAccessibleDrag(item)) {
-            info.addAction(mActions.get(MOVE));
+        if (itemSupportsAccessibleDrag(item)) {
+            out.add(mActions.get(MOVE));
 
             if (item.container >= 0) {
-                info.addAction(mActions.get(MOVE_TO_WORKSPACE));
+                out.add(mActions.get(MOVE_TO_WORKSPACE));
             } else if (item instanceof LauncherAppWidgetInfo) {
                 if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) {
-                    info.addAction(mActions.get(RESIZE));
+                    out.add(mActions.get(RESIZE));
                 }
             }
         }
 
-        if (!fromKeyboard && !itemSupportsLongClick(host, item)) {
-            info.setLongClickable(false);
-            info.removeAction(AccessibilityAction.ACTION_LONG_CLICK);
-        }
-
         if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
-            info.addAction(mActions.get(ADD_TO_WORKSPACE));
+            out.add(mActions.get(ADD_TO_WORKSPACE));
         }
     }
 
+    /**
+     * Returns all the accessibility actions that can be handled by the host.
+     */
+    public static List<LauncherAction> getSupportedActions(Launcher launcher, View host) {
+        if (host == null || !(host.getTag() instanceof  ItemInfo)) {
+            return Collections.emptyList();
+        }
+        PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher);
+        LauncherAccessibilityDelegate delegate = container != null
+                ? container.getAccessibilityDelegate() : launcher.getAccessibilityDelegate();
+        List<LauncherAction> result = new ArrayList<>();
+        delegate.getSupportedActions(host, (ItemInfo) host.getTag(), result);
+        return result;
+    }
+
     private boolean itemSupportsLongClick(View host, ItemInfo info) {
-        return PopupContainerWithArrow.canShow(host, info)
-                || new CustomActionsPopup(mLauncher, host).canShow();
+        return PopupContainerWithArrow.canShow(host, info);
     }
 
     private boolean itemSupportsAccessibleDrag(ItemInfo item) {
@@ -178,13 +197,17 @@
     @Override
     public boolean performAccessibilityAction(View host, int action, Bundle args) {
         if ((host.getTag() instanceof ItemInfo)
-                && performAction(host, (ItemInfo) host.getTag(), action)) {
+                && performAction(host, (ItemInfo) host.getTag(), action, false)) {
             return true;
         }
         return super.performAccessibilityAction(host, action, args);
     }
 
-    public boolean performAction(final View host, final ItemInfo item, int action) {
+    /**
+     * Performs the provided action on the host
+     */
+    protected boolean performAction(final View host, final ItemInfo item, int action,
+            boolean fromKeyboard) {
         if (action == ACTION_LONG_CLICK) {
             if (PopupContainerWithArrow.canShow(host, item)) {
                 // Long press should be consumed for workspace items, and it should invoke the
@@ -192,46 +215,32 @@
                 // standard long press path does.
                 PopupContainerWithArrow.showForIcon((BubbleTextView) host);
                 return true;
-            } else {
-                CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
-                if (popup.canShow()) {
-                    popup.show();
-                    return true;
-                }
             }
-        }
-        if (host instanceof AccessibilityActionHandler
-                && ((AccessibilityActionHandler) host).performAccessibilityAction(action, item)) {
-            return true;
-        }
-        if (action == MOVE) {
-            beginAccessibleDrag(host, item);
+        } else if (action == MOVE) {
+            return beginAccessibleDrag(host, item, fromKeyboard);
         } else if (action == ADD_TO_WORKSPACE) {
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
-            mLauncher.getStateManager().goToState(NORMAL, true, new Runnable() {
+            mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
+                if (item instanceof AppInfo) {
+                    WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
+                    mLauncher.getModelWriter().addItemToDatabase(info,
+                            Favorites.CONTAINER_DESKTOP,
+                            screenId, coordinates[0], coordinates[1]);
 
-                @Override
-                public void run() {
-                    if (item instanceof AppInfo) {
-                        WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
-                        mLauncher.getModelWriter().addItemToDatabase(info,
-                                Favorites.CONTAINER_DESKTOP,
-                                screenId, coordinates[0], coordinates[1]);
-
-                        ArrayList<ItemInfo> itemList = new ArrayList<>();
-                        itemList.add(info);
-                        mLauncher.bindItems(itemList, true);
-                        announceConfirmation(R.string.item_added_to_workspace);
-                    } else if (item instanceof PendingAddItemInfo) {
-                        PendingAddItemInfo info = (PendingAddItemInfo) item;
-                        Workspace workspace = mLauncher.getWorkspace();
-                        workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
-                        mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
-                                screenId, coordinates, info.spanX, info.spanY);
-                    }
+                    mLauncher.bindItems(
+                            Collections.singletonList(info),
+                            /* forceAnimateIcons= */ true,
+                            /* focusFirstItemForAccessibility= */ true);
+                    announceConfirmation(R.string.item_added_to_workspace);
+                } else if (item instanceof PendingAddItemInfo) {
+                    PendingAddItemInfo info = (PendingAddItemInfo) item;
+                    Workspace workspace = mLauncher.getWorkspace();
+                    workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
+                    mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
+                            screenId, coordinates, info.spanX, info.spanY);
                 }
-            });
+            }));
             return true;
         } else if (action == MOVE_TO_WORKSPACE) {
             Folder folder = Folder.getOpen(mLauncher);
@@ -242,47 +251,31 @@
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
             mLauncher.getModelWriter().moveItemInDatabase(info,
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                    Favorites.CONTAINER_DESKTOP,
                     screenId, coordinates[0], coordinates[1]);
 
             // Bind the item in next frame so that if a new workspace page was created,
             // it will get laid out.
-            new Handler().post(new Runnable() {
-
-                @Override
-                public void run() {
-                    ArrayList<ItemInfo> itemList = new ArrayList<>();
-                    itemList.add(item);
-                    mLauncher.bindItems(itemList, true);
-                    announceConfirmation(R.string.item_moved);
-                }
+            new Handler().post(() -> {
+                mLauncher.bindItems(Collections.singletonList(item), true);
+                announceConfirmation(R.string.item_moved);
             });
+            return true;
         } else if (action == RESIZE) {
             final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
-            final IntArray actions = getSupportedResizeActions(host, info);
-            CharSequence[] labels = new CharSequence[actions.size()];
-            for (int i = 0; i < actions.size(); i++) {
-                labels[i] = mLauncher.getText(actions.get(i));
-            }
-
-            new AlertDialog.Builder(mLauncher)
-                .setTitle(R.string.action_resize)
-                .setItems(labels, new DialogInterface.OnClickListener() {
-
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        performResizeAction(actions.get(which), host, info);
-                        dialog.dismiss();
-                    }
-                })
-                .show();
+            List<OptionItem> actions = getSupportedResizeActions(host, info);
+            Rect pos = new Rect();
+            mLauncher.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
+            ArrowPopup popup = OptionsPopupView.show(mLauncher, new RectF(pos), actions, false);
+            popup.requestFocus();
+            popup.setOnCloseCallback(host::requestFocus);
             return true;
-        } else if (action == DEEP_SHORTCUTS) {
+        } else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
             return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
         } else {
             for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
-                if (dropTarget.supportsAccessibilityDrop(item, host) &&
-                        action == dropTarget.getAccessibilityAction()) {
+                if (dropTarget.supportsAccessibilityDrop(item, host)
+                        && action == dropTarget.getAccessibilityAction()) {
                     dropTarget.onAccessibilityDrop(host, item);
                     return true;
                 }
@@ -291,40 +284,60 @@
         return false;
     }
 
-    private IntArray getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
-        IntArray actions = new IntArray();
-
+    private List<OptionItem> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
+        List<OptionItem> actions = new ArrayList<>();
         AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
         if (providerInfo == null) {
             return actions;
         }
 
-        CellLayout layout = (CellLayout) host.getParent().getParent();
+        CellLayout layout;
+        if (host.getParent() instanceof DragView) {
+            layout = (CellLayout) ((DragView) host.getParent()).getContentViewParent().getParent();
+        } else {
+            layout = (CellLayout) host.getParent().getParent();
+        }
         if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
             if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
                     layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
-                actions.add(R.string.action_increase_width);
+                actions.add(new OptionItem(mLauncher,
+                        R.string.action_increase_width,
+                        R.drawable.ic_widget_width_increase,
+                        IGNORE,
+                        v -> performResizeAction(R.string.action_increase_width, host, info)));
             }
 
             if (info.spanX > info.minSpanX && info.spanX > 1) {
-                actions.add(R.string.action_decrease_width);
+                actions.add(new OptionItem(mLauncher,
+                        R.string.action_decrease_width,
+                        R.drawable.ic_widget_width_decrease,
+                        IGNORE,
+                        v -> performResizeAction(R.string.action_decrease_width, host, info)));
             }
         }
 
         if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
             if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
                     layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
-                actions.add(R.string.action_increase_height);
+                actions.add(new OptionItem(mLauncher,
+                        R.string.action_increase_height,
+                        R.drawable.ic_widget_height_increase,
+                        IGNORE,
+                        v -> performResizeAction(R.string.action_increase_height, host, info)));
             }
 
             if (info.spanY > info.minSpanY && info.spanY > 1) {
-                actions.add(R.string.action_decrease_height);
+                actions.add(new OptionItem(mLauncher,
+                        R.string.action_decrease_height,
+                        R.drawable.ic_widget_height_decrease,
+                        IGNORE,
+                        v -> performResizeAction(R.string.action_decrease_height, host, info)));
             }
         }
         return actions;
     }
 
-    @Thunk void performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
+    private boolean performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
         CellLayout layout = (CellLayout) host.getParent().getParent();
         layout.markCellsAsUnoccupiedForView(host);
@@ -354,13 +367,12 @@
         }
 
         layout.markCellsAsOccupiedForView(host);
-        Rect sizeRange = new Rect();
-        AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange);
-        ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null,
-                sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom);
+        WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
+                info.spanX, info.spanY);
         host.requestLayout();
         mLauncher.getModelWriter().updateItemInDatabase(info);
         announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
+        return true;
     }
 
     @Thunk void announceConfirmation(int resId) {
@@ -406,7 +418,11 @@
         }
     }
 
-    public void beginAccessibleDrag(View item, ItemInfo info) {
+    private boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
+        if (!itemSupportsAccessibleDrag(info)) {
+            return false;
+        }
+
         mDragInfo = new DragInfo();
         mDragInfo.info = info;
         mDragInfo.item = item;
@@ -423,8 +439,17 @@
 
         DragOptions options = new DragOptions();
         options.isAccessibleDrag = true;
+        options.isKeyboardDrag = fromKeyboard;
         options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
-        ItemLongClickListener.beginDrag(item, mLauncher, info, options);
+
+        if (fromKeyboard) {
+            KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mLauncher.getLayoutInflater()
+                    .inflate(R.layout.keyboard_drag_and_drop, mLauncher.getDragLayer(), false);
+            popup.showForIcon(item, info, options);
+        } else {
+            ItemLongClickListener.beginDrag(item, mLauncher, info, options);
+        }
+        return true;
     }
 
     @Override
@@ -475,19 +500,28 @@
         return screenId;
     }
 
-    /**
-     * An interface allowing views to handle their own action.
-     */
-    public interface AccessibilityActionHandler {
+    public class LauncherAction {
+        public final int keyCode;
+        public final AccessibilityAction accessibilityAction;
+
+        private final LauncherAccessibilityDelegate mDelegate;
+
+        public LauncherAction(int id, int labelRes, int keyCode) {
+            this.keyCode = keyCode;
+            accessibilityAction = new AccessibilityAction(id, mLauncher.getString(labelRes));
+            mDelegate = LauncherAccessibilityDelegate.this;
+        }
 
         /**
-         * performs accessibility action and returns true on success
+         * Invokes the action for the provided host
          */
-        boolean performAccessibilityAction(int action, ItemInfo itemInfo);
-
-        /**
-         * adds all the accessibility actions that can be handled.
-         */
-        void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo);
+        public boolean invokeFromKeyboard(View host) {
+            if (host != null && host.getTag() instanceof ItemInfo) {
+                return mDelegate.performAction(
+                        host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
+            } else {
+                return false;
+            }
+        }
     }
 }
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index d4ba11e..f96afa8 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -17,10 +17,10 @@
 package com.android.launcher3.accessibility;
 
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
 
+import android.view.KeyEvent;
 import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
@@ -31,7 +31,8 @@
 import com.android.launcher3.notification.NotificationMainView;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 
-import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Extension of {@link LauncherAccessibilityDelegate} with actions specific to shortcuts in
@@ -43,23 +44,23 @@
 
     public ShortcutMenuAccessibilityDelegate(Launcher launcher) {
         super(launcher);
-        mActions.put(DISMISS_NOTIFICATION, new AccessibilityAction(DISMISS_NOTIFICATION,
-                launcher.getText(R.string.action_dismiss_notification)));
+        mActions.put(DISMISS_NOTIFICATION, new LauncherAction(DISMISS_NOTIFICATION,
+                R.string.action_dismiss_notification, KeyEvent.KEYCODE_X));
     }
 
     @Override
-    public void addSupportedActions(View host, AccessibilityNodeInfo info, boolean fromKeyboard) {
+    protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
         if ((host.getParent() instanceof DeepShortcutView)) {
-            info.addAction(mActions.get(ADD_TO_WORKSPACE));
+            out.add(mActions.get(ADD_TO_WORKSPACE));
         } else if (host instanceof NotificationMainView) {
             if (((NotificationMainView) host).canChildBeDismissed()) {
-                info.addAction(mActions.get(DISMISS_NOTIFICATION));
+                out.add(mActions.get(DISMISS_NOTIFICATION));
             }
         }
     }
 
     @Override
-    public boolean performAction(View host, ItemInfo item, int action) {
+    protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
         if (action == ADD_TO_WORKSPACE) {
             if (!(host.getParent() instanceof DeepShortcutView)) {
                 return false;
@@ -67,21 +68,14 @@
             final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
             final int[] coordinates = new int[2];
             final int screenId = findSpaceOnWorkspace(item, coordinates);
-            Runnable onComplete = new Runnable() {
-                @Override
-                public void run() {
-                    mLauncher.getModelWriter().addItemToDatabase(info,
-                            LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                            screenId, coordinates[0], coordinates[1]);
-                    ArrayList<ItemInfo> itemList = new ArrayList<>();
-                    itemList.add(info);
-                    mLauncher.bindItems(itemList, true);
-                    AbstractFloatingView.closeAllOpenViews(mLauncher);
-                    announceConfirmation(R.string.item_added_to_workspace);
-                }
-            };
-
-            mLauncher.getStateManager().goToState(NORMAL, true, onComplete);
+            mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
+                mLauncher.getModelWriter().addItemToDatabase(info,
+                        LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                        screenId, coordinates[0], coordinates[1]);
+                mLauncher.bindItems(Collections.singletonList(info), true);
+                AbstractFloatingView.closeAllOpenViews(mLauncher);
+                announceConfirmation(R.string.item_added_to_workspace);
+            }));
             return true;
         } else if (action == DISMISS_NOTIFICATION) {
             if (!(host instanceof NotificationMainView)) {
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 65a261d..a331924 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -17,17 +17,12 @@
 package com.android.launcher3.accessibility;
 
 import android.content.Context;
-import android.graphics.Rect;
 import android.text.TextUtils;
 import android.view.View;
 
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
-import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -38,9 +33,6 @@
  */
 public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate {
 
-    private final Rect mTempRect = new Rect();
-    private final int[] mTempCords = new int[2];
-
     public WorkspaceAccessibilityHelper(CellLayout layout) {
         super(layout);
     }
@@ -134,26 +126,6 @@
         }
         return "";
     }
-
-    @Override
-    protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
-        super.onPopulateNodeForVirtualView(id, node);
-
-
-        // ExploreByTouchHelper does not currently handle view scale.
-        // Update BoundsInScreen to appropriate value.
-        DragLayer dragLayer = Launcher.getLauncher(mView.getContext()).getDragLayer();
-        mTempCords[0] = mTempCords[1] = 0;
-        float scale = dragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
-
-        node.getBoundsInParent(mTempRect);
-        mTempRect.left = mTempCords[0] + (int) (mTempRect.left * scale);
-        mTempRect.right = mTempCords[0] + (int) (mTempRect.right * scale);
-        mTempRect.top = mTempCords[1] + (int) (mTempRect.top * scale);
-        mTempRect.bottom = mTempCords[1] + (int) (mTempRect.bottom * scale);
-        node.setBoundsInScreen(mTempRect);
-    }
-
     @Override
     protected String getLocationDescriptionForIconDrop(int id) {
         int x = id % mView.getCountX();
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index c989e7b..d2c71b2 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,23 +15,30 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+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.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcelable;
 import android.os.Process;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -42,7 +49,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
-import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.ColorUtils;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -55,37 +63,46 @@
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.ScrimView;
 import com.android.launcher3.views.SpringRelativeLayout;
-
-import java.util.ArrayList;
+import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
 
 /**
  * The all apps view container.
  */
 public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
-        Insettable, OnDeviceProfileChangeListener {
+        Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener,
+        ScrimView.ScrimDrawingController {
 
-    private static final float FLING_VELOCITY_MULTIPLIER = 135f;
-    // Starts the springs after at least 55% of the animation has passed.
-    private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
-    private static final int ALPHA_CHANNEL_COUNT = 2;
+    private static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
+
+    public static final float PULL_MULTIPLIER = .02f;
+    public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
+
+    private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
     protected final BaseDraggingActivity mLauncher;
     protected final AdapterHolder[] mAH;
     private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
-    private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
+    private final ItemInfoMatcher mWorkMatcher = mPersonalMatcher.negate();
     private final AllAppsStore mAllAppsStore = new AllAppsStore();
 
+    private final RecyclerView.OnScrollListener mScrollListener =
+            new RecyclerView.OnScrollListener() {
+                @Override
+                public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+                    updateHeaderScroll(((AllAppsRecyclerView) recyclerView).getCurrentScrollY());
+                }
+            };
+
     private final Paint mNavBarScrimPaint;
     private int mNavBarScrimHeight = 0;
 
@@ -93,7 +110,8 @@
     private View mSearchContainer;
     private AllAppsPagedView mViewPager;
 
-    private FloatingHeaderView mHeader;
+    protected FloatingHeaderView mHeader;
+    private float mHeaderTop;
     private WorkModeSwitch mWorkModeSwitch;
 
 
@@ -105,9 +123,15 @@
     protected RecyclerViewFastScroller mTouchHandler;
     protected final Point mFastScrollerOffset = new Point();
 
-    private final MultiValueAlpha mMultiValueAlpha;
+    private Rect mInsets = new Rect();
 
-    Rect mInsets = new Rect();
+    private SearchAdapterProvider mSearchAdapterProvider;
+    private WorkAdapterProvider mWorkAdapterProvider;
+    private final int mScrimColor;
+    private final int mHeaderProtectionColor;
+    private final float mHeaderThreshold;
+    private ScrimView mScrimView;
+    private int mHeaderColor;
 
     public AllAppsContainerView(Context context) {
         this(context, null);
@@ -121,12 +145,24 @@
         super(context, attrs, defStyleAttr);
 
         mLauncher = BaseDraggingActivity.fromContext(context);
+
+        mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+        mHeaderThreshold = getResources().getDimensionPixelSize(
+                R.dimen.dynamic_grid_cell_border_spacing);
+        mHeaderProtectionColor = Themes.getAttrColor(context, R.attr.allappsHeaderProtectionColor);
+
         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();
+            }
+        });
         mAH[AdapterHolder.MAIN] = new AdapterHolder(false /* isWork */);
         mAH[AdapterHolder.WORK] = new AdapterHolder(true /* isWork */);
 
@@ -134,12 +170,39 @@
         mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
 
         mAllAppsStore.addUpdateListener(this::onAppsUpdated);
+    }
 
-        addSpringView(R.id.all_apps_header);
-        addSpringView(R.id.apps_list_view);
-        addSpringView(R.id.all_apps_tabs_view_pager);
+    @Override
+    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> sparseArray) {
+        try {
+            // Many slice view id is not properly assigned, and hence throws null
+            // pointer exception in the underneath method. Catching the exception
+            // simply doesn't restore these slice views. This doesn't have any
+            // user visible effect because because we query them again.
+            super.dispatchRestoreInstanceState(sparseArray);
+        } catch (Exception e) {
+            Log.e("AllAppsContainerView", "restoreInstanceState viewId = 0", e);
+        }
 
-        mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
+        Bundle state = (Bundle) sparseArray.get(R.id.work_tab_state_id, null);
+        if (state != null) {
+            int currentPage = state.getInt(BUNDLE_KEY_CURRENT_PAGE, 0);
+            if (currentPage != 0 && mViewPager != null) {
+                mViewPager.setCurrentPage(currentPage);
+                rebindAdapters(true);
+            } else {
+                reset(true);
+            }
+        }
+
+    }
+
+    @Override
+    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
+        super.dispatchSaveInstanceState(container);
+        Bundle state = new Bundle();
+        state.putInt(BUNDLE_KEY_CURRENT_PAGE, getCurrentPage());
+        container.put(R.id.work_tab_state_id, state);
     }
 
     /**
@@ -155,25 +218,14 @@
         return mAllAppsStore;
     }
 
-    public AlphaProperty getAlphaProperty(int index) {
-        return mMultiValueAlpha.getProperty(index);
-    }
-
     public WorkModeSwitch getWorkModeSwitch() {
         return mWorkModeSwitch;
     }
 
-
-    @Override
-    protected void setDampedScrollShift(float shift) {
-        // Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
-        float maxShift = getSearchView().getHeight() / 2f;
-        super.setDampedScrollShift(Utilities.boundToRange(shift, -maxShift, maxShift));
-    }
-
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         for (AdapterHolder holder : mAH) {
+            holder.adapter.setAppsPerRow(dp.numShownAllAppsColumns);
             if (holder.recyclerView != null) {
                 // Remove all views and clear the pool, while keeping the data same. After this
                 // call, all the viewHolders will be recreated.
@@ -191,16 +243,20 @@
                 break;
             }
         }
-        rebindAdapters(hasWorkApps);
-        if (hasWorkApps) {
-            resetWorkProfile();
+        if (!mAH[AdapterHolder.MAIN].appsList.hasFilter()) {
+            rebindAdapters(hasWorkApps);
+            if (hasWorkApps) {
+                resetWorkProfile();
+            }
         }
     }
 
     private void resetWorkProfile() {
-        mWorkModeSwitch.update(!mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED));
-        mAH[AdapterHolder.WORK].setupOverlay();
-        mAH[AdapterHolder.WORK].applyPadding();
+        boolean isEnabled = !mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED);
+        if (mWorkModeSwitch != null) {
+            mWorkModeSwitch.updateCurrentState(isEnabled);
+        }
+        mWorkAdapterProvider.updateCurrentState(isEnabled);
     }
 
     /**
@@ -298,6 +354,7 @@
         }
         // Reset the search bar and base recycler view after transitioning home
         mSearchUiManager.resetSearch();
+        updateHeaderScroll(0);
     }
 
     @Override
@@ -317,7 +374,7 @@
 
         mSearchContainer = findViewById(R.id.search_container_all_apps);
         mSearchUiManager = (SearchUiManager) mSearchContainer;
-        mSearchUiManager.initialize(this);
+        mSearchUiManager.initializeSearch(this);
     }
 
     public SearchUiManager getSearchUiManager() {
@@ -335,13 +392,6 @@
     }
 
     @Override
-    public void fillInLogContainerData(ItemInfo childInfo, Target child,
-            ArrayList<Target> parents) {
-        parents.add(newContainerTarget(
-                getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
-    }
-
-    @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
         DeviceProfile grid = mLauncher.getDeviceProfile();
@@ -352,7 +402,6 @@
             mAH[i].padding.bottom = insets.bottom;
             mAH[i].padding.left = mAH[i].padding.right = leftRightPadding;
             mAH[i].applyPadding();
-            mAH[i].setupOverlay();
         }
 
         ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
@@ -372,7 +421,8 @@
     @Override
     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
         if (Utilities.ATLEAST_Q) {
-            mNavBarScrimHeight = insets.getTappableElementInsets().bottom;
+            mNavBarScrimHeight = insets.getTappableElementInsets().bottom
+                    - mLauncher.getDeviceProfile().nonOverlappingTaskbarInset;
         } else {
             mNavBarScrimHeight = insets.getStableInsetBottom();
         }
@@ -389,12 +439,6 @@
         }
     }
 
-    @Override
-    public int getCanvasClipTopForOverscroll() {
-        // Do not clip if the QSB is attached to the spring, otherwise the QSB will get clipped.
-        return mSpringViews.get(getSearchView().getId()) ? 0 : mHeader.getTop();
-    }
-
     private void rebindAdapters(boolean showTabs) {
         rebindAdapters(showTabs, false /* force */);
     }
@@ -413,19 +457,26 @@
             setupWorkToggle();
             mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
             mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
+            mAH[AdapterHolder.WORK].recyclerView.setId(R.id.apps_list_view_work);
             mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.MAIN);
             findViewById(R.id.tab_personal)
-                    .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.MAIN));
+                    .setOnClickListener((View view) -> {
+                        if (mViewPager.snapToPage(AdapterHolder.MAIN)) {
+                            mLauncher.getStatsLogManager().logger()
+                                    .log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
+                        }
+                    });
             findViewById(R.id.tab_work)
-                    .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
-            onTabChanged(mViewPager.getNextPage());
+                    .setOnClickListener((View view) -> {
+                        if (mViewPager.snapToPage(AdapterHolder.WORK)) {
+                            mLauncher.getStatsLogManager().logger()
+                                    .log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
+                        }
+                    });
+            onActivePageChanged(mViewPager.getNextPage());
         } else {
             mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
             mAH[AdapterHolder.WORK].recyclerView = null;
-            if (mWorkModeSwitch != null) {
-                ((ViewGroup) mWorkModeSwitch.getParent()).removeView(mWorkModeSwitch);
-                mWorkModeSwitch = null;
-            }
         }
         setupHeader();
 
@@ -436,22 +487,16 @@
     private void setupWorkToggle() {
         if (Utilities.ATLEAST_P) {
             mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
-                    R.layout.work_mode_switch, this, false);
+                    R.layout.work_mode_fab, this, false);
             this.addView(mWorkModeSwitch);
             mWorkModeSwitch.setInsets(mInsets);
-            mWorkModeSwitch.post(() -> mAH[AdapterHolder.WORK].applyPadding());
+            mWorkModeSwitch.post(() -> {
+                mAH[AdapterHolder.WORK].applyPadding();
+                resetWorkProfile();
+            });
         }
     }
 
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        View overlay = mAH[AdapterHolder.WORK].getOverlayView();
-        int v = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? GONE : VISIBLE;
-        overlay.findViewById(R.id.work_apps_paused_title).setVisibility(v);
-        overlay.findViewById(R.id.work_apps_paused_content).setVisibility(v);
-    }
-
     private void replaceRVContainer(boolean showTabs) {
         for (int i = 0; i < mAH.length; i++) {
             if (mAH[i].recyclerView != null) {
@@ -464,10 +509,14 @@
         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().setContainerView(this);
+            mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
         } else {
             mViewPager = null;
         }
@@ -477,16 +526,25 @@
         return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
     }
 
-    public void onTabChanged(int pos) {
-        mHeader.setMainActive(pos == 0);
-        if (mAH[pos].recyclerView != null) {
-            mAH[pos].recyclerView.bindFastScrollbar();
+    @Override
+    public void onActivePageChanged(int currentActivePage) {
+        mHeader.setMainActive(currentActivePage == AdapterHolder.MAIN);
+        if (mAH[currentActivePage].recyclerView != null) {
+            mAH[currentActivePage].recyclerView.bindFastScrollbar();
         }
         reset(true /* animate */);
         if (mWorkModeSwitch != null) {
-            mWorkModeSwitch.setWorkTabVisible(pos == AdapterHolder.WORK
+            mWorkModeSwitch.setWorkTabVisible(currentActivePage == AdapterHolder.WORK
                     && mAllAppsStore.hasModelFlag(
-                            FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
+                    FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
+
+            if (currentActivePage == AdapterHolder.WORK) {
+                if (mWorkModeSwitch.getParent() == null) {
+                    addView(mWorkModeSwitch);
+                }
+            } else {
+                removeView(mWorkModeSwitch);
+            }
         }
     }
 
@@ -500,7 +558,7 @@
         return view.getGlobalVisibleRect(new Rect());
     }
 
-    // Used by tests only
+    @VisibleForTesting
     public boolean isPersonalTabVisible() {
         return isDescendantViewVisible(R.id.tab_personal);
     }
@@ -526,6 +584,22 @@
         return mViewPager == null ? getActiveRecyclerView() : mViewPager;
     }
 
+    public int getCurrentPage() {
+        return mViewPager != null ? mViewPager.getCurrentPage() : AdapterHolder.MAIN;
+    }
+
+    /**
+     * Handles selection on focused view and returns success
+     */
+    public boolean launchHighlightedItem() {
+        if (mSearchAdapterProvider == null) return false;
+        return mSearchAdapterProvider.launchHighlightedItem();
+    }
+
+    public SearchAdapterProvider getSearchAdapterProvider() {
+        return mSearchAdapterProvider;
+    }
+
     public RecyclerViewFastScroller getScrollBar() {
         AllAppsRecyclerView rv = getActiveRecyclerView();
         return rv == null ? null : rv.getScrollbar();
@@ -540,6 +614,7 @@
             mAH[i].padding.top = padding;
             mAH[i].applyPadding();
         }
+        mHeaderTop = mHeader.getTop();
     }
 
     public void setLastSearchQuery(String query) {
@@ -550,6 +625,7 @@
             mSearchModeWhileUsingTabs = true;
             rebindAdapters(false); // hide tabs
         }
+        mHeader.setCollapsed(true);
     }
 
     public void onClearSearchResult() {
@@ -586,31 +662,61 @@
     /**
      * Adds an update listener to {@param animator} that adds springs to the animation.
      */
-    public void addSpringFromFlingUpdateListener(ValueAnimator animator, float velocity) {
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            boolean shouldSpring = true;
-
+    public void addSpringFromFlingUpdateListener(ValueAnimator animator,
+            float velocity /* release velocity */,
+            float progress /* portion of the distance to travel*/) {
+        animator.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationUpdate(ValueAnimator valueAnimator) {
-                if (shouldSpring
-                        && valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
-                    int searchViewId = getSearchView().getId();
-                    addSpringView(searchViewId);
-                    finishWithShiftAndVelocity(1, velocity * FLING_VELOCITY_MULTIPLIER,
-                            (anim, canceled, value, velocity) -> removeSpringView(searchViewId));
-
-                    shouldSpring = false;
-                }
+            public void onAnimationStart(Animator animator) {
+                float distance = (float) ((1 - progress) * getHeight()); // px
+                float settleVelocity = Math.min(0, distance
+                        / (AllAppsTransitionController.INTERP_COEFF * animator.getDuration())
+                        + velocity);
+                absorbSwipeUpVelocity(Math.max(1000, Math.abs(
+                        Math.round(settleVelocity * FLING_VELOCITY_MULTIPLIER))));
             }
         });
     }
 
+    public void onPull(float deltaDistance, float displacement) {
+        absorbPullDeltaDistance(PULL_MULTIPLIER * deltaDistance, PULL_MULTIPLIER * displacement);
+        // Current motion spec is to actually push and not pull
+        // on this surface. However, until EdgeEffect.onPush (b/190612804) is
+        // implemented at view level, we will simply pull
+    }
+
     @Override
     public void getDrawingRect(Rect outRect) {
         super.getDrawingRect(outRect);
         outRect.offset(0, (int) getTranslationY());
     }
 
+    @Override
+    public void setTranslationY(float translationY) {
+        super.setTranslationY(translationY);
+        invalidateHeader();
+    }
+
+    public void setScrimView(ScrimView scrimView) {
+        mScrimView = scrimView;
+    }
+
+    @Override
+    public void drawOnScrim(Canvas canvas) {
+        mHeaderPaint.setColor(mHeaderColor);
+        mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
+        if (mHeaderPaint.getColor() != mScrimColor && mHeaderPaint.getColor() != 0) {
+            int bottom = mUsingTabs && mHeader.mHeaderCollapsed ? mHeader.getVisibleBottomBound()
+                    : mSearchContainer.getBottom();
+            canvas.drawRect(0, 0, canvas.getWidth(), bottom + getTranslationY(),
+                    mHeaderPaint);
+
+            if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && getTranslationY() == 0) {
+                mSearchUiManager.getEditText().setBackground(null);
+            }
+        }
+    }
+
     public class AdapterHolder {
         public static final int MAIN = 0;
         public static final int WORK = 1;
@@ -629,8 +735,15 @@
 
         AdapterHolder(boolean isWork) {
             mIsWork = isWork;
-            appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
-            adapter = new AllAppsGridAdapter(mLauncher, getLayoutInflater(), appsList);
+            appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore,
+                    isWork ? mWorkAdapterProvider : null);
+
+            BaseAdapterProvider[] adapterProviders =
+                    isWork ? new BaseAdapterProvider[]{mSearchAdapterProvider, mWorkAdapterProvider}
+                            : new BaseAdapterProvider[]{mSearchAdapterProvider};
+
+            adapter = new AllAppsGridAdapter(mLauncher, getLayoutInflater(), appsList,
+                    adapterProviders);
             appsList.setAdapter(adapter);
             layoutManager = adapter.getLayoutManager();
         }
@@ -646,38 +759,15 @@
             recyclerView.setHasFixedSize(true);
             // No animations will occur when changes occur to the items in this RecyclerView.
             recyclerView.setItemAnimator(null);
+            recyclerView.addOnScrollListener(mScrollListener);
             FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(recyclerView);
             recyclerView.addItemDecoration(focusedItemDecorator);
             adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
             applyVerticalFadingEdgeEnabled(verticalFadingEdge);
             applyPadding();
-            setupOverlay();
-        }
-
-        void setupOverlay() {
-            if (!mIsWork || recyclerView == null) return;
-            boolean workDisabled = mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED);
-            if (mWorkDisabled == workDisabled) return;
-            recyclerView.setContentDescription(workDisabled ? mLauncher.getString(
-                    R.string.work_apps_paused_content_description) : null);
-            View overlayView = getOverlayView();
-            recyclerView.setItemAnimator(new DefaultItemAnimator());
-            if (workDisabled) {
-                overlayView.setAlpha(0);
-                recyclerView.addAutoSizedOverlay(overlayView);
-                overlayView.animate().alpha(1).withEndAction(
-                        () -> {
-                            appsList.updateItemFilter((info, cn) -> false);
-                            recyclerView.setItemAnimator(null);
-                        }).start();
-            } else if (mInfoMatcher != null) {
-                appsList.updateItemFilter(mInfoMatcher);
-                overlayView.animate().alpha(0).withEndAction(() -> {
-                    recyclerView.setItemAnimator(null);
-                    recyclerView.clearAutoSizedOverlays();
-                }).start();
+            if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+                recyclerView.addItemDecoration(mSearchAdapterProvider.getDecorator());
             }
-            mWorkDisabled = workDisabled;
         }
 
         void applyPadding() {
@@ -698,12 +788,31 @@
             mAH[AdapterHolder.MAIN].recyclerView.setVerticalFadingEdgeEnabled(!mUsingTabs
                     && verticalFadingEdge);
         }
+    }
 
-        private View getOverlayView() {
-            if (mOverlay == null) {
-                mOverlay = mLauncher.getLayoutInflater().inflate(R.layout.work_apps_paused, null);
+
+    protected void updateHeaderScroll(int scrolledOffset) {
+        float prog = Math.max(0, Math.min(1, (float) scrolledOffset / mHeaderThreshold));
+        int viewBG = ColorUtils.blendARGB(mScrimColor, mHeaderProtectionColor, prog);
+        int headerColor = ColorUtils.setAlphaComponent(viewBG,
+                (int) (getSearchView().getAlpha() * 255));
+        if (headerColor != mHeaderColor) {
+            mHeaderColor = headerColor;
+            getSearchView().setBackgroundColor(viewBG);
+            getFloatingHeaderView().setHeaderColor(viewBG);
+            invalidateHeader();
+            if (scrolledOffset == 0 && mSearchUiManager.getEditText() != null) {
+                mSearchUiManager.getEditText().show();
             }
-            return mOverlay;
+        }
+    }
+
+    /**
+     * redraws header protection
+     */
+    public void invalidateHeader() {
+        if (mScrimView != null && FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+            mScrimView.invalidate();
         }
     }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 8ec4d27..874fe80 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -30,6 +30,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.view.accessibility.AccessibilityEventCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -40,17 +41,17 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
-import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
-import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.util.PackageManagerHelper;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
  * The grid view adapter of all the apps.
  */
-public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
+public class AllAppsGridAdapter extends
+        RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
 
     public static final String TAG = "AppsGridAdapter";
 
@@ -71,6 +72,9 @@
     public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
     public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
 
+
+    private final BaseAdapterProvider[] mAdapterProviders;
+
     /**
      * ViewHolder for each icon.
      */
@@ -82,6 +86,80 @@
     }
 
     /**
+     * Info about a particular adapter item (can be either section or app)
+     */
+    public static class AdapterItem {
+        /** Common properties */
+        // The index of this adapter item in the list
+        public int position;
+        // 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
+        // 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 index of this app not including sections
+        public int appIndex = -1;
+        // Search section associated to result
+        public DecorationInfo decorationInfo = null;
+
+        /**
+         * Factory method for AppIcon AdapterItem
+         */
+        public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
+                int appIndex) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = VIEW_TYPE_ICON;
+            item.position = pos;
+            item.sectionName = sectionName;
+            item.appInfo = appInfo;
+            item.appIndex = appIndex;
+            return item;
+        }
+
+        /**
+         * Factory method for empty search results view
+         */
+        public static AdapterItem asEmptySearch(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = VIEW_TYPE_EMPTY_SEARCH;
+            item.position = pos;
+            return item;
+        }
+
+        /**
+         * Factory method for a dividerView in AllAppsSearch
+         */
+        public static AdapterItem asAllAppsDivider(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
+            item.position = pos;
+            return item;
+        }
+
+        /**
+         * Factory method for a market search button
+         */
+        public static AdapterItem asMarketSearch(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = VIEW_TYPE_SEARCH_MARKET;
+            item.position = pos;
+            return item;
+        }
+
+        protected boolean isCountedForAccessibility() {
+            return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET;
+        }
+    }
+
+    /**
      * A subclass of GridLayoutManager that overrides accessibility values during app search.
      */
     public class AppsGridLayoutManager extends GridLayoutManager {
@@ -161,11 +239,18 @@
 
         @Override
         public int getSpanSize(int position) {
-            if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
-                return 1;
+            int viewType = mApps.getAdapterItems().get(position).viewType;
+            int totalSpans = mGridLayoutMgr.getSpanCount();
+            if (isIconViewType(viewType)) {
+                return totalSpans / mAppsPerRow;
             } else {
+                BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
+                if (adapterProvider != null) {
+                    return totalSpans / adapterProvider.getItemsPerRow(viewType, mAppsPerRow);
+                }
+
                 // Section breaks span the full width
-                return mAppsPerRow;
+                return totalSpans;
             }
         }
     }
@@ -189,7 +274,7 @@
     private Intent mMarketSearchIntent;
 
     public AllAppsGridAdapter(BaseDraggingActivity launcher, LayoutInflater inflater,
-            AlphabeticalAppsList apps) {
+            AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) {
         Resources res = launcher.getResources();
         mLauncher = launcher;
         mApps = apps;
@@ -201,12 +286,21 @@
 
         mOnIconClickListener = launcher.getItemOnClickListener();
 
-        setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
+        mAdapterProviders = adapterProviders;
+        setAppsPerRow(mLauncher.getDeviceProfile().numShownAllAppsColumns);
     }
 
     public void setAppsPerRow(int appsPerRow) {
         mAppsPerRow = appsPerRow;
-        mGridLayoutMgr.setSpanCount(mAppsPerRow);
+        int totalSpans = mAppsPerRow;
+        for (BaseAdapterProvider adapterProvider : mAdapterProviders) {
+            for (int itemPerRow : adapterProvider.getSupportedItemsPerRowArray()) {
+                if (totalSpans % itemPerRow != 0) {
+                    totalSpans *= itemPerRow;
+                }
+            }
+        }
+        mGridLayoutMgr.setSpanCount(totalSpans);
     }
 
     /**
@@ -255,11 +349,10 @@
             case VIEW_TYPE_ICON:
                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                         R.layout.all_apps_icon, parent, false);
-                icon.setOnClickListener(mOnIconClickListener);
-                icon.setOnLongClickListener(mOnIconLongClickListener);
                 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;
                 return new ViewHolder(icon);
@@ -270,12 +363,16 @@
                 View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
                         parent, false);
                 searchMarketView.setOnClickListener(v -> mLauncher.startActivitySafely(
-                        v, mMarketSearchIntent, null, AppLaunchTracker.CONTAINER_SEARCH));
+                        v, mMarketSearchIntent, null));
                 return new ViewHolder(searchMarketView);
             case VIEW_TYPE_ALL_APPS_DIVIDER:
                 return new ViewHolder(mLayoutInflater.inflate(
                         R.layout.all_apps_divider, parent, false));
             default:
+                BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
+                if (adapterProvider != null) {
+                    return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
+                }
                 throw new RuntimeException("Unexpected view type");
         }
     }
@@ -284,7 +381,8 @@
     public void onBindViewHolder(ViewHolder holder, int position) {
         switch (holder.getItemViewType()) {
             case VIEW_TYPE_ICON:
-                AppInfo info = mApps.getAdapterItems().get(position).appInfo;
+                AdapterItem adapterItem = mApps.getAdapterItems().get(position);
+                AppInfo info = adapterItem.appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.itemView;
                 icon.reset();
                 icon.applyFromApplicationInfo(info);
@@ -306,10 +404,20 @@
             case VIEW_TYPE_ALL_APPS_DIVIDER:
                 // nothing to do
                 break;
+            default:
+                BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType());
+                if (adapterProvider != null) {
+                    adapterProvider.onBindView(holder, position);
+                }
         }
     }
 
     @Override
+    public void onViewRecycled(@NonNull ViewHolder holder) {
+        super.onViewRecycled(holder);
+    }
+
+    @Override
     public boolean onFailedToRecycleView(ViewHolder holder) {
         // Always recycle and we will reset the view when it is bound
         return true;
@@ -322,8 +430,14 @@
 
     @Override
     public int getItemViewType(int position) {
-        AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
+        AdapterItem item = mApps.getAdapterItems().get(position);
         return item.viewType;
     }
 
+    @Nullable
+    private BaseAdapterProvider getAdapterProvider(int viewType) {
+        return Arrays.stream(mAdapterProviders).filter(
+                adapterProvider -> adapterProvider.isViewSupported(viewType)).findFirst().orElse(
+                null);
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
index f640c3e..3cc9ce6 100644
--- a/src/com/android/launcher3/allapps/AllAppsPagedView.java
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -15,19 +15,23 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SWIPE_TO_PERSONAL_TAB;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SWIPE_TO_WORK_TAB;
+
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.PagedView;
+import com.android.launcher3.workprofile.PersonalWorkPagedView;
 
-public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
+/**
+ *  A {@link PagedView} for showing different views for the personal and work profile respectively
+ *  in the {@link AllAppsContainerView}.
+ */
+public class AllAppsPagedView extends PersonalWorkPagedView {
 
-  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;
-
-  public AllAppsPagedView(Context context) {
+    public AllAppsPagedView(Context context) {
         this(context, null);
     }
 
@@ -40,50 +44,14 @@
     }
 
     @Override
-    protected String getCurrentPageDescription() {
-        // Not necessary, tab-bar already has two tabs with their own descriptions.
-        return "";
-    }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        mPageIndicator.setScroll(l, mMaxScroll);
-    }
-
-    @Override
-    protected void determineScrollingStart(MotionEvent ev) {
-        float absDeltaX = Math.abs(ev.getX() - getDownMotionX());
-        float absDeltaY = Math.abs(ev.getY() - getDownMotionY());
-
-        if (Float.compare(absDeltaX, 0f) == 0) return;
-
-        float slope = absDeltaY / absDeltaX;
-        float theta = (float) Math.atan(slope);
-
-        if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
-            cancelCurrentPageLongPress();
+    protected boolean snapToPageWithVelocity(int whichPage, int velocity) {
+        boolean resp = super.snapToPageWithVelocity(whichPage, velocity);
+        if (resp && whichPage != mCurrentPage) {
+            Launcher.getLauncher(getContext()).getStatsLogManager().logger()
+                    .log(mCurrentPage < whichPage
+                            ? LAUNCHER_ALLAPPS_SWIPE_TO_WORK_TAB
+                            : LAUNCHER_ALLAPPS_SWIPE_TO_PERSONAL_TAB);
         }
-
-        if (theta > MAX_SWIPE_ANGLE) {
-            return;
-        } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
-            theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
-            float extraRatio = (float)
-                    Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
-            super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
-        } else {
-            super.determineScrollingStart(ev);
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    @Override
-    protected boolean canScroll(float absVScroll, float absHScroll) {
-        return (absHScroll > absVScroll) && super.canScroll(absVScroll, absHScroll);
+        return resp;
     }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index cbf02b7..2c84a3d 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -19,13 +19,16 @@
 import static android.view.View.MeasureSpec.UNSPECIFIED;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+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.UiThreadHelper.hideKeyboardAsync;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.SparseIntArray;
 import android.view.MotionEvent;
 import android.view.View;
@@ -37,11 +40,9 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
 import java.util.ArrayList;
@@ -50,7 +51,9 @@
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
-public class AllAppsRecyclerView extends BaseRecyclerView implements LogContainerProvider {
+public class AllAppsRecyclerView extends BaseRecyclerView {
+    private static final String TAG = "AllAppsContainerView";
+    private static final boolean DEBUG = false;
 
     private AlphabeticalAppsList mApps;
     private final int mNumAppsPerRow;
@@ -106,29 +109,13 @@
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
         pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows
+                * (mNumAppsPerRow + 1));
 
         mViewHeights.clear();
         mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
     }
 
-    /**
-     * Scrolls this recycler view to the top.
-     */
-    public void scrollToTop() {
-        // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
-        if (mScrollbar != null) {
-            mScrollbar.reattachThumbToScroll();
-        }
-        if (getLayoutManager() instanceof AppsGridLayoutManager) {
-            AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) getLayoutManager();
-            if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
-                // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
-                return;
-            }
-        }
-        scrollToPosition(0);
-    }
 
     @Override
     public void onDraw(Canvas c) {
@@ -136,7 +123,9 @@
         if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
             mEmptySearchBackground.draw(c);
         }
-
+        if (DEBUG) {
+            Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
+        }
         super.onDraw(c);
     }
 
@@ -175,18 +164,11 @@
         mAutoSizedOverlays.clear();
     }
 
-    @Override
-    public void fillInLogContainerData(ItemInfo childInfo, Target child,
-            ArrayList<Target> parents) {
-        parents.add(newContainerTarget(
-                getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
-    }
-
     public void onSearchResultsChanged() {
         // Always scroll the view to the top so the user can see the changed results
         scrollToTop();
 
-        if (mApps.hasNoFilteredResults()) {
+        if (mApps.hasNoFilteredResults() && !FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
             if (mEmptySearchBackground == null) {
                 mEmptySearchBackground = new AllAppsBackgroundDrawable(getContext());
                 mEmptySearchBackground.setAlpha(0);
@@ -202,12 +184,32 @@
     }
 
     @Override
+    public void onScrollStateChanged(int state) {
+        super.onScrollStateChanged(state);
+
+        StatsLogManager mgr = BaseDraggingActivity.fromContext(getContext()).getStatsLogManager();
+        switch (state) {
+            case SCROLL_STATE_DRAGGING:
+                requestFocus();
+                mgr.logger().sendToInteractionJankMonitor(
+                        LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
+                break;
+            case SCROLL_STATE_IDLE:
+                mgr.logger().sendToInteractionJankMonitor(
+                        LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END, this);
+                break;
+        }
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent e) {
         boolean result = super.onInterceptTouchEvent(e);
         if (!result && e.getAction() == MotionEvent.ACTION_DOWN
                 && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
             mEmptySearchBackground.setHotspot(e.getX(), e.getY());
         }
+        hideKeyboardAsync(ActivityContext.lookupContext(getContext()),
+                getApplicationWindowToken());
         return result;
     }
 
@@ -277,7 +279,7 @@
         if (mApps == null) {
             return;
         }
-        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+        List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
 
         // Skip early if there are no items or we haven't been measured
         if (items.isEmpty() || mNumAppsPerRow == 0) {
@@ -351,7 +353,7 @@
     @Override
     public int getCurrentScrollY() {
         // Return early if there are no items or we haven't been measured
-        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+        List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
         if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
             return -1;
         }
@@ -367,14 +369,14 @@
     }
 
     public int getCurrentScrollY(int position, int offset) {
-        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
-        AlphabeticalAppsList.AdapterItem posItem = position < items.size() ?
-                items.get(position) : null;
+        List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
+        AllAppsGridAdapter.AdapterItem posItem = position < items.size()
+                ? items.get(position) : null;
         int y = mCachedScrollPositions.get(position, -1);
         if (y < 0) {
             y = 0;
             for (int i = 0; i < position; i++) {
-                AlphabeticalAppsList.AdapterItem item = items.get(i);
+                AllAppsGridAdapter.AdapterItem item = items.get(i);
                 if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
                     // Break once we reach the desired row
                     if (posItem != null && posItem.viewType == item.viewType &&
@@ -412,7 +414,7 @@
 
     /**
      * Returns the available scroll height:
-     *   AvailableScrollHeight = Total height of the all items - last page height
+     * AvailableScrollHeight = Total height of the all items - last page height
      */
     @Override
     protected int getAvailableScrollHeight() {
@@ -448,4 +450,14 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
+
+    /**
+     * Returns distance between left and right app icons
+     */
+    public int getTabWidth() {
+        DeviceProfile grid = BaseDraggingActivity.fromContext(getContext()).getDeviceProfile();
+        int totalWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
+        int iconPadding = totalWidth / grid.numShownAllAppsColumns - grid.allAppsIconSizePx;
+        return totalWidth - iconPadding - grid.allAppsIconDrawablePaddingPx;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 3ae0a18..7bc3eec 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -17,14 +17,16 @@
 
 import static com.android.launcher3.model.data.AppInfo.COMPONENT_KEY_COMPARATOR;
 import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK;
 
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -79,6 +81,11 @@
         return (mModelFlags & mask) != 0;
     }
 
+    /**
+     * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise
+     * null.
+     */
+    @Nullable
     public AppInfo getApp(ComponentKey key) {
         mTempInfo.componentName = key.componentName;
         mTempInfo.user = key.user;
@@ -125,7 +132,7 @@
     }
 
     public void registerIconContainer(ViewGroup container) {
-        if (container != null) {
+        if (container != null && !mIconContainers.contains(container)) {
             mIconContainers.add(container);
         }
     }
@@ -145,10 +152,23 @@
         });
     }
 
-    public void updatePromiseAppProgress(PromiseAppInfo app) {
+    /**
+     * Sets the AppInfo's associated icon's progress bar.
+     *
+     * If this app is installed and supports incremental downloads, the progress bar will be updated
+     * the app's total download progress. Otherwise, the progress bar will be updated to the app's
+     * installation progress.
+     *
+     * If this app is fully downloaded, the app icon will be reapplied.
+     */
+    public void updateProgressBar(AppInfo app) {
         updateAllIcons((child) -> {
             if (child.getTag() == app) {
-                child.applyProgressLevel(app.level);
+                if ((app.runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) == 0) {
+                    child.applyFromApplicationInfo(app);
+                } else {
+                    child.applyProgressLevel();
+                }
             }
         });
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a9b030e..a0551f0 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -1,44 +1,49 @@
+/*
+ * 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.
+ */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
-import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
-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.INSTANT;
+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;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import android.animation.Animator.AnimatorListener;
 import android.animation.ObjectAnimator;
-import android.content.Context;
 import android.util.FloatProperty;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.animation.Interpolator;
-import android.widget.EditText;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimationSuccessListener;
+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;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.views.ScrimView;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
-import com.android.systemui.plugins.PluginListener;
 
 /**
  * Handles AllApps view transition.
@@ -50,27 +55,27 @@
  * If release velocity < THRES1, snap according to either top or bottom depending on whether it's
  * closer to top or closer to the page indicator.
  */
-public class AllAppsTransitionController implements StateHandler<LauncherState>,
-        OnDeviceProfileChangeListener, PluginListener<AllAppsSearchPlugin> {
+public class AllAppsTransitionController
+        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") {
 
-        @Override
-        public Float get(AllAppsTransitionController controller) {
-            return controller.mProgress;
-        }
+                @Override
+                public Float get(AllAppsTransitionController controller) {
+                    return controller.mProgress;
+                }
 
-        @Override
-        public void setValue(AllAppsTransitionController controller, float progress) {
-            controller.setProgress(progress);
-        }
-    };
-
-    private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0;
+                @Override
+                public void setValue(AllAppsTransitionController controller, float progress) {
+                    controller.setProgress(progress);
+                }
+            };
 
     private AllAppsContainerView mAppsView;
-    private ScrimView mScrimView;
 
     private final Launcher mLauncher;
     private boolean mIsVerticalLayout;
@@ -85,10 +90,7 @@
     private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
 
     private float mScrollRangeDelta = 0;
-
-    // plugin related variables
-    private AllAppsSearchPlugin mPlugin;
-    private View mPluginContent;
+    private ScrimView mScrimView;
 
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
@@ -109,7 +111,6 @@
         setScrollRangeDelta(mScrollRangeDelta);
 
         if (mIsVerticalLayout) {
-            mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1);
             mLauncher.getHotseat().setTranslationY(0);
             mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
         }
@@ -120,19 +121,12 @@
      * in xml-based animations which also handle updating the appropriate UI.
      *
      * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
-     *
      * @see #setState(LauncherState)
      * @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
      */
     public void setProgress(float progress) {
         mProgress = progress;
-        mScrimView.setProgress(progress);
-        float shiftCurrent = progress * mShiftRange;
-
-        mAppsView.setTranslationY(shiftCurrent);
-        if (mPlugin != null) {
-            mPlugin.setProgress(progress);
-        }
+        mAppsView.setTranslationY(mProgress * mShiftRange);
     }
 
     public float getProgress() {
@@ -159,22 +153,14 @@
             StateAnimationConfig config, PendingAnimation builder) {
         float targetProgress = toState.getVerticalProgress(mLauncher);
         if (Float.compare(mProgress, targetProgress) == 0) {
-            if (!config.onlyPlayAtomicComponent()) {
-                setAlphas(toState, config, builder);
-            }
+            setAlphas(toState, config, builder);
             // Fail fast
             onProgressAnimationEnd();
             return;
         }
 
-        if (config.onlyPlayAtomicComponent()) {
-            // There is no atomic component for the all apps transition, so just return early.
-            return;
-        }
-
-        Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
-                ? config.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
-                : FAST_OUT_SLOW_IN;
+        // need to decide depending on the release velocity
+        Interpolator interpolator = (config.userControlled ? LINEAR : DEACCEL_1_7);
 
         Animator anim = createSpringAnimation(mProgress, targetProgress);
         anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
@@ -193,55 +179,41 @@
      */
     public void setAlphas(LauncherState state, StateAnimationConfig config, PropertySetter setter) {
         int visibleElements = state.getVisibleElements(mLauncher);
-        boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
         boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
 
-        boolean hasAnyVisibleItem = (visibleElements & APPS_VIEW_ITEM_MASK) != 0;
+        Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE,
+                Interpolators.clampToProgress(LINEAR, 0, CONTENT_VISIBLE_MAX_THRESHOLD));
+        setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
 
-        Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
-        Interpolator headerFade = config.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
-
-        if (mPlugin == null) {
-            setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
-            setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
-            mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra,
-                    hasAllAppsContent, setter, headerFade, allAppsFade);
-        } else {
-            setter.setViewAlpha(mPluginContent, hasAllAppsContent ? 1 : 0, allAppsFade);
-            setter.setViewAlpha(mAppsView.getContentView(), 0, allAppsFade);
-            setter.setViewAlpha(mAppsView.getScrollBar(), 0, allAppsFade);
-        }
-        mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
-
-        setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
-                (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade);
-
-        // Set visibility of the container at the very beginning or end of the transition.
-        setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0,
-                hasAnyVisibleItem ? INSTANT : FINAL_FRAME);
+        boolean shouldProtectHeader =
+                ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS;
+        mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null);
     }
 
-    public AnimatorListenerAdapter getProgressAnimatorListener() {
-        return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd);
+    public AnimatorListener getProgressAnimatorListener() {
+        return AnimatorListeners.forSuccessCallback(this::onProgressAnimationEnd);
     }
 
-    public void setupViews(AllAppsContainerView appsView, ScrimView scrimView) {
-        mAppsView = appsView;
+    /**
+     * see Launcher#setupViews
+     */
+    public void setupViews(ScrimView scrimView, AllAppsContainerView appsView) {
         mScrimView = scrimView;
-        PluginManagerWrapper.INSTANCE.get(mLauncher)
-                .addPluginListener(this, AllAppsSearchPlugin.class, false);
+        mAppsView = appsView;
+        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && Utilities.ATLEAST_R) {
+            mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
+                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        }
+        mAppsView.setScrimView(scrimView);
     }
 
     /**
      * Updates the total scroll range but does not update the UI.
      */
-    void setScrollRangeDelta(float delta) {
+    public void setScrollRangeDelta(float delta) {
         mScrollRangeDelta = delta;
         mShiftRange = mLauncher.getDeviceProfile().heightPx - mScrollRangeDelta;
-
-        if (mScrimView != null) {
-            mScrimView.reInitUi();
-        }
     }
 
     /**
@@ -249,50 +221,9 @@
      * TODO: This logic should go in {@link LauncherState}
      */
     private void onProgressAnimationEnd() {
+        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
         if (Float.compare(mProgress, 1f) == 0) {
             mAppsView.reset(false /* animate */);
         }
-        updatePluginAnimationEnd();
-    }
-
-    @Override
-    public void onPluginConnected(AllAppsSearchPlugin plugin, Context context) {
-        mPlugin = plugin;
-        mPluginContent = mLauncher.getLayoutInflater().inflate(
-                R.layout.all_apps_content_layout, mAppsView, false);
-        mAppsView.addView(mPluginContent);
-        mPluginContent.setAlpha(0f);
-        mPlugin.setup((ViewGroup) mPluginContent, mLauncher, mShiftRange);
-    }
-
-    @Override
-    public void onPluginDisconnected(AllAppsSearchPlugin plugin) {
-        mPlugin = null;
-        mAppsView.removeView(mPluginContent);
-    }
-
-    public void onActivityDestroyed() {
-        PluginManagerWrapper.INSTANCE.get(mLauncher).removePluginListener(this);
-    }
-
-    /** Used for the plugin to signal when drag starts happens
-     * @param toAllApps*/
-    public void onDragStart(boolean toAllApps) {
-        if (mPlugin == null) return;
-
-        if (toAllApps) {
-            EditText editText = mAppsView.getSearchUiManager().setTextSearchEnabled(true);
-            mPlugin.setEditText(editText);
-        }
-        mPlugin.onDragStart(toAllApps ? 1f : 0f);
-    }
-
-    private void updatePluginAnimationEnd() {
-        if (mPlugin == null) return;
-        mPlugin.onAnimationEnd(mProgress);
-        if (Float.compare(mProgress, 1f) == 0) {
-            mAppsView.getSearchUiManager().setTextSearchEnabled(false);
-            mPlugin.setEditText(null);
-        }
     }
 }
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 06209bb..ce5c589 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -19,8 +19,9 @@
 import android.content.Context;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LabelComparator;
 
@@ -29,6 +30,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.TreeMap;
 
 /**
@@ -42,6 +44,7 @@
     private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1;
 
     private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
+    private final WorkAdapterProvider mWorkAdapterProvider;
 
     /**
      * Info about a fast scroller section, depending if sections are merged, the fast scroller
@@ -60,61 +63,6 @@
         }
     }
 
-    /**
-     * Info about a particular adapter item (can be either section or app)
-     */
-    public static class AdapterItem {
-        /** Common properties */
-        // The index of this adapter item in the list
-        public int position;
-        // 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
-        // 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 index of this app not including sections
-        public int appIndex = -1;
-
-        public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
-                int appIndex) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.VIEW_TYPE_ICON;
-            item.position = pos;
-            item.sectionName = sectionName;
-            item.appInfo = appInfo;
-            item.appIndex = appIndex;
-            return item;
-        }
-
-        public static AdapterItem asEmptySearch(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH;
-            item.position = pos;
-            return item;
-        }
-
-        public static AdapterItem asAllAppsDivider(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
-            item.position = pos;
-            return item;
-        }
-
-        public static AdapterItem asMarketSearch(int pos) {
-            AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET;
-            item.position = pos;
-            return item;
-        }
-    }
 
     private final BaseDraggingActivity mLauncher;
 
@@ -122,28 +70,27 @@
     private final List<AppInfo> mApps = new ArrayList<>();
     private final AllAppsStore mAllAppsStore;
 
-    // The set of filtered apps with the current filter
-    private final List<AppInfo> mFilteredApps = new ArrayList<>();
+    // The number of results in current adapter
+    private int mAccessibilityResultsCount = 0;
     // The current set of adapter items
     private final ArrayList<AdapterItem> mAdapterItems = new ArrayList<>();
     // The set of sections that we allow fast-scrolling to (includes non-merged sections)
     private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
-    // Is it the work profile app list.
-    private final boolean mIsWork;
 
     // The of ordered component names as a result of a search query
-    private ArrayList<ComponentKey> mSearchResults;
+    private ArrayList<AdapterItem> mSearchResults;
     private AllAppsGridAdapter mAdapter;
     private AppInfoComparator mAppNameComparator;
     private final int mNumAppsPerRow;
     private int mNumAppRowsInAdapter;
     private ItemInfoMatcher mItemFilter;
 
-    public AlphabeticalAppsList(Context context, AllAppsStore appsStore, boolean isWork) {
+    public AlphabeticalAppsList(Context context, AllAppsStore appsStore,
+            WorkAdapterProvider adapterProvider) {
         mAllAppsStore = appsStore;
         mLauncher = BaseDraggingActivity.fromContext(context);
         mAppNameComparator = new AppInfoComparator(context);
-        mIsWork = isWork;
+        mWorkAdapterProvider = adapterProvider;
         mNumAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
         mAllAppsStore.addUpdateListener(this);
     }
@@ -182,6 +129,28 @@
     }
 
     /**
+     * Returns the child adapter item with IME launch focus.
+     */
+    public AdapterItem getFocusedChild() {
+        if (mAdapterItems.size() == 0 || getFocusedChildIndex() == -1) {
+            return null;
+        }
+        return mAdapterItems.get(getFocusedChildIndex());
+    }
+
+    /**
+     * Returns the index of the child with IME launch focus.
+     */
+    public int getFocusedChildIndex() {
+        for (AdapterItem item : mAdapterItems) {
+            if (item.isCountedForAccessibility()) {
+                return mAdapterItems.indexOf(item);
+            }
+        }
+        return -1;
+    }
+
+    /**
      * Returns the number of rows of applications
      */
     public int getNumAppRows() {
@@ -192,7 +161,7 @@
      * Returns the number of applications in this list.
      */
     public int getNumFilteredApps() {
-        return mFilteredApps.size();
+        return mAccessibilityResultsCount;
     }
 
     /**
@@ -206,22 +175,42 @@
      * Returns whether there are no filtered results.
      */
     public boolean hasNoFilteredResults() {
-        return (mSearchResults != null) && mFilteredApps.isEmpty();
+        return (mSearchResults != null) && mAccessibilityResultsCount == 0;
     }
 
     /**
-     * Sets the sorted list of filtered components.
+     * Sets results list for search
      */
-    public boolean setOrderedFilter(ArrayList<ComponentKey> f) {
-        if (mSearchResults != f) {
-            boolean same = mSearchResults != null && mSearchResults.equals(f);
-            mSearchResults = f;
-            onAppsUpdated();
-            return !same;
+    public boolean setSearchResults(ArrayList<AdapterItem> results) {
+        if (!Objects.equals(results, mSearchResults)) {
+            mSearchResults = results;
+            updateAdapterItems();
+            return true;
         }
         return false;
     }
 
+    public boolean appendSearchResults(ArrayList<AdapterItem> results) {
+        if (mSearchResults != null && results != null && results.size() > 0) {
+            updateSearchAdapterItems(results, mSearchResults.size());
+            refreshRecyclerView();
+            return true;
+        }
+        return false;
+    }
+
+    void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
+        for (int i = 0; i < list.size(); i++) {
+            AdapterItem adapterItem = list.get(i);
+            adapterItem.position = offset + i;
+            mAdapterItems.add(adapterItem);
+
+            if (adapterItem.isCountedForAccessibility()) {
+                mAccessibilityResultsCount++;
+            }
+        }
+    }
+
     /**
      * Updates internals when the set of apps are updated.
      */
@@ -267,14 +256,16 @@
         }
 
         // Recompose the set of adapter items from the current set of apps
-        updateAdapterItems();
+        if (mSearchResults == null) {
+            updateAdapterItems();
+        }
     }
 
     /**
-     * Updates the set of filtered apps with the current filter.  At this point, we expect
+     * Updates the set of filtered apps with the current filter. At this point, we expect
      * mCachedSectionNames to have been calculated for the set of all apps in mApps.
      */
-    private void updateAdapterItems() {
+    public void updateAdapterItems() {
         refillAdapterItems();
         refreshRecyclerView();
     }
@@ -292,41 +283,53 @@
         int appIndex = 0;
 
         // Prepare to update the list of sections, filtered apps, etc.
-        mFilteredApps.clear();
+        mAccessibilityResultsCount = 0;
         mFastScrollerSections.clear();
         mAdapterItems.clear();
 
         // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
         // ordered set of sections
-        for (AppInfo info : getFiltersAppInfos()) {
-            String sectionName = info.sectionName;
 
-            // Create a new section if the section names do not match
-            if (!sectionName.equals(lastSectionName)) {
-                lastSectionName = sectionName;
-                lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
-                mFastScrollerSections.add(lastFastScrollerSectionInfo);
+        if (!hasFilter()) {
+            mAccessibilityResultsCount = mApps.size();
+            if (mWorkAdapterProvider != null) {
+                position += mWorkAdapterProvider.addWorkItems(mAdapterItems);
+                if (!mWorkAdapterProvider.shouldShowWorkApps()) {
+                    return;
+                }
             }
+            for (AppInfo info : mApps) {
+                String sectionName = info.sectionName;
 
-            // Create an app item
-            AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++);
-            if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
-                lastFastScrollerSectionInfo.fastScrollToItem = appItem;
+                // Create a new section if the section names do not match
+                if (!sectionName.equals(lastSectionName)) {
+                    lastSectionName = sectionName;
+                    lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
+                    mFastScrollerSections.add(lastFastScrollerSectionInfo);
+                }
+
+                // Create an app item
+                AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info,
+                        appIndex++);
+                if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
+                    lastFastScrollerSectionInfo.fastScrollToItem = appItem;
+                }
+
+                mAdapterItems.add(appItem);
             }
-            mAdapterItems.add(appItem);
-            mFilteredApps.add(info);
+        } else {
+            updateSearchAdapterItems(mSearchResults, 0);
+            if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+                // Append the search market item
+                if (hasNoFilteredResults()) {
+                    mAdapterItems.add(AdapterItem.asEmptySearch(position++));
+                } else {
+                    mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
+                }
+                mAdapterItems.add(AdapterItem.asMarketSearch(position++));
+
+            }
         }
-
-        if (hasFilter()) {
-            // Append the search market item
-            if (hasNoFilteredResults()) {
-                mAdapterItems.add(AdapterItem.asEmptySearch(position++));
-            } else {
-                mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
-            }
-            mAdapterItems.add(AdapterItem.asMarketSearch(position++));
-        }
-
         if (mNumAppsPerRow != 0) {
             // Update the number of rows in the adapter after we do all the merging (otherwise, we
             // would have to shift the values again)
@@ -381,18 +384,4 @@
             }
         }
     }
-
-    private List<AppInfo> getFiltersAppInfos() {
-        if (mSearchResults == null) {
-            return mApps;
-        }
-        ArrayList<AppInfo> result = new ArrayList<>();
-        for (ComponentKey key : mSearchResults) {
-            AppInfo match = mAllAppsStore.getApp(key);
-            if (match != null) {
-                result.add(match);
-            }
-        }
-        return result;
-    }
 }
diff --git a/src/com/android/launcher3/allapps/BaseAdapterProvider.java b/src/com/android/launcher3/allapps/BaseAdapterProvider.java
new file mode 100644
index 0000000..308294c
--- /dev/null
+++ b/src/com/android/launcher3/allapps/BaseAdapterProvider.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.allapps;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+/**
+ * A UI expansion wrapper providing for providing dynamic recyclerview items
+ */
+public abstract class BaseAdapterProvider {
+
+    /**
+     * Returns whether or not viewType can be handled by searchProvider
+     */
+    public abstract boolean isViewSupported(int viewType);
+
+    /**
+     * Called from RecyclerView.Adapter#onBindViewHolder
+     */
+    public abstract void onBindView(AllAppsGridAdapter.ViewHolder holder, int position);
+
+    /**
+     * Called from RecyclerView.Adapter#onCreateViewHolder
+     */
+    public abstract AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater,
+            ViewGroup parent, int viewType);
+
+    /**
+     * Returns supported item per row combinations supported
+     */
+    public int[] getSupportedItemsPerRowArray() {
+        return new int[]{};
+    }
+
+    /**
+     * Returns how many cells a view should span
+     */
+    public int getItemsPerRow(int viewType, int appsPerRow) {
+        return appsPerRow;
+    }
+
+}
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/src/com/android/launcher3/allapps/DecorationInfo.java
similarity index 69%
rename from src/com/android/launcher3/util/SafeCloseable.java
rename to src/com/android/launcher3/allapps/DecorationInfo.java
index ba8ee04..50b250c 100644
--- a/src/com/android/launcher3/util/SafeCloseable.java
+++ b/src/com/android/launcher3/allapps/DecorationInfo.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,14 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.launcher3.allapps;
 
-package com.android.launcher3.util;
-
-/**
- * Extension of closeable which does not throw an exception
- */
-public interface SafeCloseable extends AutoCloseable {
-
-    @Override
-    void close();
+public class DecorationInfo {
 }
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index b4ff5ea..be261f7 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -17,24 +17,21 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.HOTSEAT;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.PREDICTION;
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
 import android.os.Handler;
 import android.os.UserManager;
 import android.view.MotionEvent;
+import android.view.View;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.OnboardingPrefs;
 
 /**
@@ -57,21 +54,15 @@
         public void onStateTransitionComplete(LauncherState finalState) {}
     };
 
-    public DiscoveryBounce(Launcher launcher, float delta) {
+    public DiscoveryBounce(Launcher launcher) {
         super(launcher, null);
         mLauncher = launcher;
-        AllAppsTransitionController controller = mLauncher.getAllAppsController();
 
         mDiscoBounceAnimation =
                 AnimatorInflater.loadAnimator(launcher, R.animator.discovery_bounce);
-        mDiscoBounceAnimation.setTarget(new VerticalProgressWrapper(controller, delta));
-        mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                handleClose(false);
-            }
-        });
-        mDiscoBounceAnimation.addListener(controller.getProgressAnimatorListener());
+        mDiscoBounceAnimation.setTarget(new VerticalProgressWrapper(
+                launcher.getHotseat(), mLauncher.getDragLayer().getHeight()));
+        mDiscoBounceAnimation.addListener(AnimatorListeners.forEndCallback(this::handleClose));
         launcher.getStateManager().addStateListener(mStateListener);
     }
 
@@ -108,27 +99,22 @@
         if (mIsOpen) {
             mIsOpen = false;
             mLauncher.getDragLayer().removeView(this);
-            // Reset the all-apps progress to what ever it was previously.
-            mLauncher.getAllAppsController().setProgress(mLauncher.getStateManager()
-                    .getState().getVerticalProgress(mLauncher));
+            // Reset the translation to what ever it was previously.
+            mLauncher.getHotseat().setTranslationY(mLauncher.getStateManager().getState()
+                    .getHotseatScaleAndTranslation(mLauncher).translationY);
             mLauncher.getStateManager().removeStateListener(mStateListener);
         }
     }
 
     @Override
-    public void logActionCommand(int command) {
-        // Since this is on-boarding popup, it is not a user controlled action.
-    }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_DISCOVERY_BOUNCE) != 0;
     }
 
-    private void show(int containerType) {
+    private void show() {
         mIsOpen = true;
         mLauncher.getDragLayer().addView(this);
-        mLauncher.getUserEventDispatcher().logActionBounceTip(containerType);
+        // TODO: add WW log for discovery bounce tip show event.
     }
 
     public static void showForHomeIfNeeded(Launcher launcher) {
@@ -150,62 +136,28 @@
             return;
         }
         onboardingPrefs.incrementEventCount(OnboardingPrefs.HOME_BOUNCE_COUNT);
-
-        new DiscoveryBounce(launcher, 0).show(HOTSEAT);
-    }
-
-    public static void showForOverviewIfNeeded(Launcher launcher,
-                                               PagedOrientationHandler orientationHandler) {
-        showForOverviewIfNeeded(launcher, true, orientationHandler);
-    }
-
-    private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay,
-                                                PagedOrientationHandler orientationHandler) {
-        OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs();
-        if (!launcher.isInState(OVERVIEW)
-                || !launcher.hasBeenResumed()
-                || launcher.isForceInvisible()
-                || launcher.getDeviceProfile().isVerticalBarLayout()
-                || !orientationHandler.isLayoutNaturalToLauncher()
-                || onboardingPrefs.getBoolean(OnboardingPrefs.SHELF_BOUNCE_SEEN)
-                || launcher.getSystemService(UserManager.class).isDemoUser()
-                || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
-            return;
-        }
-
-        if (withDelay) {
-            new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false,
-                    orientationHandler), DELAY_MS);
-            return;
-        } else if (AbstractFloatingView.getTopOpenView(launcher) != null) {
-            // TODO: Move these checks to the top and call this method after invalidate handler.
-            return;
-        }
-        onboardingPrefs.incrementEventCount(OnboardingPrefs.SHELF_BOUNCE_COUNT);
-
-        new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher)))
-                .show(PREDICTION);
+        new DiscoveryBounce(launcher).show();
     }
 
     /**
-     * A wrapper around {@link AllAppsTransitionController} allowing a fixed shift in the value.
+     * A wrapper around hotseat animator allowing a fixed shift in the value.
      */
     public static class VerticalProgressWrapper {
 
-        private final float mDelta;
-        private final AllAppsTransitionController mController;
+        private final View mView;
+        private final float mLimit;
 
-        private VerticalProgressWrapper(AllAppsTransitionController controller, float delta) {
-            mController = controller;
-            mDelta = delta;
+        private VerticalProgressWrapper(View view, float limit) {
+            mView = view;
+            mLimit = limit;
         }
 
         public float getProgress() {
-            return mController.getProgress() + mDelta;
+            return 1 + mView.getTranslationY() / mLimit;
         }
 
         public void setProgress(float progress) {
-            mController.setProgress(progress - mDelta);
+            mView.setTranslationY(mLimit * (progress - 1));
         }
     }
 }
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index f899587..9bf6043 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -16,10 +16,9 @@
 package com.android.launcher3.allapps;
 
 import android.graphics.Rect;
-import android.view.animation.Interpolator;
+import android.view.View;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.PropertySetter;
 
 /**
  * A abstract representation of a row in all-apps view
@@ -46,13 +45,22 @@
      */
     boolean hasVisibleContent();
 
-    void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade);
-
     /**
      * Scrolls the content vertically.
      */
     void setVerticalScroll(int scroll, boolean isScrolledOut);
 
     Class<? extends FloatingHeaderRow> getTypeClass();
+
+    /**
+     * Returns a child that has focus to be launched by the IME.
+     */
+    View getFocusedChild();
+
+    /**
+     * Returns true if view is currently visible
+     */
+    default boolean isVisible() {
+        return shouldDraw();
+    }
 }
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 81e1b94..8ea83d5 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,10 +15,11 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.ArrayMap;
@@ -26,7 +27,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
@@ -37,7 +37,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.systemui.plugins.AllAppsRow;
 import com.android.systemui.plugins.AllAppsRow.OnHeightUpdatedListener;
@@ -52,27 +52,35 @@
 
     private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
     private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
+    private final ValueAnimator mHeaderAnimator = ValueAnimator.ofInt(0, 1).setDuration(100);
     private final Point mTempOffset = new Point();
-    private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
-        @Override
-        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
-        }
+    private final Paint mBGPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final RecyclerView.OnScrollListener mOnScrollListener =
+            new RecyclerView.OnScrollListener() {
+                @Override
+                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+                }
 
-        @Override
-        public void onScrolled(RecyclerView rv, int dx, int dy) {
-            if (rv != mCurrentRV) {
-                return;
-            }
+                @Override
+                public void onScrolled(RecyclerView rv, int dx, int dy) {
+                    if (rv != mCurrentRV) {
+                        return;
+                    }
 
-            if (mAnimator.isStarted()) {
-                mAnimator.cancel();
-            }
+                    if (mAnimator.isStarted()) {
+                        mAnimator.cancel();
+                    }
 
-            int current = -mCurrentRV.getCurrentScrollY();
-            moved(current);
-            applyVerticalMove();
-        }
-    };
+                    int current = -mCurrentRV.getCurrentScrollY();
+                    boolean headerCollapsed = mHeaderCollapsed;
+                    moved(current);
+                    applyVerticalMove();
+                    if (headerCollapsed != mHeaderCollapsed) {
+                        AllAppsContainerView parent = (AllAppsContainerView) getParent();
+                        parent.invalidateHeader();
+                    }
+                }
+            };
 
     private final int mHeaderTopPadding;
 
@@ -83,11 +91,11 @@
     private AllAppsRecyclerView mWorkRV;
     private AllAppsRecyclerView mCurrentRV;
     private ViewGroup mParent;
-    private boolean mHeaderCollapsed;
+    public boolean mHeaderCollapsed;
     private int mSnappedScrolledY;
     private int mTranslationY;
+    private int mHeaderColor;
 
-    private boolean mAllowTouchForwarding;
     private boolean mForwardToRecyclerView;
 
     protected boolean mTabsHidden;
@@ -130,6 +138,7 @@
         }
         mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
         mAllRows = mFixedRows;
+        mHeaderAnimator.addUpdateListener(valueAnimator -> invalidate());
     }
 
     @Override
@@ -145,6 +154,14 @@
         PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mMainRV != null) {
+            mTabLayout.getLayoutParams().width = mMainRV.getTabWidth();
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
     private void recreateAllRowsArray() {
         int pluginCount = mPluginRows.size();
         if (pluginCount == 0) {
@@ -194,6 +211,19 @@
         onHeightUpdated();
     }
 
+    @Override
+    public View getFocusedChild() {
+        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+            for (FloatingHeaderRow row : mAllRows) {
+                if (row.hasVisibleContent() && row.isVisible()) {
+                    return row.getFocusedChild();
+                }
+            }
+            return null;
+        }
+        return super.getFocusedChild();
+    }
+
     public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
         for (FloatingHeaderRow row : mAllRows) {
             row.setup(this, mAllRows, tabsHidden);
@@ -210,7 +240,7 @@
     }
 
     private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) {
-        if (old != updated && updated != null ) {
+        if (old != updated && updated != null) {
             updated.addOnScrollListener(mOnScrollListener);
         }
         return updated;
@@ -265,10 +295,31 @@
             } else if (mTranslationY <= -mMaxTranslation) { // hide or stay hidden
                 mHeaderCollapsed = true;
                 mSnappedScrolledY = -mMaxTranslation;
+                mHeaderAnimator.setCurrentFraction(0);
+                mHeaderAnimator.start();
             }
         }
     }
 
+    /**
+     * Set current header protection background color
+     */
+    public void setHeaderColor(int color) {
+        mHeaderColor = color;
+        invalidate();
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mHeaderCollapsed && !mCollapsed && mTabLayout.getVisibility() == VISIBLE
+                && mHeaderColor != Color.TRANSPARENT && FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+            mBGPaint.setColor(mHeaderColor);
+            mBGPaint.setAlpha((int) (255 * mHeaderAnimator.getAnimatedFraction()));
+            canvas.drawRect(0, 0, getWidth(), getHeight() + mTranslationY, mBGPaint);
+        }
+        super.dispatchDraw(canvas);
+    }
+
     protected void applyVerticalMove() {
         int uncappedTranslationY = mTranslationY;
         mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
@@ -333,10 +384,6 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (!mAllowTouchForwarding) {
-            mForwardToRecyclerView = false;
-            return super.onInterceptTouchEvent(ev);
-        }
         calcOffset(mTempOffset);
         ev.offsetLocation(mTempOffset.x, mTempOffset.y);
         mForwardToRecyclerView = mCurrentRV.onInterceptTouchEvent(ev);
@@ -365,20 +412,6 @@
         p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
     }
 
-    public void setContentVisibility(boolean hasHeader, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
-        for (FloatingHeaderRow row : mAllRows) {
-            row.setContentVisibility(hasHeader, hasAllAppsContent, setter, headerFade, allAppsFade);
-        }
-
-        allowTouchForwarding(hasAllAppsContent);
-        setter.setFloat(mTabLayout, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
-    }
-
-    protected void allowTouchForwarding(boolean allow) {
-        mAllowTouchForwarding = allow;
-    }
-
     public boolean hasVisibleContent() {
         for (FloatingHeaderRow row : mAllRows) {
             if (row.hasVisibleContent()) {
@@ -409,6 +442,13 @@
         }
         return null;
     }
+
+    /**
+     * Returns visible height of FloatingHeaderView contents
+     */
+    public int getVisibleBottomBound() {
+        return getBottom() + mTranslationY;
+    }
 }
 
 
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index a6bc6cf..f64b7cb 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -23,7 +23,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.views.WorkEduView;
 
 /**
  * AllAppsContainerView with launcher specific callbacks
@@ -70,8 +69,9 @@
     @Override
     public void setInsets(Rect insets) {
         super.setInsets(insets);
-        mLauncher.getAllAppsController()
-                .setScrollRangeDelta(mSearchUiManager.getScrollRangeDelta(insets));
+        int allAppsStartingPositionY = mLauncher.getDeviceProfile().availableHeightPx
+                - mLauncher.getDeviceProfile().allAppsOpenVerticalTranslate;
+        mLauncher.getAllAppsController().setScrollRangeDelta(allAppsStartingPositionY);
     }
 
     @Override
@@ -83,14 +83,7 @@
     }
 
     @Override
-    public void onTabChanged(int pos) {
-        super.onTabChanged(pos);
-        if (mUsingTabs) {
-            if (pos == AdapterHolder.WORK) {
-                WorkEduView.showWorkEduIfNeeded(mLauncher);
-            } else {
-                mWorkTabListener = WorkEduView.showEduFlowIfNeeded(mLauncher, mWorkTabListener);
-            }
-        }
+    public void onActivePageChanged(int currentActivePage) {
+        super.onActivePageChanged(currentActivePage);
     }
 }
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
deleted file mode 100644
index 2de425e..0000000
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.LinearLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.util.Themes;
-
-/**
- * Supports two indicator colors, dedicated for personal and work tabs.
- */
-public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageIndicator {
-    private static final int POSITION_PERSONAL = 0;
-    private static final int POSITION_WORK = 1;
-
-    private final Paint mSelectedIndicatorPaint;
-    private final Paint mDividerPaint;
-
-    private int mSelectedIndicatorHeight;
-    private int mIndicatorLeft = -1;
-    private int mIndicatorRight = -1;
-    private float mScrollOffset;
-    private int mSelectedPosition = 0;
-
-    private AllAppsContainerView mContainerView;
-    private int mLastActivePage = 0;
-    private boolean mIsRtl;
-
-    public PersonalWorkSlidingTabStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        setOrientation(HORIZONTAL);
-        setWillNotDraw(false);
-
-        mSelectedIndicatorHeight =
-                getResources().getDimensionPixelSize(R.dimen.all_apps_tabs_indicator_height);
-
-        mSelectedIndicatorPaint = new Paint();
-        mSelectedIndicatorPaint.setColor(
-                Themes.getAttrColor(context, android.R.attr.colorAccent));
-
-        mDividerPaint = new Paint();
-        mDividerPaint.setColor(Themes.getAttrColor(context, android.R.attr.colorControlHighlight));
-        mDividerPaint.setStrokeWidth(
-                getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
-
-        mIsRtl = Utilities.isRtl(getResources());
-    }
-
-    /**
-     * Highlights tab with index pos
-     */
-    public void updateTabTextColor(int pos) {
-        mSelectedPosition = pos;
-        for (int i = 0; i < getChildCount(); i++) {
-            Button tab = (Button) getChildAt(i);
-            tab.setSelected(i == pos);
-        }
-    }
-
-    private void updateIndicatorPosition(float scrollOffset) {
-        mScrollOffset = scrollOffset;
-        updateIndicatorPosition();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        updateTabTextColor(mSelectedPosition);
-        updateIndicatorPosition(mScrollOffset);
-    }
-
-    private void updateIndicatorPosition() {
-        int left = -1, right = -1;
-        final View leftTab = getLeftTab();
-        if (leftTab != null) {
-            left = (int) (leftTab.getLeft() + leftTab.getWidth() * mScrollOffset);
-            right = left + leftTab.getWidth();
-        }
-        setIndicatorPosition(left, right);
-    }
-
-    private View getLeftTab() {
-        return mIsRtl ? getChildAt(1) : getChildAt(0);
-    }
-
-    private void setIndicatorPosition(int left, int right) {
-        if (left != mIndicatorLeft || right != mIndicatorRight) {
-            mIndicatorLeft = left;
-            mIndicatorRight = right;
-            invalidate();
-        }
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        float y = getHeight() - mDividerPaint.getStrokeWidth();
-        canvas.drawLine(getPaddingLeft(), y, getWidth() - getPaddingRight(), y, mDividerPaint);
-        canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
-            mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
-    }
-
-    @Override
-    public void setScroll(int currentScroll, int totalScroll) {
-        float scrollOffset = ((float) currentScroll) / totalScroll;
-        updateIndicatorPosition(scrollOffset);
-    }
-
-    @Override
-    public void setActiveMarker(int activePage) {
-        updateTabTextColor(activePage);
-        if (mContainerView != null && mLastActivePage != activePage) {
-            updateIndicatorPosition(activePage);
-            mContainerView.onTabChanged(activePage);
-        }
-        mLastActivePage = activePage;
-    }
-
-    public void setContainerView(AllAppsContainerView containerView) {
-        mContainerView = containerView;
-    }
-
-    @Override
-    public void setMarkersCount(int numMarkers) { }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index 3089b18..5b5fbb7 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -18,14 +18,10 @@
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-
 import android.graphics.Rect;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.PropertySetter;
 import com.android.systemui.plugins.AllAppsRow;
 
 /**
@@ -65,13 +61,6 @@
     }
 
     @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
-        // Don't use setViewAlpha as we want to control the visibility ourselves.
-        setter.setFloat(mView, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
-    }
-
-    @Override
     public void setVerticalScroll(int scroll, boolean isScrolledOut) {
         mView.setVisibility(isScrolledOut ? INVISIBLE : VISIBLE);
         if (!isScrolledOut) {
@@ -83,4 +72,9 @@
     public Class<PluginHeaderRow> getTypeClass() {
         return PluginHeaderRow.class;
     }
+
+    @Override
+    public View getFocusedChild() {
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 7d5363f..924a392 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -15,16 +15,11 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
-
-import android.graphics.Rect;
 import android.view.KeyEvent;
-import android.view.animation.Interpolator;
-import android.widget.EditText;
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.ExtendedEditText;
 
 /**
  * Interface for controlling the Apps search UI.
@@ -34,7 +29,7 @@
     /**
      * Initializes the search manager.
      */
-    void initialize(AllAppsContainerView containerView);
+    void initializeSearch(AllAppsContainerView containerView);
 
     /**
      * Notifies the search manager to close any active search session.
@@ -45,33 +40,16 @@
      * Called before dispatching a key event, in case the search manager wants to initialize
      * some UI beforehand.
      */
-    void preDispatchKeyEvent(KeyEvent keyEvent);
+    default void preDispatchKeyEvent(KeyEvent keyEvent) { };
 
     /**
-     * Returns the vertical shift for the all-apps view, so that it aligns with the hotseat.
-     */
-    float getScrollRangeDelta(Rect insets);
-
-    /**
-     * Called as part of state transition to update the content UI
-     */
-    void setContentVisibility(int visibleElements, PropertySetter setter,
-            Interpolator interpolator);
-
-    /**
-     * Returns true if the QSB should be visible for the given set of visible elements
-     */
-    default boolean isQsbVisible(int visibleElements) {
-        return (visibleElements & ALL_APPS_HEADER) != 0;
-    }
-
-    /**
-     * Called to control how the search UI result should be handled.
-     *
-     * @param isEnabled when {@code true}, the search is all handled inside AOSP
-     *                  and is not overlayable.
-     * @return the searchbox edit text object
+     * @return the edit text object
      */
     @Nullable
-    EditText setTextSearchEnabled(boolean isEnabled);
+    ExtendedEditText getEditText();
+
+    /**
+     * 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
new file mode 100644
index 0000000..13444dd
--- /dev/null
+++ b/src/com/android/launcher3/allapps/WorkAdapterProvider.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.allapps;
+
+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;
+
+/**
+ * A UI expansion wrapper providing for providing work profile specific views
+ */
+public class WorkAdapterProvider extends BaseAdapterProvider {
+
+    public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
+
+    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;
+    }
+
+    @Override
+    public void onBindView(AllAppsGridAdapter.ViewHolder holder, int position) {
+        if (holder.itemView instanceof WorkEduCard) {
+            ((WorkEduCard) holder.itemView).setPosition(position);
+        }
+    }
+
+    @Override
+    public AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater,
+            ViewGroup parent, int viewType) {
+        int viewId = viewType == VIEW_TYPE_WORK_DISABLED_CARD ? R.layout.work_apps_paused
+                : R.layout.work_apps_edu;
+        return new AllAppsGridAdapter.ViewHolder(layoutInflater.inflate(viewId, parent, false));
+    }
+
+    /**
+     * returns whether or not work apps should be visible in work tab.
+     */
+    public boolean shouldShowWorkApps() {
+        return mEnabled;
+    }
+
+    /**
+     * Adds work profile specific adapter items to adapterItems and returns number of items added
+     */
+    public int addWorkItems(ArrayList<AllAppsGridAdapter.AdapterItem> adapterItems) {
+        if (!mEnabled) {
+            //add disabled card here.
+            AllAppsGridAdapter.AdapterItem disabledCard = new AllAppsGridAdapter.AdapterItem();
+            disabledCard.viewType = VIEW_TYPE_WORK_DISABLED_CARD;
+            adapterItems.add(disabledCard);
+        } else if (!isEduSeen()) {
+            AllAppsGridAdapter.AdapterItem eduCard = new AllAppsGridAdapter.AdapterItem();
+            eduCard.viewType = VIEW_TYPE_WORK_EDU_CARD;
+            adapterItems.add(eduCard);
+        }
+
+        return adapterItems.size();
+    }
+
+    /**
+     * Sets the current state of work profile
+     */
+    public void updateCurrentState(boolean isEnabled) {
+        mEnabled = isEnabled;
+        mRefreshCB.run();
+    }
+
+    @Override
+    public boolean isViewSupported(int viewType) {
+        return viewType == VIEW_TYPE_WORK_DISABLED_CARD || viewType == VIEW_TYPE_WORK_EDU_CARD;
+    }
+
+    @Override
+    public int getItemsPerRow(int viewType, int appsPerRow) {
+        return 1;
+    }
+
+    private boolean isEduSeen() {
+        return Utilities.getPrefs(mLauncher).getInt(KEY_WORK_EDU_STEP, 0) != 0;
+    }
+}
diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java
new file mode 100644
index 0000000..9db7bf0
--- /dev/null
+++ b/src/com/android/launcher3/allapps/WorkEduCard.java
@@ -0,0 +1,111 @@
+/*
+ * 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.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+
+/**
+ * Work profile toggle switch shown at the bottom of AllApps work tab
+ */
+public class WorkEduCard extends FrameLayout implements View.OnClickListener,
+        Animation.AnimationListener {
+
+    private final Launcher mLauncher;
+    Animation mDismissAnim;
+    private int mPosition = -1;
+
+    public WorkEduCard(Context context) {
+        this(context, null, 0);
+    }
+
+    public WorkEduCard(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WorkEduCard(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(getContext());
+        mDismissAnim = AnimationUtils.loadAnimation(context, android.R.anim.fade_out);
+        mDismissAnim.setDuration(500);
+        mDismissAnim.setAnimationListener(this);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mDismissAnim.reset();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mDismissAnim.cancel();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        findViewById(R.id.action_btn).setOnClickListener(this);
+        MarginLayoutParams lp = ((MarginLayoutParams) findViewById(R.id.wrapper).getLayoutParams());
+        lp.width = mLauncher.getAppsView().getActiveRecyclerView().getTabWidth();
+    }
+
+    @Override
+    public void onClick(View view) {
+        startAnimation(mDismissAnim);
+        mLauncher.getSharedPrefs().edit().putInt(WorkAdapterProvider.KEY_WORK_EDU_STEP, 1).apply();
+    }
+
+    @Override
+    public void onAnimationEnd(Animation animation) {
+        removeCard();
+    }
+
+    @Override
+    public void onAnimationRepeat(Animation animation) {
+
+    }
+
+    @Override
+    public void onAnimationStart(Animation animation) {
+
+    }
+
+    private void removeCard() {
+        if (mPosition == -1) {
+            if (getParent() != null) ((ViewGroup) getParent()).removeView(WorkEduCard.this);
+        } else {
+            AllAppsRecyclerView rv = mLauncher.getAppsView()
+                    .mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView;
+            rv.getApps().getAdapterItems().remove(mPosition);
+            rv.getAdapter().notifyItemRemoved(mPosition);
+        }
+    }
+
+    public void setPosition(int position) {
+        mPosition = position;
+    }
+
+}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 4567ee6..a800d34 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -15,108 +15,75 @@
  */
 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.content.SharedPreferences;
+import android.graphics.Insets;
 import android.graphics.Rect;
-import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-import android.widget.Switch;
+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.Insettable;
-import com.android.launcher3.R;
+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.views.ArrowTipView;
-
-import java.lang.ref.WeakReference;
 
 /**
  * Work profile toggle switch shown at the bottom of AllApps work tab
  */
-public class WorkModeSwitch extends Switch implements Insettable {
-
-    private static final int WORK_TIP_THRESHOLD = 2;
-    public static final String KEY_WORK_TIP_COUNTER = "worked_tip_counter";
+public class WorkModeSwitch extends Button implements Insettable, View.OnClickListener {
 
     private Rect mInsets = new Rect();
+    private boolean mWorkEnabled;
 
-    private final float[] mTouch = new float[2];
-    private int mTouchSlop;
+
+    @Nullable
+    private KeyboardInsetAnimationCallback mKeyboardInsetAnimationCallback;
 
     public WorkModeSwitch(Context context) {
-        super(context);
-        init();
+        this(context, null, 0);
     }
 
     public WorkModeSwitch(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init();
+        this(context, attrs, 0);
     }
 
     public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        init();
-    }
-
-    private void init() {
-        ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
-        mTouchSlop = viewConfiguration.getScaledTouchSlop();
     }
 
     @Override
-    public void setChecked(boolean checked) { }
-
-    @Override
-    public void toggle() {
-        // don't show tip if user uses toggle
-        Utilities.getPrefs(getContext()).edit().putInt(KEY_WORK_TIP_COUNTER, -1).apply();
-        trySetQuietModeEnabledToAllProfilesAsync(isChecked());
-    }
-
-    /**
-     * Sets the enabled or disabled state of the button
-     * @param isChecked
-     */
-    public void update(boolean isChecked) {
-        super.setChecked(isChecked);
-        setCompoundDrawablesRelativeWithIntrinsicBounds(
-                isChecked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
-        setEnabled(true);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mTouch[0] = ev.getX();
-            mTouch[1] = ev.getY();
-        } else if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
-            if (Math.abs(mTouch[0] - ev.getX()) > mTouchSlop
-                    || Math.abs(mTouch[1] - ev.getY()) > mTouchSlop) {
-                int action = ev.getAction();
-                ev.setAction(MotionEvent.ACTION_CANCEL);
-                super.onTouchEvent(ev);
-                ev.setAction(action);
-                return false;
-            }
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        setSelected(true);
+        setOnClickListener(this);
+        if (Utilities.ATLEAST_R) {
+            mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
+            setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
         }
-        return super.onTouchEvent(ev);
-    }
-
-    private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
-        new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
     }
 
     @Override
     public void setInsets(Rect insets) {
         int bottomInset = insets.bottom - mInsets.bottom;
         mInsets.set(insets);
-        setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
-                getPaddingBottom() + bottomInset);
+        ViewGroup.MarginLayoutParams marginLayoutParams =
+                (ViewGroup.MarginLayoutParams) getLayoutParams();
+        if (marginLayoutParams != null) {
+            marginLayoutParams.bottomMargin = bottomInset + marginLayoutParams.bottomMargin;
+        }
     }
 
     /**
@@ -125,78 +92,58 @@
     public void setWorkTabVisible(boolean workTabVisible) {
         clearAnimation();
         if (workTabVisible) {
-            setVisibility(VISIBLE);
+            setEnabled(true);
+            if (mWorkEnabled) {
+                setVisibility(VISIBLE);
+            }
             setAlpha(0);
             animate().alpha(1).start();
-            showTipIfNeeded();
         } else {
             animate().alpha(0).withEndAction(() -> this.setVisibility(GONE)).start();
         }
     }
 
-    private static final class SetQuietModeEnabledAsyncTask
-            extends AsyncTask<Void, Void, Boolean> {
-
-        private final boolean enabled;
-        private final WeakReference<WorkModeSwitch> switchWeakReference;
-
-        SetQuietModeEnabledAsyncTask(boolean enabled,
-                                     WeakReference<WorkModeSwitch> switchWeakReference) {
-            this.enabled = enabled;
-            this.switchWeakReference = switchWeakReference;
-        }
-
-        @Override
-        protected void onPreExecute() {
-            super.onPreExecute();
-            WorkModeSwitch workModeSwitch = switchWeakReference.get();
-            if (workModeSwitch != null) {
-                workModeSwitch.setEnabled(false);
-            }
-        }
-
-        @Override
-        protected Boolean doInBackground(Void... voids) {
-            WorkModeSwitch workModeSwitch = switchWeakReference.get();
-            if (workModeSwitch == null || !Utilities.ATLEAST_P) {
-                return false;
-            }
-
-            Context context = workModeSwitch.getContext();
-            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);
-            }
-            return showConfirm;
-        }
-
-        @Override
-        protected void onPostExecute(Boolean showConfirm) {
-            if (showConfirm) {
-                WorkModeSwitch workModeSwitch = switchWeakReference.get();
-                if (workModeSwitch != null) {
-                    workModeSwitch.setEnabled(true);
-                }
-            }
+    @Override
+    public void onClick(View view) {
+        if (Utilities.ATLEAST_P) {
+            setEnabled(false);
+            Launcher.fromContext(getContext()).getStatsLogManager().logger().log(
+                    LAUNCHER_TURN_OFF_WORK_APPS_TAP);
+            UI_HELPER_EXECUTOR.post(() -> setWorkProfileEnabled(getContext(), false));
         }
     }
 
     /**
-     * Shows a work tip on the Nth work tab open
+     * Sets the enabled or disabled state of the button
      */
-    public void showTipIfNeeded() {
-        Context context = getContext();
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        int tipCounter = prefs.getInt(KEY_WORK_TIP_COUNTER, WORK_TIP_THRESHOLD);
-        if (tipCounter < 0) return;
-        if (tipCounter == 0) {
-            new ArrowTipView(context)
-                    .show(context.getString(R.string.work_switch_tip), getTop());
+    public void updateCurrentState(boolean active) {
+        mWorkEnabled = active;
+        setEnabled(true);
+        setVisibility(active ? VISIBLE : GONE);
+    }
+
+    @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);
         }
-        prefs.edit().putInt(KEY_WORK_TIP_COUNTER, tipCounter - 1).apply();
+        return showConfirm;
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        if (Utilities.ATLEAST_R) {
+            setTranslationY(0);
+            if (insets.isVisible(WindowInsets.Type.ime())) {
+                Insets keyboardInsets = insets.getInsets(WindowInsets.Type.ime());
+                setTranslationY(mInsets.bottom - keyboardInsets.bottom);
+            }
+        }
+        return insets;
     }
 }
diff --git a/src/com/android/launcher3/allapps/WorkPausedCard.java b/src/com/android/launcher3/allapps/WorkPausedCard.java
new file mode 100644
index 0000000..7908b63
--- /dev/null
+++ b/src/com/android/launcher3/allapps/WorkPausedCard.java
@@ -0,0 +1,77 @@
+/*
+ * 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.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;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+/**
+ * Work profile toggle switch shown at the bottom of AllApps work tab
+ */
+public class WorkPausedCard extends LinearLayout implements View.OnClickListener {
+
+    private final Launcher mLauncher;
+    private Button mBtn;
+
+    public WorkPausedCard(Context context) {
+        this(context, null, 0);
+    }
+
+    public WorkPausedCard(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WorkPausedCard(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(getContext());
+    }
+
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBtn = findViewById(R.id.enable_work_apps);
+        mBtn.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (Utilities.ATLEAST_P) {
+            setEnabled(false);
+            mLauncher.getStatsLogManager().logger().log(LAUNCHER_TURN_ON_WORK_APPS_TAP);
+            UI_HELPER_EXECUTOR.post(() -> WorkModeSwitch.setWorkProfileEnabled(getContext(), true));
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int orientation = getResources().getConfiguration().orientation;
+        getLayoutParams().height = orientation == Configuration.ORIENTATION_PORTRAIT
+                ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
+        super.onLayout(changed, l, t, r, b);
+    }
+}
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index ed45749..79718fb 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.allapps.search;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME;
+
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -27,12 +29,12 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.PackageManagerHelper;
-
-import java.util.ArrayList;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.search.SearchAlgorithm;
+import com.android.launcher3.search.SearchCallback;
 
 /**
  * An interface to a search box that AllApps can command.
@@ -42,22 +44,23 @@
         OnFocusChangeListener {
 
     protected BaseDraggingActivity mLauncher;
-    protected Callbacks mCb;
+    protected SearchCallback<AdapterItem> mCallback;
     protected ExtendedEditText mInput;
     protected String mQuery;
 
-    protected SearchAlgorithm mSearchAlgorithm;
+    protected SearchAlgorithm<AdapterItem> mSearchAlgorithm;
 
     public void setVisibility(int visibility) {
         mInput.setVisibility(visibility);
     }
+
     /**
      * Sets the references to the apps model and the search result callback.
      */
     public final void initialize(
-            SearchAlgorithm searchAlgorithm, ExtendedEditText input,
-            BaseDraggingActivity launcher, Callbacks cb) {
-        mCb = cb;
+            SearchAlgorithm<AdapterItem> searchAlgorithm, ExtendedEditText input,
+            BaseDraggingActivity launcher, SearchCallback<AdapterItem> callback) {
+        mCallback = callback;
         mLauncher = launcher;
 
         mInput = input;
@@ -69,7 +72,7 @@
     }
 
     @Override
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
         // Do nothing
     }
 
@@ -83,10 +86,10 @@
         mQuery = s.toString();
         if (mQuery.isEmpty()) {
             mSearchAlgorithm.cancel(true);
-            mCb.clearSearchResult();
+            mCallback.clearSearchResult();
         } else {
             mSearchAlgorithm.cancel(false);
-            mSearchAlgorithm.doSearch(mQuery, mCb);
+            mSearchAlgorithm.doSearch(mQuery, mCallback);
         }
     }
 
@@ -96,24 +99,19 @@
         }
         // If play store continues auto updating an app, we want to show partial result.
         mSearchAlgorithm.cancel(false);
-        mSearchAlgorithm.doSearch(mQuery, mCb);
+        mSearchAlgorithm.doSearch(mQuery, mCallback);
     }
 
     @Override
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-        // Skip if it's not the right action
-        if (actionId != EditorInfo.IME_ACTION_SEARCH) {
-            return false;
-        }
 
-        // Skip if the query is empty
-        String query = v.getText().toString();
-        if (query.isEmpty()) {
-            return false;
+        if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
+            mLauncher.getStatsLogManager().logger()
+                    .log(LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME);
+            // selectFocusedView should return SearchTargetEvent that is passed onto onClick
+            return Launcher.getLauncher(mLauncher).getAppsView().launchHighlightedItem();
         }
-        return mLauncher.startActivitySafely(v,
-                PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null,
-                AppLaunchTracker.CONTAINER_SEARCH);
+        return false;
     }
 
     @Override
@@ -129,7 +127,7 @@
 
     @Override
     public void onFocusChange(View view, boolean hasFocus) {
-        if (!hasFocus) {
+        if (!hasFocus && !FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
             mInput.hideKeyboard();
         }
     }
@@ -138,7 +136,7 @@
      * Resets the search bar state.
      */
     public void reset() {
-        mCb.clearSearchResult();
+        mCallback.clearSearchResult();
         mInput.reset();
         mQuery = null;
     }
@@ -156,23 +154,4 @@
     public boolean isSearchFieldFocused() {
         return mInput.isFocused();
     }
-
-    /**
-     * Callback for getting search results.
-     */
-    public interface Callbacks {
-
-        /**
-         * Called when the search is complete.
-         *
-         * @param apps sorted list of matching components or null if in case of failure.
-         */
-        void onSearchResult(String query, ArrayList<ComponentKey> apps);
-
-        /**
-         * Called when the search results should be cleared.
-         */
-        void clearSearchResult();
-    }
-
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 356c52c..2491217 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -31,8 +31,6 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
-import android.view.animation.Interpolator;
-import android.widget.EditText;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
@@ -40,11 +38,11 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AlphabeticalAppsList;
 import com.android.launcher3.allapps.SearchUiManager;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.search.SearchCallback;
 
 import java.util.ArrayList;
 
@@ -52,7 +50,7 @@
  * Layout to contain the All-apps search UI.
  */
 public class AppsSearchContainerLayout extends ExtendedEditText
-        implements SearchUiManager, AllAppsSearchBarController.Callbacks,
+        implements SearchUiManager, SearchCallback<AdapterItem>,
         AllAppsStore.OnUpdateListener, Insettable {
 
     private final BaseDraggingActivity mLauncher;
@@ -107,7 +105,8 @@
         int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft()
                 - mAppsView.getActiveRecyclerView().getPaddingRight();
 
-        int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.inv.numHotseatIcons);
+        int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.cellLayoutBorderSpacingPx,
+                dp.numShownHotseatIcons);
         int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx);
         int iconPadding = cellWidth - iconVisibleSize;
 
@@ -131,11 +130,12 @@
     }
 
     @Override
-    public void initialize(AllAppsContainerView appsView) {
+    public void initializeSearch(AllAppsContainerView appsView) {
         mApps = appsView.getApps();
         mAppsView = appsView;
         mSearchBarController.initialize(
-                new DefaultAppSearchAlgorithm(mApps.getApps()), this, mLauncher, this);
+                new DefaultAppSearchAlgorithm(mLauncher),
+                this, mLauncher, this);
     }
 
     @Override
@@ -168,17 +168,25 @@
     }
 
     @Override
-    public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
-        if (apps != null) {
-            mApps.setOrderedFilter(apps);
+    public void onSearchResult(String query, ArrayList<AdapterItem> items) {
+        if (items != null) {
+            mApps.setSearchResults(items);
             notifyResultChanged();
             mAppsView.setLastSearchQuery(query);
         }
     }
 
     @Override
+    public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
+        if (items != null) {
+            mApps.appendSearchResults(items);
+            notifyResultChanged();
+        }
+    }
+
+    @Override
     public void clearSearchResult() {
-        if (mApps.setOrderedFilter(null)) {
+        if (mApps.setSearchResults(null)) {
             notifyResultChanged();
         }
 
@@ -201,22 +209,7 @@
     }
 
     @Override
-    public float getScrollRangeDelta(Rect insets) {
-        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
-            return 0;
-        } else {
-            return insets.bottom + insets.top;
-        }
-    }
-
-    @Override
-    public void setContentVisibility(int visibleElements, PropertySetter setter,
-            Interpolator interpolator) {
-        setter.setViewAlpha(this, isQsbVisible(visibleElements) ? 1 : 0, interpolator);
-    }
-
-    @Override
-    public EditText setTextSearchEnabled(boolean isEnabled) {
+    public ExtendedEditText getEditText() {
         return this;
     }
 }
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index bcb5427..1f854c6 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -15,26 +15,39 @@
  */
 package com.android.launcher3.allapps.search;
 
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Context;
 import android.os.Handler;
 
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.util.ComponentKey;
+import androidx.annotation.AnyThread;
 
-import java.text.Collator;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.search.SearchAlgorithm;
+import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.search.StringMatcherUtility;
+
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * The default search implementation.
  */
-public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
+public class DefaultAppSearchAlgorithm implements SearchAlgorithm<AdapterItem> {
 
-    private final List<AppInfo> mApps;
-    protected final Handler mResultHandler;
+    private static final int MAX_RESULTS_COUNT = 5;
 
-    public DefaultAppSearchAlgorithm(List<AppInfo> apps) {
-        mApps = apps;
-        mResultHandler = new Handler();
+    private final LauncherAppState mAppState;
+    private final Handler mResultHandler;
+
+    public DefaultAppSearchAlgorithm(Context context) {
+        mAppState = LauncherAppState.getInstance(context);
+        mResultHandler = new Handler(MAIN_EXECUTOR.getLooper());
     }
 
     @Override
@@ -45,157 +58,38 @@
     }
 
     @Override
-    public void doSearch(final String query,
-            final AllAppsSearchBarController.Callbacks callback) {
-        final ArrayList<ComponentKey> result = getTitleMatchResult(query);
-        mResultHandler.post(new Runnable() {
-
+    public void doSearch(String query, SearchCallback<AdapterItem> callback) {
+        mAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
             @Override
-            public void run() {
-                callback.onSearchResult(query, result);
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+                ArrayList<AdapterItem> result = getTitleMatchResult(apps.data, query);
+                mResultHandler.post(() -> callback.onSearchResult(query, result));
             }
         });
     }
 
-    private ArrayList<ComponentKey> getTitleMatchResult(String query) {
+    /**
+     * Filters {@link AppInfo}s matching specified query
+     */
+    @AnyThread
+    public static ArrayList<AdapterItem> getTitleMatchResult(List<AppInfo> apps, String query) {
         // Do an intersection of the words in the query and each title, and filter out all the
         // apps that don't match all of the words in the query.
         final String queryTextLower = query.toLowerCase();
-        final ArrayList<ComponentKey> result = new ArrayList<>();
-        StringMatcher matcher = StringMatcher.getInstance();
-        for (AppInfo info : mApps) {
-            if (matches(info, queryTextLower, matcher)) {
-                result.add(info.toComponentKey());
+        final ArrayList<AdapterItem> result = new ArrayList<>();
+        StringMatcherUtility.StringMatcher matcher =
+                StringMatcherUtility.StringMatcher.getInstance();
+
+        int resultCount = 0;
+        int total = apps.size();
+        for (int i = 0; i < total && resultCount < MAX_RESULTS_COUNT; i++) {
+            AppInfo info = apps.get(i);
+            if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) {
+                AdapterItem appItem = AdapterItem.asApp(resultCount, "", info, resultCount);
+                result.add(appItem);
+                resultCount++;
             }
         }
         return result;
     }
-
-    public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
-        int queryLength = query.length();
-
-        String title = info.title.toString();
-        int titleLength = title.length();
-
-        if (titleLength < queryLength || queryLength <= 0) {
-            return false;
-        }
-
-        if (requestSimpleFuzzySearch(query)) {
-            return title.toLowerCase().contains(query);
-        }
-
-        int lastType;
-        int thisType = Character.UNASSIGNED;
-        int nextType = Character.getType(title.codePointAt(0));
-
-        int end = titleLength - queryLength;
-        for (int i = 0; i <= end; i++) {
-            lastType = thisType;
-            thisType = nextType;
-            nextType = i < (titleLength - 1) ?
-                    Character.getType(title.codePointAt(i + 1)) : Character.UNASSIGNED;
-            if (isBreak(thisType, lastType, nextType) &&
-                    matcher.matches(query, title.substring(i, i + queryLength))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the current point should be a break point. Following cases
-     * are considered as break points:
-     *      1) Any non space character after a space character
-     *      2) Any digit after a non-digit character
-     *      3) Any capital character after a digit or small character
-     *      4) Any capital character before a small character
-     */
-    private static boolean isBreak(int thisType, int prevType, int nextType) {
-        switch (prevType) {
-            case Character.UNASSIGNED:
-            case Character.SPACE_SEPARATOR:
-            case Character.LINE_SEPARATOR:
-            case Character.PARAGRAPH_SEPARATOR:
-                return true;
-        }
-        switch (thisType) {
-            case Character.UPPERCASE_LETTER:
-                if (nextType == Character.UPPERCASE_LETTER) {
-                    return true;
-                }
-                // Follow through
-            case Character.TITLECASE_LETTER:
-                // Break point if previous was not a upper case
-                return prevType != Character.UPPERCASE_LETTER;
-            case Character.LOWERCASE_LETTER:
-                // Break point if previous was not a letter.
-                return prevType > Character.OTHER_LETTER || prevType <= Character.UNASSIGNED;
-            case Character.DECIMAL_DIGIT_NUMBER:
-            case Character.LETTER_NUMBER:
-            case Character.OTHER_NUMBER:
-                // Break point if previous was not a number
-                return !(prevType == Character.DECIMAL_DIGIT_NUMBER
-                        || prevType == Character.LETTER_NUMBER
-                        || prevType == Character.OTHER_NUMBER);
-            case Character.MATH_SYMBOL:
-            case Character.CURRENCY_SYMBOL:
-            case Character.OTHER_PUNCTUATION:
-            case Character.DASH_PUNCTUATION:
-                // Always a break point for a symbol
-                return true;
-            default:
-                return  false;
-        }
-    }
-
-    public static class StringMatcher {
-
-        private static final char MAX_UNICODE = '\uFFFF';
-
-        private final Collator mCollator;
-
-        StringMatcher() {
-            // On android N and above, Collator uses ICU implementation which has a much better
-            // support for non-latin locales.
-            mCollator = Collator.getInstance();
-            mCollator.setStrength(Collator.PRIMARY);
-            mCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
-        }
-
-        /**
-         * Returns true if {@param query} is a prefix of {@param target}
-         */
-        public boolean matches(String query, String target) {
-            switch (mCollator.compare(query, target)) {
-                case 0:
-                    return true;
-                case -1:
-                    // The target string can contain a modifier which would make it larger than
-                    // the query string (even though the length is same). If the query becomes
-                    // larger after appending a unicode character, it was originally a prefix of
-                    // the target string and hence should match.
-                    return mCollator.compare(query + MAX_UNICODE, target) > -1;
-                default:
-                    return false;
-            }
-        }
-
-        public static StringMatcher getInstance() {
-            return new StringMatcher();
-        }
-    }
-
-    private static boolean requestSimpleFuzzySearch(String s) {
-        for (int i = 0; i < s.length(); ) {
-            int codepoint = s.codePointAt(i);
-            i += Character.charCount(codepoint);
-            switch (Character.UnicodeScript.of(codepoint)) {
-                case HAN:
-                    //Character.UnicodeScript.HAN: use String.contains to match
-                    return true;
-            }
-        }
-        return false;
-    }
 }
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
new file mode 100644
index 0000000..7abd555
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps.search;
+
+import android.graphics.Canvas;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.model.data.ItemInfo;
+
+/**
+ * Provides views for local search results
+ */
+public class DefaultSearchAdapterProvider extends SearchAdapterProvider {
+
+    private final RecyclerView.ItemDecoration mDecoration;
+    private View mHighlightedView;
+
+    public DefaultSearchAdapterProvider(BaseDraggingActivity launcher,
+            AllAppsContainerView appsContainerView) {
+        super(launcher, appsContainerView);
+        mDecoration = new RecyclerView.ItemDecoration() {
+            @Override
+            public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent,
+                    @NonNull RecyclerView.State state) {
+                super.onDraw(c, parent, state);
+            }
+        };
+    }
+
+    @Override
+    public void onBindView(AllAppsGridAdapter.ViewHolder holder, int position) {
+        if (position == 0) {
+            mHighlightedView = holder.itemView;
+        }
+    }
+
+    @Override
+    public boolean isViewSupported(int viewType) {
+        return false;
+    }
+
+    @Override
+    public AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater,
+            ViewGroup parent, int viewType) {
+        return null;
+    }
+
+    @Override
+    public boolean launchHighlightedItem() {
+        if (mHighlightedView instanceof BubbleTextView
+                && mHighlightedView.getTag() instanceof ItemInfo) {
+            ItemInfo itemInfo = (ItemInfo) mHighlightedView.getTag();
+            return mLauncher.startActivitySafely(mHighlightedView, itemInfo.getIntent(), itemInfo);
+        }
+        return false;
+    }
+
+    @Override
+    public View getHighlightedItem() {
+        return mHighlightedView;
+    }
+
+    @Override
+    public RecyclerView.ItemDecoration getDecorator() {
+        return mDecoration;
+    }
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
new file mode 100644
index 0000000..7af0406
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -0,0 +1,60 @@
+/*
+ * 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.search;
+
+import android.net.Uri;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.BaseAdapterProvider;
+
+/**
+ * A UI expansion wrapper providing for search results
+ */
+public abstract class SearchAdapterProvider extends BaseAdapterProvider {
+
+    protected final BaseDraggingActivity mLauncher;
+
+    public SearchAdapterProvider(BaseDraggingActivity launcher, AllAppsContainerView appsView) {
+        mLauncher = launcher;
+    }
+
+    /**
+     * Called from LiveSearchManager to notify slice status updates.
+     */
+    public void onSliceStatusUpdate(Uri sliceUri) {
+    }
+
+    /**
+     * Handles selection event on search adapter item. Returns false if provider can not handle
+     * event
+     */
+    public abstract boolean launchHighlightedItem();
+
+    /**
+     * Returns the current highlighted view
+     */
+    public abstract View getHighlightedItem();
+
+    /**
+     * Returns the item decorator.
+     */
+    public abstract RecyclerView.ItemDecoration getDecorator();
+}
diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java
index eabd283..8dad1b4 100644
--- a/src/com/android/launcher3/anim/AlphaUpdateListener.java
+++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.anim;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.view.View;
@@ -25,7 +26,7 @@
 /**
  * A convenience class to update a view's visibility state after an alpha animation.
  */
-public class AlphaUpdateListener extends AnimationSuccessListener
+public class AlphaUpdateListener extends AnimatorListenerAdapter
         implements AnimatorUpdateListener {
     public static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
 
@@ -41,7 +42,7 @@
     }
 
     @Override
-    public void onAnimationSuccess(Animator animator) {
+    public void onAnimationEnd(Animator animator) {
         updateVisibility(mView);
     }
 
diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java
index 9905e81..a312070 100644
--- a/src/com/android/launcher3/anim/AnimationSuccessListener.java
+++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java
@@ -40,24 +40,4 @@
 
     public abstract void onAnimationSuccess(Animator animator);
 
-    /**
-     * Returns an AnimationSuccessListener which runs the provided action on success
-     */
-    public static AnimationSuccessListener forRunnable(Runnable r) {
-        return new RunnableSuccessListener(r);
-    }
-
-    private static class RunnableSuccessListener extends AnimationSuccessListener {
-
-        private final Runnable mRunnable;
-
-        private RunnableSuccessListener(Runnable r) {
-            mRunnable = r;
-        }
-
-        @Override
-        public void onAnimationSuccess(Animator animator) {
-            mRunnable.run();
-        }
-    }
 }
diff --git a/src/com/android/launcher3/anim/AnimatorListeners.java b/src/com/android/launcher3/anim/AnimatorListeners.java
new file mode 100644
index 0000000..d9046b9
--- /dev/null
+++ b/src/com/android/launcher3/anim/AnimatorListeners.java
@@ -0,0 +1,100 @@
+/*
+ * 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.anim;
+
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import java.util.function.Consumer;
+
+/**
+ * Utility class for creating common {@link AnimatorListener}
+ */
+public class AnimatorListeners {
+
+    /**
+     * Returns an AnimatorListener which executes the callback on successful animation completion
+     */
+    public static AnimatorListener forSuccessCallback(Runnable callback) {
+        return new RunnableSuccessListener(callback);
+    }
+
+    /**
+     * Returns an AnimatorListener which executes the callback on animation completion,
+     * with the boolean representing success
+     */
+    public static AnimatorListener forEndCallback(Consumer<Boolean> callback) {
+        return new EndStateCallbackWrapper(callback);
+    }
+
+    /**
+     * Returns an AnimatorListener which executes the callback on animation completion
+     */
+    public static AnimatorListener forEndCallback(Runnable callback) {
+        return new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                callback.run();
+            }
+        };
+    }
+
+    private static class EndStateCallbackWrapper extends AnimatorListenerAdapter {
+
+        private final Consumer<Boolean> mListener;
+        private boolean mListenerCalled = false;
+
+        EndStateCallbackWrapper(Consumer<Boolean> listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (!mListenerCalled) {
+                mListenerCalled = true;
+                mListener.accept(false);
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator anim) {
+            if (!mListenerCalled) {
+                mListenerCalled = true;
+                mListener.accept(anim instanceof ValueAnimator
+                        ? ((ValueAnimator) anim).getAnimatedFraction() > SUCCESS_TRANSITION_PROGRESS
+                        : true);
+            }
+        }
+    }
+
+    private static class RunnableSuccessListener extends AnimationSuccessListener {
+
+        private final Runnable mRunnable;
+
+        private RunnableSuccessListener(Runnable r) {
+            mRunnable = r;
+        }
+
+        @Override
+        public void onAnimationSuccess(Animator animator) {
+            mRunnable.run();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index ea0ff8b..85ca280 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -19,7 +19,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
@@ -29,7 +29,7 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 
-import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -72,7 +72,6 @@
     private Runnable mEndAction;
 
     protected boolean mTargetCancelled = false;
-    protected Runnable mOnCancelRunnable;
 
     /** package private */
     AnimatorPlaybackController(AnimatorSet anim, long duration, ArrayList<Holder> childAnims) {
@@ -88,16 +87,11 @@
             @Override
             public void onAnimationCancel(Animator animation) {
                 mTargetCancelled = true;
-                if (mOnCancelRunnable != null) {
-                    mOnCancelRunnable.run();
-                    mOnCancelRunnable = null;
-                }
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
                 mTargetCancelled = false;
-                mOnCancelRunnable = null;
             }
 
             @Override
@@ -141,15 +135,20 @@
 
     /**
      * Starts playing the animation with the provided velocity optionally playing any
-     * physics based animations
+     * physics based animations.
+     * @param goingToEnd Whether we are going to the end (progress = 1) or not (progress = 0).
+     * @param velocityPxPerMs The velocity at which to start the animation, in pixels / millisecond.
+     * @param endDistance The distance (pixels) that the animation will travel from progress 0 to 1.
+     * @param animationDuration The duration of the non-physics based animation.
      */
     public void startWithVelocity(Context context, boolean goingToEnd,
-            float velocity, float scale, long animationDuration) {
-        float scaleInverse = 1 / Math.abs(scale);
-        float scaledVelocity = velocity * scaleInverse;
+            float velocityPxPerMs, float endDistance, long animationDuration) {
+        float distanceInverse = 1 / Math.abs(endDistance);
+        float velocityProgressPerMs = velocityPxPerMs * distanceInverse;
 
+        float oneFrameProgress = velocityProgressPerMs * getSingleFrameMs(context);
         float nextFrameProgress = boundToRange(getProgressFraction()
-                + scaledVelocity * getSingleFrameMs(context), 0f, 1f);
+                + oneFrameProgress, 0f, 1f);
 
         // Update setters for spring
         int springFlag = goingToEnd
@@ -162,8 +161,8 @@
                 SpringAnimationBuilder s = new SpringAnimationBuilder(context)
                         .setStartValue(mCurrentFraction)
                         .setEndValue(goingToEnd ? 1 : 0)
-                        .setStartVelocity(scaledVelocity)
-                        .setMinimumVisibleChange(scaleInverse)
+                        .setStartVelocity(velocityProgressPerMs)
+                        .setMinimumVisibleChange(distanceInverse)
                         .setDampingRatio(h.springProperty.mDampingRatio)
                         .setStiffness(h.springProperty.mStiffness)
                         .computeParams();
@@ -172,8 +171,18 @@
                 springDuration = Math.max(expectedDurationL, springDuration);
 
                 float expectedDuration = expectedDurationL;
-                h.mapper = (progress, globalEndProgress) ->
-                        mAnimationPlayer.getCurrentPlayTime() / expectedDuration;
+                h.mapper = (progress, globalEndProgress) -> {
+                    if (expectedDuration <= 0 || oneFrameProgress >= 1) {
+                        return 1;
+                    } else {
+                        // Start from one frame ahead of the current position.
+                        return Utilities.mapToRange(
+                                mAnimationPlayer.getCurrentPlayTime() / expectedDuration,
+                                0, 1,
+                                Math.abs(oneFrameProgress), 1,
+                                LINEAR);
+                    }
+                };
                 h.anim.setInterpolator(s::getInterpolatedValue);
             }
         }
@@ -182,7 +191,7 @@
 
         if (springDuration <= animationDuration) {
             mAnimationPlayer.setDuration(animationDuration);
-            mAnimationPlayer.setInterpolator(scrollInterpolatorForVelocity(velocity));
+            mAnimationPlayer.setInterpolator(scrollInterpolatorForVelocity(velocityPxPerMs));
         } else {
             // Since spring requires more time to run, we let the other animations play with
             // current time and interpolation and by clamping the duration.
@@ -190,7 +199,7 @@
 
             float cutOff = animationDuration / (float) springDuration;
             mAnimationPlayer.setInterpolator(
-                    clampToProgress(scrollInterpolatorForVelocity(velocity), 0, cutOff));
+                    clampToProgress(scrollInterpolatorForVelocity(velocityPxPerMs), 0, cutOff));
         }
         mAnimationPlayer.start();
     }
@@ -269,46 +278,29 @@
         }
     }
 
-    /** @see #dispatchOnCancelWithoutCancelRunnable(Runnable) */
-    public void dispatchOnCancelWithoutCancelRunnable() {
-        dispatchOnCancelWithoutCancelRunnable(null);
-    }
-
-    /**
-     * Sets mOnCancelRunnable = null before dispatching the cancel and restoring the runnable. This
-     * is intended to be used only if you need to cancel but want to defer cleaning up yourself.
-     * @param callback An optional callback to run after dispatching the cancel but before resetting
-     *                 the onCancelRunnable.
-     */
-    public void dispatchOnCancelWithoutCancelRunnable(@Nullable Runnable callback) {
-        Runnable onCancel = mOnCancelRunnable;
-        setOnCancelRunnable(null);
-        dispatchOnCancel();
-        if (callback != null) {
-            callback.run();
-        }
-        setOnCancelRunnable(onCancel);
-    }
-
-
-    public AnimatorPlaybackController setOnCancelRunnable(Runnable runnable) {
-        mOnCancelRunnable = runnable;
+    public AnimatorPlaybackController dispatchOnStart() {
+        callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationStart);
         return this;
     }
 
-    public void dispatchOnStart() {
-        callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationStart);
+    public AnimatorPlaybackController dispatchOnCancel() {
+        callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationCancel);
+        return this;
     }
 
-    public void dispatchOnCancel() {
-        callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationCancel);
+    public AnimatorPlaybackController dispatchOnEnd() {
+        callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationEnd);
+        return this;
     }
 
     public void dispatchSetInterpolator(TimeInterpolator interpolator) {
         callAnimatorCommandRecursively(mAnim, a -> a.setInterpolator(interpolator));
     }
 
-    private static void callListenerCommandRecursively(
+    /**
+     * Recursively calls a command on all the listeners of the provided animation
+     */
+    public static void callListenerCommandRecursively(
             Animator anim, BiConsumer<AnimatorListener, Animator> command) {
         callAnimatorCommandRecursively(anim, a-> {
             for (AnimatorListener l : nonNullList(a.getListeners())) {
@@ -343,7 +335,7 @@
         public void onAnimationSuccess(Animator animator) {
             // We wait for the spring (if any) to finish running before completing the end callback.
             if (!mDispatched) {
-                callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationEnd);
+                dispatchOnEnd();
                 if (mEndAction != null) {
                     mEndAction.run();
                 }
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 6ad43ea..1e7b224 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -16,9 +16,6 @@
 
 package com.android.launcher3.anim;
 
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
-
-import android.content.Context;
 import android.graphics.Path;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AccelerateInterpolator;
@@ -30,7 +27,6 @@
 
 import com.android.launcher3.Utilities;
 
-
 /**
  * Common interpolators used in Launcher
  */
@@ -39,6 +35,7 @@
     public static final Interpolator LINEAR = new LinearInterpolator();
 
     public static final Interpolator ACCEL = new AccelerateInterpolator();
+    public static final Interpolator ACCEL_0_5 = new AccelerateInterpolator(0.5f);
     public static final Interpolator ACCEL_0_75 = new AccelerateInterpolator(0.75f);
     public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f);
     public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2);
@@ -49,7 +46,6 @@
     public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2);
     public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f);
     public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f);
-    public static final Interpolator DEACCEL_5 = new DecelerateInterpolator(5f);
 
     public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator();
 
@@ -58,6 +54,9 @@
     public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f);
     public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1);
 
+    public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
+    public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
+
     public static final Interpolator EXAGGERATED_EASE;
 
     public static final Interpolator INSTANT = t -> 1;
@@ -67,9 +66,6 @@
      */
     public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1;
 
-    private static final int MIN_SETTLE_DURATION = 200;
-    private static final float OVERSHOOT_FACTOR = 0.9f;
-
     static {
         Path exaggeratedEase = new Path();
         exaggeratedEase.moveTo(0, 0);
@@ -83,6 +79,9 @@
 
     public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+    public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR_ACCEL_DEACCEL =
+            v -> ACCEL_DEACCEL.getInterpolation(TOUCH_RESPONSE_INTERPOLATOR.getInterpolation(v));
+
 
     /**
      * Inversion of ZOOM_OUT, compounded with an ease-out.
@@ -151,11 +150,15 @@
      */
     public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
             float upperBound) {
-        if (upperBound <= lowerBound) {
-            throw new IllegalArgumentException(String.format(
-                    "lowerBound (%f) must be less than upperBound (%f)", lowerBound, upperBound));
+        if (upperBound < lowerBound) {
+            throw new IllegalArgumentException(
+                    String.format("upperBound (%f) must be greater than lowerBound (%f)",
+                            upperBound, lowerBound));
         }
         return t -> {
+            if (t == lowerBound && t == upperBound) {
+                return t == 0f ? 0 : 1;
+            }
             if (t < lowerBound) {
                 return 0;
             }
@@ -175,76 +178,4 @@
             float upperBound) {
         return t -> Utilities.mapRange(interpolator.getInterpolation(t), lowerBound, upperBound);
     }
-
-    /**
-     * Computes parameters necessary for an overshoot effect.
-     */
-    public static class OvershootParams {
-        public Interpolator interpolator;
-        public float start;
-        public float end;
-        public long duration;
-
-        /**
-         * Given the input params, sets OvershootParams variables to be used by the caller.
-         * @param startProgress The progress from 0 to 1 that the overshoot starts from.
-         * @param overshootPastProgress The progress from 0 to 1 where we overshoot past (should
-         *        either be equal to startProgress or endProgress, depending on if we want to
-         *        overshoot immediately or only once we reach the end).
-         * @param endProgress The final progress from 0 to 1 that we will settle to.
-         * @param velocityPxPerMs The initial velocity that causes this overshoot.
-         * @param totalDistancePx The distance against which progress is calculated.
-         */
-        public OvershootParams(float startProgress, float overshootPastProgress,
-                float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) {
-            velocityPxPerMs = Math.abs(velocityPxPerMs);
-            overshootPastProgress = Math.max(overshootPastProgress, startProgress);
-            start = startProgress;
-            int startPx = (int) (start * totalDistancePx);
-            // Overshoot by about half a frame.
-            float overshootBy = OVERSHOOT_FACTOR * velocityPxPerMs *
-                    getSingleFrameMs(context) / totalDistancePx / 2;
-            overshootBy = Utilities.boundToRange(overshootBy, 0.02f, 0.15f);
-            end = overshootPastProgress + overshootBy;
-            int endPx = (int) (end  * totalDistancePx);
-            int overshootDistance = endPx - startPx;
-            // Calculate deceleration necessary to reach overshoot distance.
-            // Formula: velocityFinal^2 = velocityInitial^2 + 2 * acceleration * distance
-            //          0 = v^2 + 2ad (velocityFinal == 0)
-            //          a = v^2 / -2d
-            float decelerationPxPerMs = velocityPxPerMs * velocityPxPerMs / (2 * overshootDistance);
-            // Calculate time necessary to reach peak of overshoot.
-            // Formula: acceleration = velocity / time
-            //          time = velocity / acceleration
-            duration = (long) (velocityPxPerMs / decelerationPxPerMs);
-
-            // Now that we're at the top of the overshoot, need to settle back to endProgress.
-            float settleDistance = end - endProgress;
-            int settleDistancePx = (int) (settleDistance * totalDistancePx);
-            // Calculate time necessary for the settle.
-            // Formula: distance = velocityInitial * time + 1/2 * acceleration * time^2
-            //          d = 1/2at^2 (velocityInitial = 0, since we just stopped at the top)
-            //          t = sqrt(2d/a)
-            // Above formula assumes constant acceleration. Since we use ACCEL_DEACCEL, we actually
-            // have acceleration to halfway then deceleration the rest. So the formula becomes:
-            //          t = sqrt(d/a) * 2 (half the distance for accel, half for deaccel)
-            long settleDuration = (long) Math.sqrt(settleDistancePx / decelerationPxPerMs) * 4;
-
-            settleDuration = Math.max(MIN_SETTLE_DURATION, settleDuration);
-            // How much of the animation to devote to playing the overshoot (the rest is for settle).
-            float overshootFraction = (float) duration / (duration + settleDuration);
-            duration += settleDuration;
-            // Finally, create the interpolator, composed of two interpolators: an overshoot, which
-            // reaches end > 1, and then a settle to endProgress.
-            Interpolator overshoot = Interpolators.clampToProgress(DEACCEL, 0, overshootFraction);
-            // The settle starts at 1, where 1 is the top of the overshoot, and maps to a fraction
-            // such that final progress is endProgress. For example, if we overshot to 1.1 but want
-            // to end at 1, we need to map to 1/1.1.
-            Interpolator settle = Interpolators.clampToProgress(Interpolators.mapToProgress(
-                    ACCEL_DEACCEL, 1, (endProgress - start) / (end - start)), overshootFraction, 1);
-            interpolator = t -> t <= overshootFraction
-                    ? overshoot.getInterpolation(t)
-                    : settle.getInterpolation(t);
-        }
-    }
 }
diff --git a/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
new file mode 100644
index 0000000..ef4ada3
--- /dev/null
+++ b/src/com/android/launcher3/anim/KeyboardInsetAnimationCallback.java
@@ -0,0 +1,71 @@
+/*
+ * 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.anim;
+
+import android.os.Build;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.launcher3.Utilities;
+
+import java.util.List;
+
+/**
+ * Callback that animates views above the IME
+ */
+@RequiresApi(api = Build.VERSION_CODES.R)
+public class KeyboardInsetAnimationCallback extends WindowInsetsAnimation.Callback {
+    private final View mView;
+
+    private float mInitialTranslation;
+    private float mTerminalTranslation;
+
+    public KeyboardInsetAnimationCallback(View view) {
+        super(DISPATCH_MODE_STOP);
+        mView = view;
+    }
+
+    @Override
+    public void onPrepare(WindowInsetsAnimation animation) {
+        mInitialTranslation = mView.getTranslationY();
+    }
+
+
+    @Override
+    public WindowInsets onProgress(WindowInsets windowInsets, List<WindowInsetsAnimation> list) {
+        if (list.size() == 0) {
+            mView.setTranslationY(mInitialTranslation);
+            return windowInsets;
+        }
+        float progress = list.get(0).getInterpolatedFraction();
+
+        mView.setTranslationY(
+                Utilities.mapRange(progress, mInitialTranslation, mTerminalTranslation));
+
+        return windowInsets;
+    }
+
+    @Override
+    public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation,
+            WindowInsetsAnimation.Bounds bounds) {
+        mTerminalTranslation = mView.getTranslationY();
+        mView.setTranslationY(mInitialTranslation);
+        return super.onStart(animation, bounds);
+    }
+}
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 4195933..01f7de6 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.anim;
 
+import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
 import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
 
 import android.animation.Animator;
@@ -23,6 +24,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.graphics.drawable.ColorDrawable;
 import android.util.FloatProperty;
 import android.util.IntProperty;
 import android.view.View;
@@ -43,8 +45,6 @@
  */
 public class PendingAnimation implements PropertySetter {
 
-    private final ArrayList<Consumer<EndState>> mEndListeners = new ArrayList<>();
-
     private final ArrayList<Holder> mAnimHolders = new ArrayList<>();
     private final AnimatorSet mAnim;
     private final long mDuration;
@@ -73,13 +73,6 @@
         addAnimationHoldersRecur(a, mDuration, springProperty, mAnimHolders);
     }
 
-    public void finish(boolean isSuccess, int logAction) {
-        for (Consumer<EndState> listeners : mEndListeners) {
-            listeners.accept(new EndState(isSuccess, logAction));
-        }
-        mEndListeners.clear();
-    }
-
     @Override
     public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
         if (view == null || view.getAlpha() == alpha) {
@@ -92,6 +85,17 @@
     }
 
     @Override
+    public void setViewBackgroundColor(View view, int color, TimeInterpolator interpolator) {
+        if (view == null || (view.getBackground() instanceof ColorDrawable
+                && ((ColorDrawable) view.getBackground()).getColor() == color)) {
+            return;
+        }
+        ObjectAnimator anim = ObjectAnimator.ofArgb(view, VIEW_BACKGROUND_COLOR, color);
+        anim.setInterpolator(interpolator);
+        add(anim);
+    }
+
+    @Override
     public <T> void setFloat(T target, FloatProperty<T> property, float value,
             TimeInterpolator interpolator) {
         if (property.get(target) == value) {
@@ -124,11 +128,18 @@
      * Adds a callback to be run on every frame of the animation
      */
     public void addOnFrameCallback(Runnable runnable) {
+        addOnFrameListener(anim -> runnable.run());
+    }
+
+    /**
+     * Adds a listener to be run on every frame of the animation
+     */
+    public void addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener) {
         if (mProgressAnimator == null) {
             mProgressAnimator = ValueAnimator.ofFloat(0, 1);
         }
 
-        mProgressAnimator.addUpdateListener(anim -> runnable.run());
+        mProgressAnimator.addUpdateListener(listener);
     }
 
     /**
@@ -149,7 +160,7 @@
             mProgressAnimator = null;
         }
         if (mAnimHolders.isEmpty()) {
-            // Add a dummy animation to that the duration is respected
+            // Add a placeholder animation to that the duration is respected
             add(ValueAnimator.ofFloat(0, 1).setDuration(mDuration));
         }
         return mAnim;
@@ -163,21 +174,12 @@
     }
 
     /**
-     * Add a listener of receiving the end state.
-     * Note that the listeners are called as a result of calling {@link #finish(boolean, int)}
-     * and not automatically
+     * Add a listener of receiving the success/failure callback in the end.
      */
-    public void addEndListener(Consumer<EndState> listener) {
-        mEndListeners.add(listener);
-    }
-
-    public static class EndState {
-        public boolean isSuccess;
-        public int logAction;
-
-        public EndState(boolean isSuccess, int logAction) {
-            this.isSuccess = isSuccess;
-            this.logAction = logAction;
+    public void addEndListener(Consumer<Boolean> listener) {
+        if (mProgressAnimator == null) {
+            mProgressAnimator = ValueAnimator.ofFloat(0, 1);
         }
+        mProgressAnimator.addListener(AnimatorListeners.forEndCallback(listener));
     }
 }
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index 2ce620b..729523f 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -41,6 +41,15 @@
     }
 
     /**
+     * Sets the background color of the provided view using the provided interpolator.
+     */
+    default void setViewBackgroundColor(View view, int color, TimeInterpolator interpolator) {
+        if (view != null) {
+            view.setBackgroundColor(color);
+        }
+    }
+
+    /**
      * Updates the float property of the target using the provided interpolator
      */
     default <T> void setFloat(T target, FloatProperty<T> property, float value,
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index a9702b4..bd52158 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -25,7 +25,7 @@
 import androidx.annotation.FloatRange;
 import androidx.dynamicanimation.animation.SpringForce;
 
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
 
 /**
  * Utility class to build an object animator which follows the same path as a spring animation for
@@ -134,7 +134,7 @@
     }
 
     public SpringAnimationBuilder computeParams() {
-        int singleFrameMs = DefaultDisplay.getSingleFrameMs(mContext);
+        int singleFrameMs = DisplayController.getSingleFrameMs(mContext);
         double naturalFreq = Math.sqrt(mStiffness);
         double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
 
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 1d32d1d..30c3417 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -70,7 +70,8 @@
         final Bundle parcel = new Bundle();
         parcel.putInt(TestProtocol.STATE_FIELD, stateOrdinal);
 
-        sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
+        sendEventToTest(
+                accessibilityManager, context, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
         Log.d(TestProtocol.PERMANENT_DIAG_TAG, "sendStateEventToTest: " + stateOrdinal);
     }
 
@@ -78,22 +79,24 @@
         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
         if (accessibilityManager == null) return;
 
-        sendEventToTest(accessibilityManager, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
+        sendEventToTest(accessibilityManager, context, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
     }
 
     public static void sendPauseDetectedEventToTest(Context context) {
         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
         if (accessibilityManager == null) return;
 
-        sendEventToTest(accessibilityManager, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
+        sendEventToTest(accessibilityManager, context, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
     }
 
     private static void sendEventToTest(
-            AccessibilityManager accessibilityManager, String eventTag, Bundle data) {
+            AccessibilityManager accessibilityManager,
+            Context context, String eventTag, Bundle data) {
         final AccessibilityEvent e = AccessibilityEvent.obtain(
                 AccessibilityEvent.TYPE_ANNOUNCEMENT);
         e.setClassName(eventTag);
         e.setParcelableData(data);
+        e.setPackageName(context.getApplicationContext().getPackageName());
         accessibilityManager.sendAccessibilityEvent(e);
     }
 
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 88a9aba..f066dc1 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -72,15 +72,15 @@
             "PROMISE_APPS_NEW_INSTALLS", true,
             "Adds a promise icon to the home screen for new install sessions.");
 
-    public static final BooleanFlag APPLY_CONFIG_AT_RUNTIME = getDebugFlag(
-            "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
-
     public static final BooleanFlag QUICKSTEP_SPRINGS = getDebugFlag(
             "QUICKSTEP_SPRINGS", true, "Enable springs for quickstep animations");
 
     public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag(
             "UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations");
 
+    public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag(
+            "ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups.");
+
     public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(
             "KEYGUARD_ANIMATION", false, "Enable animation for keyguard going away on wallpaper");
 
@@ -88,11 +88,30 @@
             "ADAPTIVE_ICON_WINDOW_ANIM", true, "Use adaptive icons for window animations.");
 
     public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag(
-            "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
+            "ENABLE_QUICKSTEP_LIVE_TILE", true, "Enable live tile in Quickstep overview");
+
+    public static final BooleanFlag ENABLE_QUICKSTEP_WIDGET_APP_START = getDebugFlag(
+            "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", true, "Show chip hints on the overview screen");
+            "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_DEVICE_SEARCH_PERFORMANCE_LOGGING = new DeviceFlag(
+            "ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", true,
+            "Allows on device search in all apps logging");
+
+    public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(
+            "IME_STICKY_SNACKBAR_EDU", true, "Show sticky IME edu in AllApps");
+
+    public static final BooleanFlag ENABLE_PEOPLE_TILE_PREVIEW = getDebugFlag(
+            "ENABLE_PEOPLE_TILE_PREVIEW", false,
+            "Experimental: Shows conversation shortcuts on home screen as search results");
 
     public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
             "FOLDER_NAME_SUGGEST", true,
@@ -102,10 +121,6 @@
             "FOLDER_NAME_MAJORITY_RANKING", true,
             "Suggests folder names based on majority based ranking.");
 
-    public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
-            "APP_SEARCH_IMPROVEMENTS", true,
-            "Adds localized title and keyword search and ranking");
-
     public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
             "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
 
@@ -123,9 +138,6 @@
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
 
-    public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
-            "ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps");
-
     public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = getDebugFlag(
             "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
 
@@ -135,31 +147,46 @@
     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_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
-            "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker");
-
-    public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
-            "ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
-            + " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
+    public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag(
+            "ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace");
 
     // 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");
 
+    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", true,
+            "ENABLE_DATABASE_RESTORE", false,
             "Enable database restore when new restore session is created");
 
-    public static final BooleanFlag ENABLE_UNIVERSAL_SMARTSPACE = getDebugFlag(
-            "ENABLE_UNIVERSAL_SMARTSPACE", false,
+    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_LSQ_VELOCITY_PROVIDER = getDebugFlag(
-            "ENABLE_LSQ_VELOCITY_PROVIDER", true,
-            "Use Least Square algorithm for motion pause detection.");
+    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", true,
+            "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.");
 
     public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
             getDebugFlag(
@@ -174,13 +201,59 @@
             "SEPARATE_RECENTS_ACTIVITY", false,
             "Uses a separate recents activity instead of using the integrated recents+Launcher UI");
 
-    public static final BooleanFlag USER_EVENT_DISPATCHER = new DeviceFlag(
-            "USER_EVENT_DISPATCHER", true, "User event dispatcher collects logs.");
-
-    public static final BooleanFlag ENABLE_MINIMAL_DEVICE = new DeviceFlag(
+    public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
             "ENABLE_MINIMAL_DEVICE", false,
             "Allow user to toggle minimal device mode in launcher.");
 
+    public static final BooleanFlag EXPANDED_SMARTSPACE = new DeviceFlag(
+            "EXPANDED_SMARTSPACE", false, "Expands smartspace height to two rows. "
+              + "Any apps occupying the first row will be removed from workspace.");
+
+    // TODO: b/172467144 Remove ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE feature flag.
+    public static final BooleanFlag ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE = new DeviceFlag(
+            "ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE", false, "Enables a "
+            + "crossfade animation when the system these changes.");
+
+    // TODO: b/174174514 Remove ENABLE_APP_PREDICTIONS_WHILE_VISIBLE feature flag.
+    public static final BooleanFlag ENABLE_APP_PREDICTIONS_WHILE_VISIBLE = new DeviceFlag(
+            "ENABLE_APP_PREDICTIONS_WHILE_VISIBLE", true, "Allows app "
+            + "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.");
+
+    public static final BooleanFlag ENABLE_OVERVIEW_GRID = getDebugFlag(
+            "ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. "
+            + "Only applicable on large screen devices.");
+
+    public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(
+            "ENABLE_TWO_PANEL_HOME", false,
+            "Uses two panel on home screen. Only applicable on large screen devices.");
+
+    public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(
+            "ENABLE_SCRIM_FOR_APP_LAUNCH", false,
+            "Enables scrim during app launch animation.");
+
+    public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag(
+            "ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI");
+
+    public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
+            "ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
+
+    public static final BooleanFlag ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER = new DeviceFlag(
+            "ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER", true,
+            "Enables a local filter for recommended widgets.");
+
+    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.");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
@@ -239,6 +312,8 @@
         }
 
         public void addChangeListener(Context context, Runnable r) { }
+
+        public void removeChangeListener(Runnable r) {}
     }
 
     public static class DebugFlag extends BooleanFlag {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 0df6713..55be4a4 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -16,19 +16,24 @@
 
 package com.android.launcher3.dragndrop;
 
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PIN_WIDGETS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_BACK;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_START;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.annotation.TargetApi;
 import android.app.ActivityOptions;
 import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Intent;
 import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -36,36 +41,49 @@
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.DragShadowBuilder;
 import android.view.View.OnLongClickListener;
 import android.view.View.OnTouchListener;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.TextView;
 
 import com.android.launcher3.BaseActivity;
-import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.PinRequestHelper;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.InstantAppResolver;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.views.AbstractSlideInView;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.AddItemWidgetsBottomSheet;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.NavigableAppWidgetHostView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
-import com.android.launcher3.widget.WidgetHostViewLoader;
+import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetCellPreview;
 import com.android.launcher3.widget.WidgetImageView;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
 import java.util.function.Supplier;
 
+/**
+ * Activity to show pin widget dialog.
+ */
 @TargetApi(Build.VERSION_CODES.O)
-public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
+public class AddItemActivity extends BaseActivity
+        implements OnLongClickListener, OnTouchListener, AbstractSlideInView.OnCloseListener {
 
     private static final int SHADOW_SIZE = 10;
 
@@ -77,8 +95,11 @@
     private PinItemRequest mRequest;
     private LauncherAppState mApp;
     private InvariantDeviceProfile mIdp;
+    private BaseDragLayer<AddItemActivity> mDragLayer;
+    private AddItemWidgetsBottomSheet mSlideInView;
+    private AccessibilityManager mAccessibilityManager;
 
-    private LivePreviewWidgetCell mWidgetCell;
+    private WidgetCell mWidgetCell;
 
     // Widget request specific options.
     private LauncherAppWidgetHost mAppWidgetHost;
@@ -87,7 +108,6 @@
     private Bundle mWidgetOptions;
 
     private boolean mFinishOnPause = false;
-    private InstantAppResolver mInstantAppResolver;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -101,14 +121,20 @@
 
         mApp = LauncherAppState.getInstance(this);
         mIdp = mApp.getInvariantDeviceProfile();
-        mInstantAppResolver = InstantAppResolver.newInstance(this);
 
         // Use the application context to get the device profile, as in multiwindow-mode, the
         // confirmation activity might be rotated.
         mDeviceProfile = mIdp.getDeviceProfile(getApplicationContext());
 
         setContentView(R.layout.add_item_confirmation_activity);
+        // Set flag to allow activity to draw over navigation and status bar.
+        getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
+                WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
+        mDragLayer = findViewById(R.id.add_item_drag_layer);
+        mDragLayer.recreateControllers();
         mWidgetCell = findViewById(R.id.widget_cell);
+        mAccessibilityManager =
+                getApplicationContext().getSystemService(AccessibilityManager.class);
 
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
             setupShortcut();
@@ -119,14 +145,24 @@
             }
         }
 
-        mWidgetCell.setOnTouchListener(this);
-        mWidgetCell.setOnLongClickListener(this);
+        WidgetCellPreview previewContainer = mWidgetCell.findViewById(
+                R.id.widget_preview_container);
+        previewContainer.setOnTouchListener(this);
+        previewContainer.setOnLongClickListener(this);
 
         // savedInstanceState is null when the activity is created the first time (i.e., avoids
         // duplicate logging during rotation)
         if (savedInstanceState == null) {
-            logCommand(Action.Command.ENTRY);
+            logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
         }
+
+        TextView widgetAppName = findViewById(R.id.widget_appName);
+        widgetAppName.setText(getApplicationInfo().labelRes);
+
+        mSlideInView = findViewById(R.id.add_item_bottom_sheet);
+        mSlideInView.addOnCloseListener(this);
+        mSlideInView.show();
+        setupNavBarColor();
     }
 
     @Override
@@ -139,20 +175,31 @@
     public boolean onLongClick(View view) {
         // Find the position of the preview relative to the touch location.
         WidgetImageView img = mWidgetCell.getWidgetView();
+        NavigableAppWidgetHostView appWidgetHostView = mWidgetCell.getAppWidgetHostViewPreview();
 
         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
         // we abort the drag.
-        if (img.getBitmap() == null) {
+        if (img.getDrawable() == null && appWidgetHostView == null) {
             return false;
         }
 
-        Rect bounds = img.getBitmapBounds();
-        bounds.offset(img.getLeft() - (int) mLastTouchPos.x, img.getTop() - (int) mLastTouchPos.y);
-
+        final Rect bounds;
         // Start home and pass the draw request params
-        PinItemDragListener listener = new PinItemDragListener(mRequest, bounds,
-                img.getBitmap().getWidth(), img.getWidth());
-
+        final PinItemDragListener listener;
+        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());
+        } else {
+            bounds = img.getBitmapBounds();
+            bounds.offset(img.getLeft() - (int) mLastTouchPos.x,
+                    img.getTop() - (int) mLastTouchPos.y);
+            listener = new PinItemDragListener(mRequest, bounds,
+                    img.getDrawable().getIntrinsicWidth(), img.getWidth());
+        }
 
         // Start a system drag and drop. We use a transparent bitmap as preview for system drag
         // as the preview is handled internally by launcher.
@@ -174,10 +221,11 @@
                         .addCategory(Intent.CATEGORY_HOME)
                         .setPackage(getPackageName())
                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        Launcher.ACTIVITY_TRACKER.runCallbackWhenActivityExists(listener, homeIntent);
+        Launcher.ACTIVITY_TRACKER.registerCallback(listener);
         startActivity(homeIntent,
                 ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out)
                         .toBundle());
+        logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED);
         mFinishOnPause = true;
         return false;
     }
@@ -205,15 +253,16 @@
             // Cannot add widget
             return false;
         }
-        mWidgetCell.setPreview(PinItemDragListener.getPreview(mRequest));
+        mWidgetCell.setRemoteViewsPreview(PinItemDragListener.getPreview(mRequest));
 
         mAppWidgetManager = new WidgetManagerHelper(this);
         mAppWidgetHost = new LauncherAppWidgetHost(this);
 
-        PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo);
+        PendingAddWidgetInfo pendingInfo =
+                new PendingAddWidgetInfo(widgetInfo, CONTAINER_PIN_WIDGETS);
         pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
         pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
-        mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+        mWidgetOptions = pendingInfo.getDefaultSizeOptions(this);
         mWidgetCell.getWidgetView().setTag(pendingInfo);
 
         applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
@@ -229,6 +278,7 @@
 
             @Override
             protected void onPostExecute(WidgetItem item) {
+                mWidgetCell.setPreviewSize(item);
                 mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
                 mWidgetCell.ensurePreview();
             }
@@ -240,8 +290,8 @@
      * Called when the cancel button is clicked.
      */
     public void onCancelClick(View v) {
-        logCommand(Action.Command.CANCEL);
-        finish();
+        logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED);
+        mSlideInView.close(/* animate= */ true);
     }
 
     /**
@@ -249,17 +299,25 @@
      */
     public void onPlaceAutomaticallyClick(View v) {
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
-            InstallShortcutReceiver.queueShortcut(mRequest.getShortcutInfo(), this);
-            logCommand(Action.Command.CONFIRM);
+            ShortcutInfo shortcutInfo = mRequest.getShortcutInfo();
+            ItemInstallQueue.INSTANCE.get(this).queueItem(shortcutInfo);
+            logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY);
             mRequest.accept();
-            finish();
+            CharSequence label = shortcutInfo.getLongLabel();
+            if (TextUtils.isEmpty(label)) {
+                label = shortcutInfo.getShortLabel();
+            }
+            sendWidgetAddedToScreenAccessibilityEvent(label.toString());
+            mSlideInView.close(/* animate= */ true);
             return;
         }
 
         mPendingBindWidgetId = mAppWidgetHost.allocateAppWidgetId();
+        AppWidgetProviderInfo widgetProviderInfo = mRequest.getAppWidgetProviderInfo(this);
         boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
-                mPendingBindWidgetId, mRequest.getAppWidgetProviderInfo(this), mWidgetOptions);
+                mPendingBindWidgetId, widgetProviderInfo, mWidgetOptions);
         if (success) {
+            sendWidgetAddedToScreenAccessibilityEvent(widgetProviderInfo.label);
             acceptWidget(mPendingBindWidgetId);
             return;
         }
@@ -270,17 +328,18 @@
     }
 
     private void acceptWidget(int widgetId) {
-        InstallShortcutReceiver.queueWidget(mRequest.getAppWidgetProviderInfo(this), widgetId, this);
+        ItemInstallQueue.INSTANCE.get(this)
+                .queueItem(mRequest.getAppWidgetProviderInfo(this), widgetId);
         mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
         mRequest.accept(mWidgetOptions);
-        logCommand(Action.Command.CONFIRM);
-        finish();
+        logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY);
+        mSlideInView.close(/* animate= */ true);
     }
 
     @Override
     public void onBackPressed() {
-        logCommand(Action.Command.BACK);
-        super.onBackPressed();
+        logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_BACK);
+        mSlideInView.close(/* animate= */ true);
     }
 
     @Override
@@ -316,13 +375,36 @@
 
     @Override
     public BaseDragLayer getDragLayer() {
-        throw new UnsupportedOperationException();
+        return mDragLayer;
     }
 
-    private void logCommand(int command) {
-        getUserEventDispatcher().dispatchUserEvent(newLauncherEvent(
-                newCommandAction(command),
-                newItemTarget(mWidgetCell.getWidgetView(), mInstantAppResolver),
-                newContainerTarget(ContainerType.PINITEM)), null);
+    @Override
+    public void onSlideInViewClosed() {
+        finish();
+    }
+
+    protected void setupNavBarColor() {
+        boolean isSheetDark = (getApplicationContext().getResources().getConfiguration().uiMode
+                & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+        getSystemUiController().updateUiState(
+                SystemUiController.UI_STATE_BASE_WINDOW,
+                isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
+    }
+
+    private void sendWidgetAddedToScreenAccessibilityEvent(String widgetName) {
+        if (mAccessibilityManager.isEnabled()) {
+            AccessibilityEvent event =
+                    AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+            event.setContentDescription(
+                    getApplicationContext().getResources().getString(
+                            R.string.added_to_home_screen_accessibility_text, widgetName));
+            mAccessibilityManager.sendAccessibilityEvent(event);
+        }
+    }
+
+    private void logCommand(StatsLogManager.EventEnum command) {
+        getStatsLogManager().logger()
+                .withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
+                .log(command);
     }
 }
diff --git a/src/com/android/launcher3/dragndrop/AddItemDragLayer.java b/src/com/android/launcher3/dragndrop/AddItemDragLayer.java
new file mode 100644
index 0000000..5b52c3d
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/AddItemDragLayer.java
@@ -0,0 +1,42 @@
+/*
+ * 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.content.Context;
+import android.util.AttributeSet;
+
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+
+/**
+ * Drag layer for {@link AddItemActivity}.
+ */
+public class AddItemDragLayer extends BaseDragLayer<AddItemActivity> {
+
+    public AddItemDragLayer(Context context, AttributeSet attrs) {
+        this(context, attrs, /*alphaChannelCount= */ 1);
+    }
+
+    public AddItemDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
+        super(context, attrs, alphaChannelCount);
+    }
+
+    @Override
+    public void recreateControllers() {
+        mControllers = new TouchController[] {};
+    }
+}
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 707fd06..981e3a6 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -34,7 +34,6 @@
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
 import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
 import com.android.launcher3.widget.PendingItemDragHelper;
 
@@ -143,15 +142,13 @@
         // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
         // dragView from being visible. Instead just skip the fade-in animation here.
         mLauncher.getDragLayer().setAlpha(1);
-
-        dragObject.dragView.setColor(
-                mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
+        dragObject.dragView.setAlpha(.5f);
     }
 
     @Override
     public void onPreDragEnd(DragObject dragObject, boolean dragStarted) {
         if (dragStarted) {
-            dragObject.dragView.setColor(0);
+            dragObject.dragView.setAlpha(1f);
         }
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 03028d3..5731db4 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -16,42 +16,37 @@
 
 package com.android.launcher3.dragndrop;
 
-import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
-import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.Utilities.ATLEAST_Q;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
-import android.animation.ValueAnimator;
 import android.content.ComponentName;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.view.DragEvent;
-import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 
-import com.android.launcher3.AbstractFloatingView;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
+import java.util.Optional;
 
 /**
  * Class for initiating a drag within a view or across multiple views.
+ * @param <T>
  */
-public class DragController implements DragDriver.EventListener, TouchController {
-    private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+public abstract class DragController<T extends ActivityContext>
+        implements DragDriver.EventListener, TouchController {
 
     /**
      * When a drag is started from a deep press, you need to drag this much farther than normal to
@@ -59,8 +54,7 @@
      */
     private static final int DEEP_PRESS_DISTANCE_FACTOR = 3;
 
-    private final Launcher mLauncher;
-    private final FlingToDeleteHelper mFlingToDeleteHelper;
+    protected final T mActivity;
 
     // temporaries to avoid gc thrash
     private final Rect mRectTemp = new Rect();
@@ -70,30 +64,30 @@
      * Drag driver for the current drag/drop operation, or null if there is no active DND operation.
      * It's null during accessible drag operations.
      */
-    private DragDriver mDragDriver = null;
+    protected DragDriver mDragDriver = null;
 
     /** Options controlling the drag behavior. */
-    private DragOptions mOptions;
+    protected DragOptions mOptions;
 
     /** Coordinate for motion down event */
-    private final Point mMotionDown = new Point();
+    protected final Point mMotionDown = new Point();
     /** Coordinate for last touch event **/
-    private final Point mLastTouch = new Point();
+    protected final Point mLastTouch = new Point();
 
     private final Point mTmpPoint = new Point();
 
-    private DropTarget.DragObject mDragObject;
+    protected DropTarget.DragObject mDragObject;
 
     /** Who can receive drop events */
     private final ArrayList<DropTarget> mDropTargets = new ArrayList<>();
     private final ArrayList<DragListener> mListeners = new ArrayList<>();
 
-    private DropTarget mLastDropTarget;
+    protected DropTarget mLastDropTarget;
 
     private int mLastTouchClassification;
-    private int mDistanceSinceScroll = 0;
+    protected int mDistanceSinceScroll = 0;
 
-    private boolean mIsInPreDrag;
+    protected boolean mIsInPreDrag;
 
     /**
      * Interface to receive notifications when a drag starts or stops
@@ -116,119 +110,112 @@
     /**
      * Used to create a new DragLayer from XML.
      */
-    public DragController(Launcher launcher) {
-        mLauncher = launcher;
-        mFlingToDeleteHelper = new FlingToDeleteHelper(launcher);
+    public DragController(T activity) {
+        mActivity = activity;
     }
 
     /**
      * Starts a drag.
-     * When the drag is started, the UI automatically goes into spring loaded mode. On a successful
-     * drop, it is the responsibility of the {@link DropTarget} to exit out of the spring loaded
-     * mode. If the drop was cancelled for some reason, the UI will automatically exit out of this mode.
      *
-     * @param b The bitmap to display as the drag image.  It will be re-scaled to the
-     *          enlarged size.
-     * @param originalView The source view (ie. icon, widget etc.) that is being dragged
-     *          and which the DragView represents
+     * <p>When the drag is started, the UI automatically goes into spring loaded mode. On a
+     * successful drop, it is the responsibility of the {@link DropTarget} to exit out of the spring
+     * loaded mode. If the drop was cancelled for some reason, the UI will automatically exit out of
+     * this mode.
+     *
+     * @param drawable The drawable to be displayed in the drag view.  It will be re-scaled to the
+     *                 enlarged size.
+     * @param originalView The source view (ie. icon, widget etc.) that is being dragged and which
+     *                     the DragView represents
      * @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
      * @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
      * @param source An object representing where the drag originated
      * @param dragInfo The data associated with the object that is being dragged
      * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
-     *          Makes dragging feel more precise, e.g. you can clip out a transparent border
+     *                   Makes dragging feel more precise, e.g. you can clip out a transparent
+     *                   border
      */
-    public DragView startDrag(Bitmap b, DraggableView originalView, int dragLayerX, int dragLayerY,
-            DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion,
-            float initialDragViewScale, float dragViewScaleOnDrop, DragOptions options) {
-        if (PROFILE_DRAWING_DURING_DRAG) {
-            android.os.Debug.startMethodTracing("Launcher");
-        }
-
-        mLauncher.hideKeyboard();
-        AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE);
-
-        mOptions = options;
-        if (mOptions.simulatedDndStartPoint != null) {
-            mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
-            mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
-        }
-
-        final int registrationX = mMotionDown.x - dragLayerX;
-        final int registrationY = 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(mLauncher.getApplicationContext());
-        mDragObject.originalView = originalView;
-
-        mIsInPreDrag = mOptions.preDragCondition != null
-                && !mOptions.preDragCondition.shouldStartDrag(0);
-
-        final Resources res = mLauncher.getResources();
-        final float scaleDps = mIsInPreDrag
-                ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
-        final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
-                registrationY, initialDragViewScale, dragViewScaleOnDrop, scaleDps);
-        dragView.setItemInfo(dragInfo);
-        mDragObject.dragComplete = false;
-
-        mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
-        mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
-
-        mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent);
-        if (!mOptions.isAccessibleDrag) {
-            mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
-        }
-
-        mDragObject.dragSource = source;
-        mDragObject.dragInfo = dragInfo;
-        mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
-
-        if (dragOffset != null) {
-            dragView.setDragVisualizeOffset(new Point(dragOffset));
-        }
-        if (dragRegion != null) {
-            dragView.setDragRegion(new Rect(dragRegion));
-        }
-
-        mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-        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);
-        mLauncher.getUserEventDispatcher().resetActionDurationMillis();
-
-        if (!mLauncher.isTouchInProgress() && options.simulatedDndStartPoint == null) {
-            // If it is an internal drag and the touch is already complete, cancel immediately
-            MAIN_EXECUTOR.submit(this::cancelDrag);
-        }
-        return dragView;
+    public DragView startDrag(
+            Drawable drawable,
+            DraggableView originalView,
+            int dragLayerX,
+            int dragLayerY,
+            DragSource source,
+            ItemInfo dragInfo,
+            Point dragOffset,
+            Rect dragRegion,
+            float initialDragViewScale,
+            float dragViewScaleOnDrop,
+            DragOptions options) {
+        return startDrag(drawable, /* view= */ null, originalView, dragLayerX, dragLayerY,
+                source, dragInfo, dragOffset, dragRegion, initialDragViewScale, dragViewScaleOnDrop,
+                options);
     }
 
-    private void callOnDragStart() {
+    /**
+     * Starts a drag.
+     *
+     * <p>When the drag is started, the UI automatically goes into spring loaded mode. On a
+     * successful drop, it is the responsibility of the {@link DropTarget} to exit out of the spring
+     * loaded mode. If the drop was cancelled for some reason, the UI will automatically exit out of
+     * this mode.
+     *
+     * @param view The view to be displayed in the drag view.  It will be re-scaled to the
+     *             enlarged size.
+     * @param originalView The source view (ie. icon, widget etc.) that is being dragged and which
+     *                     the DragView represents
+     * @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
+     * @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
+     * @param source An object representing where the drag originated
+     * @param dragInfo The data associated with the object that is being dragged
+     * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
+     *                   Makes dragging feel more precise, e.g. you can clip out a transparent
+     *                   border
+     */
+    public DragView startDrag(
+            View view,
+            DraggableView originalView,
+            int dragLayerX,
+            int dragLayerY,
+            DragSource source,
+            ItemInfo dragInfo,
+            Point dragOffset,
+            Rect dragRegion,
+            float initialDragViewScale,
+            float dragViewScaleOnDrop,
+            DragOptions options) {
+        return startDrag(/* drawable= */ null, view, originalView, dragLayerX, dragLayerY,
+                source, dragInfo, dragOffset, dragRegion, initialDragViewScale, dragViewScaleOnDrop,
+                options);
+    }
+
+    protected abstract 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);
+
+    protected void callOnDragStart() {
         if (mOptions.preDragCondition != null) {
             mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
         }
         mIsInPreDrag = false;
+        mDragObject.dragView.onDragStart();
         for (DragListener listener : new ArrayList<>(mListeners)) {
             listener.onDragStart(mDragObject, mOptions);
         }
     }
 
-    public void addFirstFrameAnimationHelper(ValueAnimator anim) {
-        if (mDragObject != null && mDragObject.dragView != null) {
-            mDragObject.dragView.mFirstFrameAnimatorHelper.addTo(anim);
-        }
+    public Optional<InstanceId> getLogInstanceId() {
+        return Optional.ofNullable(mDragObject)
+                .map(dragObject -> dragObject.logInstanceId);
     }
 
     /**
@@ -271,13 +258,15 @@
         if (!accepted) {
             // If it was not accepted, cleanup the state. If it was accepted, it is the
             // responsibility of the drop target to cleanup the state.
-            mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+            exitDrag();
             mDragObject.deferDragViewCleanupPostAnimation = false;
         }
 
         mDragObject.dragSource.onDropCompleted(dropTarget, mDragObject, accepted);
     }
 
+    protected abstract void exitDrag();
+
     public void onAppsRemoved(ItemInfoMatcher matcher) {
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
@@ -291,7 +280,7 @@
         }
     }
 
-    private void endDrag() {
+    protected void endDrag() {
         if (isDragging()) {
             mDragDriver = null;
             boolean isDeferred = false;
@@ -310,8 +299,6 @@
                 callOnDragEnd();
             }
         }
-
-        mFlingToDeleteHelper.releaseVelocityTracker();
     }
 
     public void animateDragViewToOriginalPosition(final Runnable onComplete,
@@ -357,7 +344,7 @@
      * Clamps the position to the drag layer bounds.
      */
     private Point getClampedDragLayerPos(float x, float y) {
-        mLauncher.getDragLayer().getLocalVisibleRect(mRectTemp);
+        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));
         return mTmpPoint;
@@ -379,19 +366,16 @@
 
     @Override
     public void onDriverDragEnd(float x, float y) {
-        DropTarget dropTarget;
-        Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject, mOptions);
-        if (flingAnimation != null) {
-            dropTarget = mFlingToDeleteHelper.getDropTarget();
-        } else {
-            dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp);
+        if (!endWithFlingAnimation()) {
+            drop(findDropTarget((int) x, (int) y, mCoordinatesTemp), null);
         }
-
-        drop(dropTarget, flingAnimation);
-
         endDrag();
     }
 
+    protected boolean endWithFlingAnimation() {
+        return false;
+    }
+
     @Override
     public void onDriverDragCancel() {
         cancelDrag();
@@ -434,7 +418,7 @@
         return mDragDriver != null && mDragDriver.onDragEvent(event);
     }
 
-    private void handleMoveEvent(int x, int y) {
+    protected void handleMoveEvent(int x, int y) {
         mDragObject.dragView.move(x, y);
 
         // Drop on someone?
@@ -463,10 +447,10 @@
     }
 
     public void forceTouchMove() {
-        int[] dummyCoordinates = mCoordinatesTemp;
-        DropTarget dropTarget = findDropTarget(mLastTouch.x, mLastTouch.y, dummyCoordinates);
-        mDragObject.x = dummyCoordinates[0];
-        mDragObject.y = dummyCoordinates[1];
+        int[] placeholderCoordinates = mCoordinatesTemp;
+        DropTarget dropTarget = findDropTarget(mLastTouch.x, mLastTouch.y, placeholderCoordinates);
+        mDragObject.x = placeholderCoordinates[0];
+        mDragObject.y = placeholderCoordinates[1];
         checkTouchMove(dropTarget);
     }
 
@@ -506,7 +490,7 @@
         endDrag();
     }
 
-    private void drop(DropTarget dropTarget, Runnable flingAnimation) {
+    protected void drop(DropTarget dropTarget, Runnable flingAnimation) {
         final int[] coordinates = mCoordinatesTemp;
         mDragObject.x = coordinates[0];
         mDragObject.y = coordinates[1];
@@ -544,7 +528,6 @@
             }
         }
         final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
-        mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
         dispatchDropComplete(dropTargetAsView, accepted);
     }
 
@@ -564,7 +547,7 @@
             if (r.contains(x, y)) {
                 dropCoordinates[0] = x;
                 dropCoordinates[1] = y;
-                mLauncher.getDragLayer().mapCoordInSelfToDescendant((View) target, dropCoordinates);
+                mActivity.getDragLayer().mapCoordInSelfToDescendant((View) target, dropCoordinates);
                 return target;
             }
         }
@@ -572,11 +555,11 @@
         // cell layout to drop to in the existing drag/drop logic.
         dropCoordinates[0] = x;
         dropCoordinates[1] = y;
-        mLauncher.getDragLayer().mapCoordInSelfToDescendant(mLauncher.getWorkspace(),
-                dropCoordinates);
-        return mLauncher.getWorkspace();
+        return getDefaultDropTarget(dropCoordinates);
     }
 
+    protected abstract DropTarget getDefaultDropTarget(int[] dropCoordinates);
+
     /**
      * Sets the drag listener which will be notified when a drag starts or ends.
      */
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index ddf44ca..011325d 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -45,10 +45,10 @@
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.folder.Folder;
-import com.android.launcher3.graphics.OverviewScrim;
-import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
+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;
 
 import java.util.ArrayList;
@@ -82,11 +82,7 @@
 
     // Related to adjacent page hints
     private final ViewGroupFocusHelper mFocusIndicatorHelper;
-    private final WorkspaceAndHotseatScrim mWorkspaceScrim;
-    private final OverviewScrim mOverviewScrim;
-
-    // View that should handle move events
-    private View mMoveTarget;
+    private Scrim mWorkspaceDragScrim;
 
     /**
      * Used to create a new DragLayer from XML.
@@ -102,15 +98,12 @@
         setChildrenDrawingOrderEnabled(true);
 
         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
-        mWorkspaceScrim = new WorkspaceAndHotseatScrim(this);
-        mOverviewScrim = new OverviewScrim(this);
     }
 
     public void setup(DragController dragController, Workspace workspace) {
         mDragController = dragController;
-        mWorkspaceScrim.setWorkspace(workspace);
-        mMoveTarget = workspace;
         recreateControllers();
+        mWorkspaceDragScrim = new Scrim(this);
     }
 
     @Override
@@ -215,12 +208,6 @@
     }
 
     @Override
-    public boolean dispatchUnhandledMove(View focused, int direction) {
-        return super.dispatchUnhandledMove(focused, direction)
-                || mMoveTarget.dispatchUnhandledMove(focused, direction);
-    }
-
-    @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         ev.offsetLocation(getTranslationX(), 0);
         try {
@@ -525,41 +512,22 @@
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw the background below children.
-        mWorkspaceScrim.draw(canvas);
-        mOverviewScrim.updateCurrentScrimmedView(this);
+        mWorkspaceDragScrim.draw(canvas);
         mFocusIndicatorHelper.draw(canvas);
         super.dispatchDraw(canvas);
-        if (mOverviewScrim.getScrimmedView() == null) {
-            mOverviewScrim.draw(canvas);
+    }
+
+    public Scrim getWorkspaceDragScrim() {
+        return mWorkspaceDragScrim;
+    }
+
+    /**
+     * Called when one handed mode state changed.
+     * @param activated true if one handed mode activated, false otherwise.
+     */
+    public void onOneHandedModeStateChanged(boolean activated) {
+        for (TouchController controller : mControllers) {
+            controller.onOneHandedModeStateChanged(activated);
         }
     }
-
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (child == mOverviewScrim.getScrimmedView()) {
-            mOverviewScrim.draw(canvas);
-        }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mWorkspaceScrim.setSize(w, h);
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        super.setInsets(insets);
-        mWorkspaceScrim.onInsetsChanged(insets, mAllowSysuiScrims);
-        mOverviewScrim.onInsetsChanged(insets);
-    }
-
-    public WorkspaceAndHotseatScrim getScrim() {
-        return mWorkspaceScrim;
-    }
-
-    public OverviewScrim getOverviewScrim() {
-        return mOverviewScrim;
-    }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 959602b..e8ff8da 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -28,6 +28,9 @@
     /** Whether or not an accessible drag operation is in progress. */
     public boolean isAccessibleDrag = false;
 
+    /** Whether or not the drag operation is controlled by keyboard. */
+    public boolean isKeyboardDrag = false;
+
     /**
      * Specifies the start location for a simulated DnD (like system drag or accessibility drag),
      * null when using internal DnD
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index de0fa1a..3fdb256 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -16,61 +16,71 @@
 
 package com.android.launcher3.dragndrop;
 
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.Utilities.getBadge;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
-import android.animation.FloatArrayEvaluator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.TargetApi;
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.Picture;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PictureDrawable;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
 
+import androidx.annotation.Nullable;
 import androidx.dynamicanimation.animation.FloatPropertyCompat;
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
-import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.FirstFrameAnimatorHelper;
 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.Themes;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
-import java.util.Arrays;
+/** A custom view for rendering an icon, folder, shortcut or widget during drag-n-drop. */
+public class DragView extends FrameLayout implements StateListener<LauncherState> {
 
-public class DragView extends View implements StateListener<LauncherState> {
-    private static final ColorMatrix sTempMatrix1 = new ColorMatrix();
-    private static final ColorMatrix sTempMatrix2 = new ColorMatrix();
-
-    public static final int COLOR_CHANGE_DURATION = 120;
     public static final int VIEW_ZOOM_DURATION = 150;
 
-    private boolean mDrawBitmap = true;
-    private Bitmap mBitmap;
-    private Bitmap mCrossFadeBitmap;
-    @Thunk Paint mPaint;
+    private final View mContent;
+    // The following are only used for rendering mContent directly during drag-n-drop.
+    @Nullable private ViewGroup.LayoutParams mContentViewLayoutParams;
+    @Nullable private ViewGroup mContentViewParent;
+    private int mContentViewInParentViewIndex = -1;
+    private final int mWidth;
+    private final int mHeight;
+
     private final int mBlurSizeOutline;
     private final int mRegistrationX;
     private final int mRegistrationY;
@@ -78,23 +88,16 @@
     private final float mScaleOnDrop;
     private 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;
-    final FirstFrameAnimatorHelper mFirstFrameAnimatorHelper;
     private boolean mHasDrawn = false;
-    @Thunk float mCrossFadeProgress = 0f;
-    private boolean mAnimationCancelled = false;
 
-    ValueAnimator mAnim;
-    // The intrinsic icon scale factor is the scale factor for a drag icon over the workspace
-    // size.  This is ignored for non-icons.
-    private float mIntrinsicIconScale = 1f;
-
-    @Thunk float[] mCurrentFilter;
-    private ValueAnimator mFilterAnimator;
+    final ValueAnimator mAnim;
 
     private int mLastTouchX;
     private int mLastTouchY;
@@ -106,7 +109,14 @@
     private SpringFloatValue mTranslateX, mTranslateY;
     private Path mScaledMaskPath;
     private Drawable mBadge;
-    private ColorMatrixColorFilter mBaseFilter;
+
+    public DragView(Launcher launcher, Drawable drawable, int registrationX,
+            int registrationY, final float initialScale, final float scaleOnDrop,
+            final float finalScaleDps) {
+        this(launcher, getViewFromDrawable(launcher, drawable),
+                drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
+                registrationX, registrationY, initialScale, scaleOnDrop, finalScaleDps);
+    }
 
     /**
      * Construct the drag view.
@@ -114,19 +124,36 @@
      * The registration point is the point inside our view that the touch events should
      * be centered upon.
      * @param launcher The Launcher instance
-     * @param bitmap The view that we're dragging around.  We scale it up when we draw it.
+     * @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
+     * @param initialScale The view that we're dragging around.  We scale it up when we draw it.
      * @param registrationX The x coordinate of the registration point.
      * @param registrationY The y coordinate of the registration point.
+     * @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, Bitmap bitmap, int registrationX, int registrationY,
-                    final float initialScale, final float scaleOnDrop, final float finalScaleDps) {
+    public DragView(Launcher launcher, 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();
-        mFirstFrameAnimatorHelper = new FirstFrameAnimatorHelper(this);
 
-        final float scale = (bitmap.getWidth() + finalScaleDps) / bitmap.getWidth();
+        mContent = content;
+        mWidth = width;
+        mHeight = height;
+        mContentViewLayoutParams = mContent.getLayoutParams();
+        if (mContent.getParent() instanceof ViewGroup) {
+            mContentViewParent = (ViewGroup) mContent.getParent();
+            mContentViewInParentViewIndex = mContentViewParent.indexOfChild(mContent);
+            mContentViewParent.removeView(mContent);
+        }
+
+        addView(content, new LayoutParams(width, height));
+
+        final float scale = (width + finalScaleDps) / width;
 
         // Set the initial scale to avoid any jumps
         setScaleX(initialScale);
@@ -144,8 +171,7 @@
             }
         });
 
-        mBitmap = bitmap;
-        setDragRegion(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
+        setDragRegion(new Rect(0, 0, width, height));
 
         // The point in our scaled bitmap that the touch events are located
         mRegistrationX = registrationX;
@@ -155,12 +181,11 @@
         mScaleOnDrop = scaleOnDrop;
 
         // Force a measure, because Workspace uses getMeasuredHeight() before the layout pass
-        int ms = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
-        measure(ms, ms);
-        mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+        measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
 
         mBlurSizeOutline = getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
         setElevation(getResources().getDimension(R.dimen.drag_elevation));
+        setWillNotDraw(false);
     }
 
     @Override
@@ -187,148 +212,113 @@
      */
     @TargetApi(Build.VERSION_CODES.O)
     public void setItemInfo(final ItemInfo info) {
-        if (!Utilities.ATLEAST_OREO) {
-            return;
-        }
-        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
-                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
-                info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+        if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             return;
         }
         // Load the adaptive icon on a background thread and add the view in ui thread.
-        MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(new Runnable() {
-            @Override
-            public void run() {
-                Object[] outObj = new Object[1];
-                int w = mBitmap.getWidth();
-                int h = mBitmap.getHeight();
-                Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);
+        MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {
+            Object[] outObj = new Object[1];
+            int w = mWidth;
+            int h = mHeight;
+            Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);
 
-                if (dr instanceof AdaptiveIconDrawable) {
-                    int blurMargin = (int) mLauncher.getResources()
-                            .getDimension(R.dimen.blur_size_medium_outline) / 2;
+            if (dr instanceof AdaptiveIconDrawable) {
+                int blurMargin = (int) mLauncher.getResources()
+                        .getDimension(R.dimen.blur_size_medium_outline) / 2;
 
-                    Rect bounds = new Rect(0, 0, w, h);
-                    bounds.inset(blurMargin, blurMargin);
-                    // 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.setBounds(badgeBounds);
+                Rect bounds = new Rect(0, 0, w, h);
+                bounds.inset(blurMargin, blurMargin);
+                // 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.setBounds(badgeBounds);
 
-                    // Do not draw the background in case of folder as its translucent
-                    mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
+                // Do not draw the background in case of folder as its translucent
+                final boolean shouldDrawBackground = !(dr instanceof FolderAdaptiveIcon);
 
-                    try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) {
-                        Drawable nDr; // drawable to be normalized
-                        if (mDrawBitmap) {
-                            nDr = dr;
-                        } else {
-                            // Since we just want the scale, avoid heavy drawing operations
-                            nDr = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null);
-                        }
-                        Utilities.scaleRectAboutCenter(bounds,
-                                li.getNormalizer().getScale(nDr, null, null, null));
+                try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) {
+                    Drawable nDr; // drawable to be normalized
+                    if (shouldDrawBackground) {
+                        nDr = dr;
+                    } else {
+                        // Since we just want the scale, avoid heavy drawing operations
+                        nDr = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null);
                     }
-                    AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
-
-                    // Shrink very tiny bit so that the clip path is smaller than the original bitmap
-                    // that has anti aliased edges and shadows.
-                    Rect shrunkBounds = new Rect(bounds);
-                    Utilities.scaleRectAboutCenter(shrunkBounds, 0.98f);
-                    adaptiveIcon.setBounds(shrunkBounds);
-                    final Path mask = adaptiveIcon.getIconMask();
-
-                    mTranslateX = new SpringFloatValue(DragView.this,
-                            w * AdaptiveIconDrawable.getExtraInsetFraction());
-                    mTranslateY = new SpringFloatValue(DragView.this,
-                            h * AdaptiveIconDrawable.getExtraInsetFraction());
-
-                    bounds.inset(
-                            (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()),
-                            (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction())
-                    );
-                    mBgSpringDrawable = adaptiveIcon.getBackground();
-                    if (mBgSpringDrawable == null) {
-                        mBgSpringDrawable = new ColorDrawable(Color.TRANSPARENT);
-                    }
-                    mBgSpringDrawable.setBounds(bounds);
-                    mFgSpringDrawable = adaptiveIcon.getForeground();
-                    if (mFgSpringDrawable == null) {
-                        mFgSpringDrawable = new ColorDrawable(Color.TRANSPARENT);
-                    }
-                    mFgSpringDrawable.setBounds(bounds);
-
-                    new Handler(Looper.getMainLooper()).post(new Runnable() {
-                        @Override
-                        public void run() {
-                            // Assign the variable on the UI thread to avoid race conditions.
-                            mScaledMaskPath = mask;
-
-                            if (info.isDisabled()) {
-                                FastBitmapDrawable d = new FastBitmapDrawable((Bitmap) null);
-                                d.setIsDisabled(true);
-                                mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter();
-                            }
-                            updateColorFilter();
-                        }
-                    });
+                    Utilities.scaleRectAboutCenter(bounds,
+                            li.getNormalizer().getScale(nDr, null, null, null));
                 }
-            }});
+                AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
+
+                // Shrink very tiny bit so that the clip path is smaller than the original bitmap
+                // that has anti aliased edges and shadows.
+                Rect shrunkBounds = new Rect(bounds);
+                Utilities.scaleRectAboutCenter(shrunkBounds, 0.98f);
+                adaptiveIcon.setBounds(shrunkBounds);
+                final Path mask = adaptiveIcon.getIconMask();
+
+                mTranslateX = new SpringFloatValue(DragView.this,
+                        w * AdaptiveIconDrawable.getExtraInsetFraction());
+                mTranslateY = new SpringFloatValue(DragView.this,
+                        h * AdaptiveIconDrawable.getExtraInsetFraction());
+
+                bounds.inset(
+                        (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()),
+                        (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction())
+                );
+                mBgSpringDrawable = adaptiveIcon.getBackground();
+                if (mBgSpringDrawable == null) {
+                    mBgSpringDrawable = new ColorDrawable(Color.TRANSPARENT);
+                }
+                mBgSpringDrawable.setBounds(bounds);
+                mFgSpringDrawable = adaptiveIcon.getForeground();
+                if (mFgSpringDrawable == null) {
+                    mFgSpringDrawable = new ColorDrawable(Color.TRANSPARENT);
+                }
+                mFgSpringDrawable.setBounds(bounds);
+
+                new Handler(Looper.getMainLooper()).post(() -> mOnDragStartCallback.add(() -> {
+                    // TODO: Consider fade-in animation
+                    // Assign the variable on the UI thread to avoid race conditions.
+                    mScaledMaskPath = mask;
+                    // Avoid relayout as we do not care about children affecting layout
+                    removeAllViewsInLayout();
+
+                    if (info.isDisabled()) {
+                        FastBitmapDrawable d = new FastBitmapDrawable((Bitmap) null);
+                        d.setIsDisabled(true);
+                        mBgSpringDrawable.setColorFilter(d.getColorFilter());
+                        mFgSpringDrawable.setColorFilter(d.getColorFilter());
+                        mBadge.setColorFilter(d.getColorFilter());
+                    }
+                    invalidate();
+                }));
+            }
+        });
     }
 
-    @TargetApi(Build.VERSION_CODES.O)
-    private void updateColorFilter() {
-        if (mCurrentFilter == null) {
-            mPaint.setColorFilter(null);
+    /**
+     * Called when pre-drag finishes for an icon
+     */
+    public void onDragStart() {
+        mOnDragStartCallback.executeAllAndDestroy();
+    }
 
-            if (mScaledMaskPath != null) {
-                mBgSpringDrawable.setColorFilter(mBaseFilter);
-                mFgSpringDrawable.setColorFilter(mBaseFilter);
-                mBadge.setColorFilter(mBaseFilter);
-            }
-        } else {
-            ColorMatrixColorFilter currentFilter = new ColorMatrixColorFilter(mCurrentFilter);
-            mPaint.setColorFilter(currentFilter);
-
-            if (mScaledMaskPath != null) {
-                if (mBaseFilter != null) {
-                    mBaseFilter.getColorMatrix(sTempMatrix1);
-                    sTempMatrix2.set(mCurrentFilter);
-                    sTempMatrix1.postConcat(sTempMatrix2);
-
-                    currentFilter = new ColorMatrixColorFilter(sTempMatrix1);
-                }
-
-                mBgSpringDrawable.setColorFilter(currentFilter);
-                mFgSpringDrawable.setColorFilter(currentFilter);
-                mBadge.setColorFilter(currentFilter);
-            }
+    // 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();
         }
-
-        invalidate();
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
-    }
-
-    /** Sets the scale of the view over the normal workspace icon size. */
-    public void setIntrinsicIconScaleFactor(float scale) {
-        mIntrinsicIconScale = scale;
-    }
-
-    public float getIntrinsicIconScaleFactor() {
-        return mIntrinsicIconScale;
-    }
-
-    public int getDragRegionLeft() {
-        return mDragRegion.left;
-    }
-
-    public int getDragRegionTop() {
-        return mDragRegion.top;
+        super.onMeasure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY));
     }
 
     public int getDragRegionWidth() {
@@ -355,33 +345,12 @@
         return mDragRegion;
     }
 
-    public Bitmap getPreviewBitmap() {
-        return mBitmap;
-    }
-
     @Override
-    protected void onDraw(Canvas canvas) {
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+
+        // Draw after the content
         mHasDrawn = true;
-
-        if (mDrawBitmap) {
-            // Always draw the bitmap to mask anti aliasing due to clipPath
-            boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
-            if (crossFade) {
-                int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255;
-                mPaint.setAlpha(alpha);
-            }
-            canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
-            if (crossFade) {
-                mPaint.setAlpha((int) (255 * mCrossFadeProgress));
-                final int saveCount = canvas.save();
-                float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
-                float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
-                canvas.scale(sX, sY);
-                canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
-                canvas.restoreToCount(saveCount);
-            }
-        }
-
         if (mScaledMaskPath != null) {
             int cnt = canvas.save();
             canvas.clipPath(mScaledMaskPath);
@@ -393,74 +362,27 @@
         }
     }
 
-    public void setCrossFadeBitmap(Bitmap crossFadeBitmap) {
-        mCrossFadeBitmap = crossFadeBitmap;
-    }
-
-    public void crossFade(int duration) {
-        ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
-        va.setDuration(duration);
-        va.setInterpolator(Interpolators.DEACCEL_1_5);
-        va.addUpdateListener(a -> {
-            mCrossFadeProgress = a.getAnimatedFraction();
-            invalidate();
-        });
-        va.start();
-    }
-
-    public void setColor(int color) {
-        if (mPaint == null) {
-            mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+    public void crossFadeContent(Drawable crossFadeDrawable, int duration) {
+        if (mContent.getParent() == null) {
+            // If the content is already removed, ignore
+            return;
         }
-        if (color != 0) {
-            ColorMatrix m1 = new ColorMatrix();
-            m1.setSaturation(0);
+        View newContent = getViewFromDrawable(getContext(), crossFadeDrawable);
+        newContent.measure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY));
+        newContent.layout(0, 0, mWidth, mHeight);
+        addViewInLayout(newContent, 0, new LayoutParams(mWidth, mHeight));
 
-            ColorMatrix m2 = new ColorMatrix();
-            Themes.setColorScaleOnMatrix(color, m2);
-            m1.postConcat(m2);
-
-            animateFilterTo(m1.getArray());
-        } else {
-            if (mCurrentFilter == null) {
-                updateColorFilter();
-            } else {
-                animateFilterTo(new ColorMatrix().getArray());
-            }
-        }
-    }
-
-    private void animateFilterTo(float[] targetFilter) {
-        float[] oldFilter = mCurrentFilter == null ? new ColorMatrix().getArray() : mCurrentFilter;
-        mCurrentFilter = Arrays.copyOf(oldFilter, oldFilter.length);
-
-        if (mFilterAnimator != null) {
-            mFilterAnimator.cancel();
-        }
-        mFilterAnimator = ValueAnimator.ofObject(new FloatArrayEvaluator(mCurrentFilter),
-                oldFilter, targetFilter);
-        mFilterAnimator.setDuration(COLOR_CHANGE_DURATION);
-        mFilterAnimator.addUpdateListener(new AnimatorUpdateListener() {
-
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                updateColorFilter();
-            }
-        });
-        mFilterAnimator.start();
+        AnimatorSet anim = new AnimatorSet();
+        anim.play(ObjectAnimator.ofFloat(newContent, VIEW_ALPHA, 0, 1));
+        anim.play(ObjectAnimator.ofFloat(mContent, VIEW_ALPHA, 0));
+        anim.setDuration(duration).setInterpolator(Interpolators.DEACCEL_1_5);
+        anim.start();
     }
 
     public boolean hasDrawn() {
         return mHasDrawn;
     }
 
-    @Override
-    public void setAlpha(float alpha) {
-        super.setAlpha(alpha);
-        mPaint.setAlpha((int) (255 * alpha));
-        invalidate();
-    }
-
     /**
      * Create a window containing this view and show it.
      *
@@ -471,22 +393,21 @@
         mDragLayer.addView(this);
 
         // Start the pick-up animation
-        DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0);
-        lp.width = mBitmap.getWidth();
-        lp.height = mBitmap.getHeight();
+        BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(mWidth, mHeight);
         lp.customPosition = true;
         setLayoutParams(lp);
+
+        if (mContent != null) {
+            // At the drag start, the source view visibility is set to invisible.
+            mContent.setVisibility(VISIBLE);
+        }
+
         move(touchX, touchY);
         // Post the animation to skip other expensive work happening on the first frame
-        post(new Runnable() {
-            public void run() {
-                mAnim.start();
-            }
-        });
+        post(mAnim::start);
     }
 
     public void cancelAnimation() {
-        mAnimationCancelled = true;
         if (mAnim != null && mAnim.isRunning()) {
             mAnim.cancel();
         }
@@ -539,6 +460,63 @@
         setTranslationY(mLastTouchY - mRegistrationY + mAnimatedShiftY);
     }
 
+    /**
+     * Detaches {@link #mContent}, if previously attached, from this view.
+     *
+     * <p>In the case of no change in the drop position, sets {@code reattachToPreviousParent} to
+     * {@code true} to attach the {@link #mContent} back to its previous parent.
+     */
+    public void detachContentView(boolean reattachToPreviousParent) {
+        if (mContent != null && mContentViewParent != null && mContentViewInParentViewIndex >= 0) {
+            Picture picture = new Picture();
+            mContent.draw(picture.beginRecording(mWidth, mHeight));
+            picture.endRecording();
+            View view = new View(mLauncher);
+            view.setBackground(new PictureDrawable(picture));
+            view.measure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY));
+            view.layout(mContent.getLeft(), mContent.getTop(),
+                    mContent.getRight(), mContent.getBottom());
+            setClipToOutline(mContent.getClipToOutline());
+            setOutlineProvider(mContent.getOutlineProvider());
+            addViewInLayout(view, indexOfChild(mContent), mContent.getLayoutParams(), true);
+
+            removeViewInLayout(mContent);
+            mContent.setVisibility(INVISIBLE);
+            mContent.setLayoutParams(mContentViewLayoutParams);
+            if (reattachToPreviousParent) {
+                mContentViewParent.addView(mContent, mContentViewInParentViewIndex);
+            }
+            mContentViewParent = null;
+            mContentViewInParentViewIndex = -1;
+        }
+    }
+
+    /**
+     * 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
+     * {@link #mContent} back to its previous parent. To reattach to previous parent, the caller
+     * should call {@link #detachContentView} with {@code reattachToPreviousParent} sets to true
+     * before this call.
+     */
     public void remove() {
         if (getParent() != null) {
             mDragLayer.removeView(DragView.this);
@@ -553,6 +531,25 @@
         return mInitialScale;
     }
 
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    /** Returns the current content view that is rendered in the drag view. */
+    public View getContentView() {
+        return mContent;
+    }
+
+    /**
+     * Returns the previous {@link ViewGroup} parent of the {@link #mContent} before the drag
+     * content is attached to this view.
+     */
+    @Nullable
+    public ViewGroup getContentViewParent() {
+        return mContentViewParent;
+    }
+
     private static class SpringFloatValue {
 
         private static final FloatPropertyCompat<SpringFloatValue> VALUE =
@@ -570,9 +567,9 @@
                 };
 
         // Following three values are fine tuned with motion ux designer
-        private final static int STIFFNESS = 4000;
-        private final static float DAMPENING_RATIO = 1f;
-        private final static int PARALLAX_MAX_IN_DP = 8;
+        private static final int STIFFNESS = 4000;
+        private static final float DAMPENING_RATIO = 1f;
+        private static final int PARALLAX_MAX_IN_DP = 8;
 
         private final View mView;
         private final SpringAnimation mSpring;
@@ -594,4 +591,10 @@
             mSpring.animateToFinalPosition(Utilities.boundToRange(value, -mDelta, mDelta));
         }
     }
+
+    private static View getViewFromDrawable(Context context, Drawable drawable) {
+        ImageView iv = new ImageView(context);
+        iv.setImageDrawable(drawable);
+        return iv;
+    }
 }
diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
index 7788f93..336fced 100644
--- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
+++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
@@ -22,6 +22,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.ButtonDropTarget;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
@@ -35,15 +36,12 @@
     private static final float MAX_FLING_DEGREES = 35f;
 
     private final Launcher mLauncher;
-    private final int mFlingToDeleteThresholdVelocity;
 
     private ButtonDropTarget mDropTarget;
     private VelocityTracker mVelocityTracker;
 
     public FlingToDeleteHelper(Launcher launcher) {
         mLauncher = launcher;
-        mFlingToDeleteThresholdVelocity = launcher.getResources()
-                .getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity);
     }
 
     public void recordMotionEvent(MotionEvent ev) {
@@ -65,6 +63,9 @@
     }
 
     public Runnable getFlingAnimation(DropTarget.DragObject dragObject, DragOptions options) {
+        if (options == null) {
+            return null;
+        }
         PointF vel = isFlingingToDelete();
         options.isFlingToDelete = vel != null;
         if (!options.isFlingToDelete) {
@@ -88,12 +89,13 @@
         mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
         PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
         float theta = MAX_FLING_DEGREES + 1;
-        if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
+        DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
+        if (mVelocityTracker.getYVelocity() < deviceProfile.flingToDeleteThresholdVelocity) {
             // Do a quick dot product test to ensure that we are flinging upwards
             PointF upVec = new PointF(0f, -1f);
             theta = getAngleBetweenVectors(vel, upVec);
         } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() &&
-                mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) {
+                mVelocityTracker.getXVelocity() < deviceProfile.flingToDeleteThresholdVelocity) {
             // Remove icon is on left side instead of top, so check if we are flinging to the left.
             PointF leftVec = new PointF(-1f, 0f);
             theta = getAngleBetweenVectors(vel, leftVec);
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index ea1fbdb..98c0cfc 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -21,11 +21,11 @@
 import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
+import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.Log;
@@ -129,10 +129,19 @@
                     canvas.restore();
                 });
 
+        Bitmap bgBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
+                (canvas) -> {
+                    Paint p = new Paint();
+                    p.setColor(bg.getBgColor());
+
+                    canvas.drawCircle(dragViewSize.x / 2f, dragViewSize.y / 2f, bg.getRadius(), p);
+                });
+
         ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBmp, 0, 0);
         ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, 0, 0);
+        ShiftedBitmapDrawable background = new ShiftedBitmapDrawable(bgBitmap, 0, 0);
 
-        return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
+        return new FolderAdaptiveIcon(background, foreground, badge, mask);
     }
 
     @Override
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
new file mode 100644
index 0000000..a98d70c
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -0,0 +1,185 @@
+/*
+ * 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 static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.model.data.ItemInfo;
+
+/**
+ * Drag controller for Launcher activity
+ */
+public class LauncherDragController extends DragController<Launcher> {
+
+    private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+
+    private final FlingToDeleteHelper mFlingToDeleteHelper;
+
+    public LauncherDragController(Launcher launcher) {
+        super(launcher);
+        mFlingToDeleteHelper = new FlingToDeleteHelper(launcher);
+    }
+
+    @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) {
+        if (PROFILE_DRAWING_DURING_DRAG) {
+            android.os.Debug.startMethodTracing("Launcher");
+        }
+
+        mActivity.hideKeyboard();
+        AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_DISCOVERY_BOUNCE);
+
+        mOptions = options;
+        if (mOptions.simulatedDndStartPoint != null) {
+            mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
+            mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
+        }
+
+        final int registrationX = mMotionDown.x - dragLayerX;
+        final int registrationY = 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;
+
+        mIsInPreDrag = mOptions.preDragCondition != null
+                && !mOptions.preDragCondition.shouldStartDrag(0);
+
+        final Resources res = mActivity.getResources();
+        final float scaleDps = mIsInPreDrag
+                ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+        final DragView dragView = mDragObject.dragView = drawable != null
+                ? new DragView(
+                mActivity,
+                drawable,
+                registrationX,
+                registrationY,
+                initialDragViewScale,
+                dragViewScaleOnDrop,
+                scaleDps)
+                : new DragView(
+                        mActivity,
+                        view,
+                        view.getMeasuredWidth(),
+                        view.getMeasuredHeight(),
+                        registrationX,
+                        registrationY,
+                        initialDragViewScale,
+                        dragViewScaleOnDrop,
+                        scaleDps);
+        dragView.setItemInfo(dragInfo);
+        mDragObject.dragComplete = false;
+
+        mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
+        mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
+
+        mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent);
+        if (!mOptions.isAccessibleDrag) {
+            mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+        }
+
+        mDragObject.dragSource = source;
+        mDragObject.dragInfo = dragInfo;
+        mDragObject.originalDragInfo = mDragObject.dragInfo.makeShallowCopy();
+
+        if (dragOffset != null) {
+            dragView.setDragVisualizeOffset(new Point(dragOffset));
+        }
+        if (dragRegion != null) {
+            dragView.setDragRegion(new Rect(dragRegion));
+        }
+
+        mActivity.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        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);
+
+        if (!mActivity.isTouchInProgress() && options.simulatedDndStartPoint == null) {
+            // If it is an internal drag and the touch is already complete, cancel immediately
+            MAIN_EXECUTOR.submit(this::cancelDrag);
+        }
+        return dragView;
+    }
+
+    @Override
+    protected void exitDrag() {
+        mActivity.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+    }
+
+    @Override
+    protected boolean endWithFlingAnimation() {
+        Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject, mOptions);
+        if (flingAnimation != null) {
+            drop(mFlingToDeleteHelper.getDropTarget(), flingAnimation);
+            return true;
+        }
+        return super.endWithFlingAnimation();
+    }
+
+    @Override
+    protected void endDrag() {
+        super.endDrag();
+        mFlingToDeleteHelper.releaseVelocityTracker();
+    }
+
+    @Override
+    protected DropTarget getDefaultDropTarget(int[] dropCoordinates) {
+        mActivity.getDragLayer().mapCoordInSelfToDescendant(mActivity.getWorkspace(),
+                dropCoordinates);
+        return mActivity.getWorkspace();
+    }
+}
diff --git a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java b/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
deleted file mode 100644
index a9389bc..0000000
--- a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.android.launcher3.dragndrop;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.RemoteViews;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.icons.BitmapRenderer;
-import com.android.launcher3.widget.WidgetCell;
-
-/**
- * Extension of {@link WidgetCell} which supports generating previews from {@link RemoteViews}
- */
-public class LivePreviewWidgetCell extends WidgetCell {
-
-    private RemoteViews mPreview;
-
-    public LivePreviewWidgetCell(Context context) {
-        this(context, null);
-    }
-
-    public LivePreviewWidgetCell(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public LivePreviewWidgetCell(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    public void setPreview(RemoteViews view) {
-        mPreview = view;
-    }
-
-    @Override
-    public void ensurePreview() {
-        if (mPreview != null && mActiveRequest == null) {
-            Bitmap preview = generateFromRemoteViews(
-                    mActivity, mPreview, mItem.widgetInfo, mPresetPreviewSize, new int[1]);
-            if (preview != null) {
-                applyPreview(preview);
-                return;
-            }
-        }
-        super.ensurePreview();
-    }
-
-    /**
-     * Generates a bitmap by inflating {@param views}.
-     * @see com.android.launcher3.WidgetPreviewLoader#generateWidgetPreview
-     *
-     * TODO: Consider moving this to the background thread.
-     */
-    public static Bitmap generateFromRemoteViews(BaseActivity activity, RemoteViews views,
-            LauncherAppWidgetProviderInfo info, int previewSize, int[] preScaledWidthOut) {
-
-        DeviceProfile dp = activity.getDeviceProfile();
-        int viewWidth = dp.cellWidthPx * info.spanX;
-        int viewHeight = dp.cellHeightPx * info.spanY;
-
-        final View v;
-        try {
-            v = views.apply(activity, new FrameLayout(activity));
-            v.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
-
-            viewWidth = v.getMeasuredWidth();
-            viewHeight = v.getMeasuredHeight();
-            v.layout(0, 0, viewWidth, viewHeight);
-        } catch (Exception e) {
-            return null;
-        }
-
-        preScaledWidthOut[0] = viewWidth;
-        final int bitmapWidth, bitmapHeight;
-        final float scale;
-        if (viewWidth > previewSize) {
-            scale = ((float) previewSize) / viewWidth;
-            bitmapWidth = previewSize;
-            bitmapHeight = (int) (viewHeight * scale);
-        } else {
-            scale = 1;
-            bitmapWidth = viewWidth;
-            bitmapHeight = viewHeight;
-        }
-
-        return BitmapRenderer.createSoftwareBitmap(bitmapWidth, bitmapHeight, c -> {
-            c.scale(scale, scale);
-            v.draw(c);
-        });
-    }
-}
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index bf3aa7f..2bdf8a0 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -16,7 +16,8 @@
 
 package com.android.launcher3.dragndrop;
 
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PIN_WIDGETS;
 
 import android.annotation.TargetApi;
 import android.appwidget.AppWidgetManager;
@@ -31,17 +32,13 @@
 
 import com.android.launcher3.DragSource;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.PendingAddItemInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.PendingItemDragHelper;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 
-import java.util.ArrayList;
-
 /**
  * {@link DragSource} for handling drop from a different window. This object is initialized
  * in the source window and is passed on to the Launcher activity as an Intent extra.
@@ -89,7 +86,7 @@
                             mLauncher, mRequest.getAppWidgetProviderInfo(mLauncher));
             final PinWidgetFlowHandler flowHandler =
                     new PinWidgetFlowHandler(providerInfo, mRequest);
-            item = new PendingAddWidgetInfo(providerInfo) {
+            item = new PendingAddWidgetInfo(providerInfo, CONTAINER_PIN_WIDGETS) {
                 @Override
                 public WidgetAddFlowHandler getHandler() {
                     return flowHandler;
@@ -101,18 +98,12 @@
 
         PendingItemDragHelper dragHelper = new PendingItemDragHelper(view);
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_APPWIDGET) {
-            dragHelper.setPreview(getPreview(mRequest));
+            dragHelper.setRemoteViewsPreview(getPreview(mRequest));
         }
         return dragHelper;
     }
 
     @Override
-    public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
-            ArrayList<LauncherLogProto.Target> parents) {
-        parents.add(newContainerTarget(LauncherLogProto.ContainerType.PINITEM));
-    }
-
-    @Override
     protected void postCleanup() {
         super.postCleanup();
         mCancelSignal.cancel();
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index 9982b39..29e7c18 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -28,7 +28,6 @@
 import android.os.Build;
 import android.os.Process;
 
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherAppState;
@@ -49,14 +48,14 @@
 
     // Class name used in the target component, such that it will never represent an
     // actual existing class.
-    private static final String DUMMY_COMPONENT_CLASS = "pinned-shortcut";
+    private static final String STUB_COMPONENT_CLASS = "pinned-shortcut";
 
     private final PinItemRequest mRequest;
     private final ShortcutInfo mInfo;
     private final Context mContext;
 
     public PinShortcutRequestActivityInfo(PinItemRequest request, Context context) {
-        super(new ComponentName(request.getShortcutInfo().getPackage(), DUMMY_COMPONENT_CLASS),
+        super(new ComponentName(request.getShortcutInfo().getPackage(), STUB_COMPONENT_CLASS),
                 request.getShortcutInfo().getUserHandle());
         mRequest = request;
         mInfo = request.getShortcutInfo();
@@ -78,7 +77,7 @@
         Drawable d = mContext.getSystemService(LauncherApps.class)
                 .getShortcutIconDrawable(mInfo, LauncherAppState.getIDP(mContext).fillResIconDpi);
         if (d == null) {
-            d = new FastBitmapDrawable(cache.getDefaultIcon(Process.myUserHandle()));
+            d = cache.getDefaultIcon(Process.myUserHandle()).newIcon(mContext);
         }
         return d;
     }
diff --git a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index 37200a6..6325877 100644
--- a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -56,9 +56,8 @@
         if (mScreen != null) {
             // Snap to the screen that we are hovering over now
             Workspace w = mLauncher.getWorkspace();
-            int page = w.indexOfChild(mScreen);
-            if (page != w.getCurrentPage()) {
-                w.snapToPage(page);
+            if (!w.isVisible(mScreen)) {
+                w.snapToPage(w.indexOfChild(mScreen));
             }
         } else {
             mLauncher.getDragController().cancelDrag();
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 5954efa..8cd91d3 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -5,10 +5,12 @@
     public static final int MAX_NUM_ITEMS_IN_PREVIEW = 4;
     private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2;
 
-    private static final float MIN_SCALE = 0.48f;
-    private static final float MAX_SCALE = 0.58f;
-    private static final float MAX_RADIUS_DILATION = 0.15f;
-    private static final float ITEM_RADIUS_SCALE_FACTOR = 1.33f;
+    private static final float MIN_SCALE = 0.44f;
+    private static final float MAX_SCALE = 0.51f;
+    private static final float MAX_RADIUS_DILATION = 0.25f;
+    // The max amount of overlap the preview items can go outside of the background bounds.
+    public static final float ICON_OVERLAP_FACTOR = 1 + (MAX_RADIUS_DILATION / 2f);
+    private static final float ITEM_RADIUS_SCALE_FACTOR = 1.15f;
 
     public static final int EXIT_INDEX = -2;
     public static final int ENTER_INDEX = -3;
@@ -34,7 +36,6 @@
         float totalScale = scaleForItem(curNumItems);
         float transX;
         float transY;
-        float overlayAlpha = 0;
 
         if (index == EXIT_INDEX) {
             // 0 1 * <-- Exit position (row 0, col 2)
@@ -55,10 +56,9 @@
         transY = mTmpPoint[1];
 
         if (params == null) {
-            params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+            params = new PreviewItemDrawingParams(transX, transY, totalScale);
         } else {
             params.update(transX, transY, totalScale);
-            params.overlayAlpha = overlayAlpha;
         }
         return params;
     }
@@ -97,7 +97,7 @@
 
         double thetaShift = 0;
         if (curNumItems == 3) {
-            thetaShift = Math.PI / 6;
+            thetaShift = Math.PI / 2;
         } else if (curNumItems == 4) {
             thetaShift = Math.PI / 4;
         }
@@ -130,10 +130,8 @@
     public float scaleForItem(int numItems) {
         // Scale is determined by the number of items in the preview.
         final float scale;
-        if (numItems <= 2) {
+        if (numItems <= 3) {
             scale = MAX_SCALE;
-        } else if (numItems == 3) {
-            scale = (MAX_SCALE + MIN_SCALE) / 2;
         } else {
             scale = MIN_SCALE;
         }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index b91d1c3..22bb56c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -19,13 +19,12 @@
 import static android.text.TextUtils.isEmpty;
 
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -34,24 +33,33 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Insets;
 import android.graphics.Path;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
+import android.util.TypedValue;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+import androidx.core.content.res.ResourcesCompat;
+
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Alarm;
 import com.android.launcher3.BubbleTextView;
@@ -67,14 +75,13 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
 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.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragController.DragListener;
-import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logger.LauncherAtom.FromState;
 import com.android.launcher3.logger.LauncherAtom.ToState;
@@ -86,9 +93,10 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.views.ClipPathView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
@@ -142,6 +150,7 @@
     private static final float ICON_OVERSCROLL_WIDTH_FACTOR = 0.45f;
 
     private static final int FOLDER_NAME_ANIMATION_DURATION = 633;
+    private static final int FOLDER_COLOR_ANIMATION_DURATION = 200;
 
     private static final int REORDER_DELAY = 250;
     private static final int ON_EXIT_CLOSE_DELAY = 400;
@@ -151,22 +160,29 @@
     private final Alarm mReorderAlarm = new Alarm();
     private final Alarm mOnExitAlarm = new Alarm();
     private final Alarm mOnScrollHintAlarm = new Alarm();
-    @Thunk final Alarm mScrollPauseAlarm = new Alarm();
+    final Alarm mScrollPauseAlarm = new Alarm();
 
-    @Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
+    final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
 
     private AnimatorSet mCurrentAnimator;
     private boolean mIsAnimatingClosed = false;
 
-    protected final Launcher mLauncher;
+    // Folder can be displayed in Launcher's activity or a separate window (e.g. Taskbar).
+    // Anything specific to Launcher should use mLauncherDelegate, otherwise should
+    // use mActivityContext.
+    protected final LauncherDelegate mLauncherDelegate;
+    protected final ActivityContext mActivityContext;
+
     protected DragController mDragController;
     public FolderInfo mInfo;
     private CharSequence mFromTitle;
     private FromState mFromLabelState;
 
-    @Thunk FolderIcon mFolderIcon;
+    @Thunk
+    FolderIcon mFolderIcon;
 
-    @Thunk FolderPagedView mContent;
+    @Thunk
+    FolderPagedView mContent;
     public FolderNameEditText mFolderName;
     private PageIndicatorDots mPageIndicator;
 
@@ -174,7 +190,8 @@
     private int mFooterHeight;
 
     // Cell ranks used for drag and drop
-    @Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank;
+    @Thunk
+    int mTargetRank, mPrevTargetRank, mEmptyCellRank;
 
     private Path mClipPath;
 
@@ -185,7 +202,8 @@
                     @ViewDebug.IntToString(from = STATE_ANIMATING, to = "STATE_ANIMATING"),
                     @ViewDebug.IntToString(from = STATE_OPEN, to = "STATE_OPEN"),
             })
-    @Thunk int mState = STATE_NONE;
+    @Thunk
+    int mState = STATE_NONE;
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mRearrangeOnClose = false;
     boolean mItemsInvalidated = false;
@@ -203,38 +221,61 @@
     // Folder scrolling
     private int mScrollAreaOffset;
 
-    @Thunk int mScrollHintDir = SCROLL_NONE;
-    @Thunk int mCurrentScrollDir = SCROLL_NONE;
+    @Thunk
+    int mScrollHintDir = SCROLL_NONE;
+    @Thunk
+    int mCurrentScrollDir = SCROLL_NONE;
 
     private StatsLogManager mStatsLogManager;
 
+    @Nullable
+    private KeyboardInsetAnimationCallback mKeyboardInsetAnimationCallback;
+
+    private GradientDrawable mBackground;
+
     /**
      * Used to inflate the Workspace from XML.
      *
      * @param context The application's context.
-     * @param attrs The attributes set containing the Workspace's customization values.
+     * @param attrs   The attributes set containing the Workspace's customization values.
      */
     public Folder(Context context, AttributeSet attrs) {
         super(context, attrs);
         setAlwaysDrawnWithCacheEnabled(false);
 
-        mLauncher = Launcher.getLauncher(context);
+        mActivityContext = ActivityContext.lookupContext(context);
+        mLauncherDelegate = LauncherDelegate.from(mActivityContext);
+
         mStatsLogManager = StatsLogManager.newInstance(context);
         // We need this view to be focusable in touch mode so that when text editing of the folder
         // name is complete, we have something to focus on, thus hiding the cursor and giving
-        // reliable behavior when clicking the text field (since it will always gain focus on click).
+        // reliable behavior when clicking the text field (since it will always gain focus on
+        // click).
         setFocusableInTouchMode(true);
 
     }
 
     @Override
+    public Drawable getBackground() {
+        return mBackground;
+    }
+
+    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        final DeviceProfile dp = mActivityContext.getDeviceProfile();
+        final int paddingLeftRight = dp.folderContentPaddingLeftRight;
+
+        mBackground = (GradientDrawable) ResourcesCompat.getDrawable(getResources(),
+                R.drawable.round_rect_folder, getContext().getTheme());
+
         mContent = findViewById(R.id.folder_content);
+        mContent.setPadding(paddingLeftRight, dp.folderContentPaddingTop, paddingLeftRight, 0);
         mContent.setFolder(this);
 
         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);
@@ -246,17 +287,17 @@
         mFolderName.forceDisableSuggestions(true);
 
         mFooter = findViewById(R.id.folder_footer);
+        mFooterHeight = getResources().getDimensionPixelSize(R.dimen.folder_label_height);
 
-        // We find out how tall footer wants to be (it is set to wrap_content), so that
-        // we can allocate the appropriate amount of space for it.
-        int measureSpec = MeasureSpec.UNSPECIFIED;
-        mFooter.measure(measureSpec, measureSpec);
-        mFooterHeight = mFooter.getMeasuredHeight();
+        if (Utilities.ATLEAST_R) {
+            mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
+            setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
+        }
     }
 
     public boolean onLongClick(View v) {
         // Return if global dragging is not enabled
-        if (!mLauncher.isDraggingEnabled()) return true;
+        if (!mLauncherDelegate.isDraggingEnabled()) return true;
         return startDrag(v, new DragOptions());
     }
 
@@ -272,17 +313,17 @@
             if (options.isAccessibleDrag) {
                 mDragController.addDragListener(new AccessibleDragListenerAdapter(
                         mContent, FolderAccessibilityHelper::new) {
-                            @Override
-                            protected void enableAccessibleDrag(boolean enable) {
-                                super.enableAccessibleDrag(enable);
-                                mFooter.setImportantForAccessibility(enable
-                                        ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                                        : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-                            }
-                        });
+                    @Override
+                    protected void enableAccessibleDrag(boolean enable) {
+                        super.enableAccessibleDrag(enable);
+                        mFooter.setImportantForAccessibility(enable
+                                ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                                : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+                    }
+                });
             }
 
-            mLauncher.getWorkspace().beginDragShared(v, this, options);
+            mLauncherDelegate.beginDragShared(v, this, options);
         }
         return true;
     }
@@ -338,7 +379,7 @@
         if (DEBUG) {
             Log.d(TAG, "onBackKey newTitle=" + newTitle);
         }
-        mInfo.setTitle(newTitle, mLauncher.getModelWriter());
+        mInfo.setTitle(newTitle, mLauncherDelegate.getModelWriter());
         mFolderIcon.onTitleChanged(newTitle);
 
         if (TextUtils.isEmpty(mInfo.title)) {
@@ -373,6 +414,26 @@
         return false;
     }
 
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+        if (Utilities.ATLEAST_R) {
+            this.setTranslationY(0);
+
+            if (windowInsets.isVisible(WindowInsets.Type.ime())) {
+                Insets keyboardInsets = windowInsets.getInsets(WindowInsets.Type.ime());
+                int folderHeightFromBottom = getHeightFromBottom();
+
+                if (keyboardInsets.bottom > folderHeightFromBottom) {
+                    // Translate this folder above the keyboard, then add the folder name's padding
+                    this.setTranslationY(folderHeightFromBottom - keyboardInsets.bottom
+                            - mFolderName.getPaddingBottom());
+                }
+            }
+        }
+
+        return windowInsets;
+    }
+
     public FolderIcon getFolderIcon() {
         return mFolderIcon;
     }
@@ -383,6 +444,7 @@
 
     public void setFolderIcon(FolderIcon icon) {
         mFolderIcon = icon;
+        mLauncherDelegate.init(this, icon);
     }
 
     @Override
@@ -421,9 +483,9 @@
         Collections.sort(children, ITEM_POS_COMPARATOR);
         updateItemLocationsInDatabaseBatch(true);
 
-        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) getLayoutParams();
         if (lp == null) {
-            lp = new DragLayer.LayoutParams(0, 0);
+            lp = new BaseDragLayer.LayoutParams(0, 0);
             lp.customPosition = true;
             setLayoutParams(lp);
         }
@@ -477,20 +539,36 @@
     /**
      * Creates a new UserFolder, inflated from R.layout.user_folder.
      *
-     * @param launcher The main activity.
-     *
+     * @param activityContext The main ActivityContext in which to inflate this Folder. It must also
+     *                        be an instance or ContextWrapper around the Launcher activity context.
      * @return A new UserFolder.
      */
     @SuppressLint("InflateParams")
-    static Folder fromXml(Launcher launcher) {
-        return (Folder) launcher.getLayoutInflater()
+    static <T extends Context & ActivityContext> Folder fromXml(T activityContext) {
+        return (Folder) LayoutInflater.from(activityContext).cloneInContext(activityContext)
                 .inflate(R.layout.user_folder_icon_normalized, null);
     }
 
     private void startAnimation(final AnimatorSet a) {
-        final Workspace workspace = mLauncher.getWorkspace();
-        final CellLayout currentCellLayout =
-                (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
+        mLauncherDelegate.forEachVisibleWorkspacePage(
+                visiblePage -> addAnimatorListenerForPage(a, (CellLayout) visiblePage));
+
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mState = STATE_ANIMATING;
+                mCurrentAnimator = a;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mCurrentAnimator = null;
+            }
+        });
+        a.start();
+    }
+
+    private void addAnimatorListenerForPage(AnimatorSet a, CellLayout currentCellLayout) {
         final boolean useHardware = shouldUseHardwareLayerForAnimation(currentCellLayout);
         final boolean wasHardwareAccelerated = currentCellLayout.isHardwareLayerEnabled();
 
@@ -500,8 +578,6 @@
                 if (useHardware) {
                     currentCellLayout.enableHardwareLayer(true);
                 }
-                mState = STATE_ANIMATING;
-                mCurrentAnimator = a;
             }
 
             @Override
@@ -509,10 +585,8 @@
                 if (useHardware) {
                     currentCellLayout.enableHardwareLayer(wasHardwareAccelerated);
                 }
-                mCurrentAnimator = null;
             }
         });
-        a.start();
     }
 
     private boolean shouldUseHardwareLayerForAnimation(CellLayout currentCellLayout) {
@@ -561,16 +635,7 @@
      * is played.
      */
     private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
-        animateOpen(items, pageNo, false);
-    }
-
-    /**
-     * Opens the user folder described by the specified tag. The opening of the folder
-     * is animated relative to the specified View. If the View is null, no animation
-     * is played.
-     */
-    private void animateOpen(List<WorkspaceItemInfo> items, int pageNo, boolean skipUserEventLog) {
-        Folder openFolder = getOpen(mLauncher);
+        Folder openFolder = getOpen(mActivityContext);
         if (openFolder != null && openFolder != this) {
             // Close any open folder before opening a folder.
             openFolder.close(true);
@@ -583,7 +648,7 @@
 
         mIsOpen = true;
 
-        DragLayer dragLayer = mLauncher.getDragLayer();
+        BaseDragLayer dragLayer = mActivityContext.getDragLayer();
         // Just verify that the folder hasn't already been added to the DragLayer.
         // There was a one-off crash where the folder had a parent already.
         if (getParent() == null) {
@@ -597,36 +662,28 @@
         }
 
         mContent.completePendingPageChanges();
-        mContent.snapToPageImmediately(pageNo);
+        mContent.setCurrentPage(pageNo);
 
         // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
         // leads to an inconsistent state if you drag out of the folder and drag back in without
         // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
         mDeleteFolderOnDropCompleted = false;
 
-        if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
-            mCurrentAnimator.cancel();
-        }
-        AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator();
+        cancelRunningAnimations();
+        FolderAnimationManager fam = new FolderAnimationManager(this, true /* isOpening */);
+        AnimatorSet anim = fam.getAnimator();
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 mFolderIcon.setIconVisible(false);
                 mFolderIcon.drawLeaveBehindIfExists();
             }
+
             @Override
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
                 announceAccessibilityChanges();
 
-                if (!skipUserEventLog) {
-                    mLauncher.getUserEventDispatcher().logActionOnItem(
-                            LauncherLogProto.Action.Touch.TAP,
-                            LauncherLogProto.Action.Direction.NONE,
-                            LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
-                }
-
-
                 mContent.setFocusOnFirstChild();
             }
         });
@@ -636,7 +693,7 @@
             int footerWidth = mContent.getDesiredWidth()
                     - mFooter.getPaddingLeft() - mFooter.getPaddingRight();
 
-            float textWidth =  mFolderName.getPaint().measureText(mFolderName.getText().toString());
+            float textWidth = mFolderName.getPaint().measureText(mFolderName.getText().toString());
             float translation = (footerWidth - textWidth) / 2;
             mFolderName.setTranslationX(mContent.mIsRtl ? -translation : translation);
             mPageIndicator.prepareEntryAnimation();
@@ -650,14 +707,14 @@
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION)
-                        .translationX(0)
-                        .setInterpolator(AnimationUtils.loadInterpolator(
-                                mLauncher, android.R.interpolator.fast_out_slow_in));
+                            .translationX(0)
+                            .setInterpolator(AnimationUtils.loadInterpolator(
+                                    getContext(), android.R.interpolator.fast_out_slow_in));
                     mPageIndicator.playEntryAnimation();
 
                     if (updateAnimationFlag) {
                         mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true,
-                                mLauncher.getModelWriter());
+                                mLauncherDelegate.getModelWriter());
                     }
                 }
             });
@@ -667,6 +724,9 @@
 
         mPageIndicator.stopAllAnimations();
         startAnimation(anim);
+        // Because t=0 has the folder match the folder icon, we can skip the
+        // first frame and have the same movement one frame earlier.
+        anim.setCurrentPlayTime(Math.min(getSingleFrameMs(getContext()), anim.getTotalDuration()));
 
         // Make sure the folder picks up the last drag move even if the finger doesn't move.
         if (mDragController.isDragging()) {
@@ -705,26 +765,40 @@
 
         // Notify the accessibility manager that this folder "window" has disappeared and no
         // longer occludes the workspace items
-        mLauncher.getDragLayer().sendAccessibilityEvent(
+        mActivityContext.getDragLayer().sendAccessibilityEvent(
                 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
+    private void cancelRunningAnimations() {
+        if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+            mCurrentAnimator.cancel();
+        }
+    }
+
     private void animateClosed() {
         if (mIsAnimatingClosed) {
             return;
         }
-        if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
-            mCurrentAnimator.cancel();
-        }
+
+        mContent.completePendingPageChanges();
+        mContent.snapToPageImmediately(mContent.getDestinationPage());
+
+        cancelRunningAnimations();
         AnimatorSet a = new FolderAnimationManager(this, false /* isOpening */).getAnimator();
         a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
+                if (Utilities.ATLEAST_R) {
+                    setWindowInsetsAnimationCallback(null);
+                }
                 mIsAnimatingClosed = true;
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
+                if (Utilities.ATLEAST_R && mKeyboardInsetAnimationCallback != null) {
+                    setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
+                }
                 closeComplete(true);
                 announceAccessibilityChanges();
                 mIsAnimatingClosed = false;
@@ -747,7 +821,7 @@
 
     private void closeComplete(boolean wasAnimated) {
         // TODO: Clear all active animations.
-        DragLayer parent = (DragLayer) getParent();
+        BaseDragLayer parent = (BaseDragLayer) getParent();
         if (parent != null) {
             parent.removeView(this);
         }
@@ -980,13 +1054,13 @@
         if (getItemCount() <= mContent.itemsPerPage()) {
             // Show the animation, next time something is added to the folder.
             mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false,
-                    mLauncher.getModelWriter());
+                    mLauncherDelegate.getModelWriter());
         }
     }
 
     private void updateItemLocationsInDatabaseBatch(boolean isBind) {
         FolderGridOrganizer verifier = new FolderGridOrganizer(
-                mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+                mActivityContext.getDeviceProfile().inv).setFolderInfo(mInfo);
 
         ArrayList<ItemInfo> items = new ArrayList<>();
         int total = mInfo.contents.size();
@@ -998,7 +1072,7 @@
         }
 
         if (!items.isEmpty()) {
-            mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
+            mLauncherDelegate.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
         }
         if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && !isBind
                 && total > 1 /* no need to update if there's one icon */) {
@@ -1023,10 +1097,8 @@
     }
 
     private void centerAboutIcon() {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-
-        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
-        DragLayer parent = mLauncher.getDragLayer();
+        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) getLayoutParams();
+        BaseDragLayer parent = mActivityContext.getDragLayer();
         int width = getFolderWidth();
         int height = getFolderHeight();
 
@@ -1036,38 +1108,13 @@
         int centeredLeft = centerX - width / 2;
         int centeredTop = centerY - height / 2;
 
-        // We need to bound the folder to the currently visible workspace area
-        if (mLauncher.getStateManager().getState().overviewUi) {
-            parent.getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(), sTempRect);
-        } else {
-            mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
-        }
-        int left = Math.min(Math.max(sTempRect.left, centeredLeft),
-                sTempRect.right- width);
-        int top = Math.min(Math.max(sTempRect.top, centeredTop),
-                sTempRect.bottom - height);
-
-        int distFromEdgeOfScreen = mLauncher.getWorkspace().getPaddingLeft() + getPaddingLeft();
-
-        if (grid.isPhone && (grid.availableWidthPx - width) < 4 * distFromEdgeOfScreen) {
-            // Center the folder if it is very close to being centered anyway, by virtue of
-            // filling the majority of the viewport. ie. remove it from the uncanny valley
-            // of centeredness.
-            left = (grid.availableWidthPx - width) / 2;
-        } else if (width >= sTempRect.width()) {
-            // If the folder doesn't fit within the bounds, center it about the desired bounds
-            left = sTempRect.left + (sTempRect.width() - width) / 2;
-        }
-        if (height >= sTempRect.height()) {
-            // Folder height is greater than page height, center on page
-            top = sTempRect.top + (sTempRect.height() - height) / 2;
-        } else {
-            // Folder height is less than page height, so bound it to the absolute open folder
-            // bounds if necessary
-            Rect folderBounds = grid.getAbsoluteOpenFolderBounds();
-            left = Math.max(folderBounds.left, Math.min(left, folderBounds.right - width));
-            top = Math.max(folderBounds.top, Math.min(top, folderBounds.bottom - height));
-        }
+        sTempRect.set(mActivityContext.getFolderBoundingBox());
+        int left = Utilities.boundToRange(centeredLeft, sTempRect.left, sTempRect.right - width);
+        int top = Utilities.boundToRange(centeredTop, sTempRect.top, sTempRect.bottom - height);
+        int[] inOutPosition = new int[]{left, top};
+        mActivityContext.updateOpenFolderPosition(inOutPosition, sTempRect, width, height);
+        left = inOutPosition[0];
+        top = inOutPosition[1];
 
         int folderPivotX = width / 2 + (centeredLeft - left);
         int folderPivotY = height / 2 + (centeredTop - top);
@@ -1078,10 +1125,12 @@
         lp.height = height;
         lp.x = left;
         lp.y = top;
+
+        mBackground.setBounds(0, 0, width, height);
     }
 
     protected int getContentAreaHeight() {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
+        DeviceProfile grid = mActivityContext.getDeviceProfile();
         int maxContentAreaHeight = grid.availableHeightPx - grid.getTotalWorkspacePadding().y
                 - mFooterHeight;
         int height = Math.min(maxContentAreaHeight,
@@ -1117,7 +1166,7 @@
 
         if (mContent.getChildCount() > 0) {
             int cellIconGap = (mContent.getPageAt(0).getCellWidth()
-                    - mLauncher.getDeviceProfile().iconSizePx) / 2;
+                    - mActivityContext.getDeviceProfile().iconSizePx) / 2;
             mFooter.setPadding(mContent.getPaddingLeft() + cellIconGap,
                     mFooter.getPaddingTop(),
                     mContent.getPaddingRight() + cellIconGap,
@@ -1146,54 +1195,8 @@
         return mInfo.contents.size();
     }
 
-    @Thunk void replaceFolderWithFinalItem() {
-        // Add the last remaining child to the workspace in place of the folder
-        Runnable onCompleteRunnable = new Runnable() {
-            @Override
-            public void run() {
-                int itemCount = getItemCount();
-                if (itemCount <= 1) {
-                    View newIcon = null;
-                    WorkspaceItemInfo finalItem = null;
-
-                    if (itemCount == 1) {
-                        // Move the item from the folder to the workspace, in the position of the
-                        // folder
-                        CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container,
-                                mInfo.screenId);
-                        finalItem =  mInfo.contents.remove(0);
-                        newIcon = mLauncher.createShortcut(cellLayout, finalItem);
-                        mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
-                                mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY);
-                    }
-
-                    // Remove the folder
-                    mLauncher.removeItem(mFolderIcon, mInfo, true /* deleteFromDb */);
-                    if (mFolderIcon instanceof DropTarget) {
-                        mDragController.removeDropTarget((DropTarget) mFolderIcon);
-                    }
-
-                    if (newIcon != null) {
-                        // We add the child after removing the folder to prevent both from existing
-                        // at the same time in the CellLayout.  We need to add the new item with
-                        // addInScreenFromBind() to ensure that hotseat items are placed correctly.
-                        mLauncher.getWorkspace().addInScreenFromBind(newIcon, mInfo);
-
-                        // Focus the newly created child
-                        newIcon.requestFocus();
-                    }
-                    if (finalItem != null) {
-                        mLauncher.folderConvertedToItem(mFolderIcon.getFolder(), finalItem);
-                    }
-                }
-            }
-        };
-        View finalChild = mContent.getLastItem();
-        if (finalChild != null) {
-            mFolderIcon.performDestroyAnimation(onCompleteRunnable);
-        } else {
-            onCompleteRunnable.run();
-        }
+    void replaceFolderWithFinalItem() {
+        mLauncherDelegate.replaceFolderWithFinalItem(this);
         mDestroyed = true;
     }
 
@@ -1252,6 +1255,10 @@
             mScrollPauseAlarm.cancelAlarm();
         }
         mContent.completePendingPageChanges();
+        Launcher launcher = mLauncherDelegate.getLauncher();
+        if (launcher == null) {
+            return;
+        }
 
         PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
                 ? (PendingAddShortcutInfo) d.dragInfo : null;
@@ -1261,7 +1268,7 @@
             pasi.container = mInfo.id;
             pasi.rank = mEmptyCellRank;
 
-            mLauncher.addPendingItem(pasi, pasi.container, pasi.screenId, null, pasi.spanX,
+            launcher.addPendingItem(pasi, pasi.container, pasi.screenId, null, pasi.spanX,
                     pasi.spanY);
             d.deferDragViewCleanupPostAnimation = false;
             mRearrangeOnClose = true;
@@ -1283,7 +1290,7 @@
 
                 // Actually move the item in the database if it was an external drag. Call this
                 // before creating the view, so that WorkspaceItemInfo is updated appropriately.
-                mLauncher.getModelWriter().addOrMoveItemInDatabase(
+                mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(
                         si, mInfo.id, 0, si.cellX, si.cellY);
                 mIsExternalDrag = false;
             } else {
@@ -1298,7 +1305,7 @@
                 float scaleY = getScaleY();
                 setScaleX(1.0f);
                 setScaleY(1.0f);
-                mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView, null);
+                launcher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView, null);
                 setScaleX(scaleX);
                 setScaleY(scaleY);
             } else {
@@ -1326,10 +1333,11 @@
 
         if (mContent.getPageCount() > 1) {
             // The animation has already been shown while opening the folder.
-            mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter());
+            mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true,
+                    mLauncherDelegate.getModelWriter());
         }
 
-        mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
+        launcher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
         if (d.stateAnnouncer != null) {
             d.stateAnnouncer.completeAction(R.string.item_moved);
         }
@@ -1346,6 +1354,7 @@
             v.setVisibility(INVISIBLE);
         }
     }
+
     public void showItem(WorkspaceItemInfo info) {
         View v = getViewForInfo(info);
         if (v != null) {
@@ -1356,9 +1365,9 @@
     @Override
     public void onAdd(WorkspaceItemInfo item, int rank) {
         FolderGridOrganizer verifier = new FolderGridOrganizer(
-                mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+                mActivityContext.getDeviceProfile().inv).setFolderInfo(mInfo);
         verifier.updateRankAndPos(item, rank);
-        mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
+        mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
                 item.cellY);
         updateItemLocationsInDatabaseBatch(false);
 
@@ -1368,10 +1377,10 @@
         mItemsInvalidated = true;
     }
 
-    public void onRemove(WorkspaceItemInfo item) {
+    @Override
+    public void onRemove(List<WorkspaceItemInfo> items) {
         mItemsInvalidated = true;
-        View v = getViewForInfo(item);
-        mContent.removeItem(v);
+        items.stream().map(this::getViewForInfo).forEach(mContent::removeItem);
         if (mState == STATE_ANIMATING) {
             mRearrangeOnClose = true;
         } else {
@@ -1468,7 +1477,6 @@
                 }
 
                 statsLogger.log(LAUNCHER_FOLDER_LABEL_UPDATED);
-                logFolderLabelState(mFromLabelState, toLabelState);
                 mFolderName.dispatchBackKey();
             }
         }
@@ -1481,27 +1489,6 @@
         outRect.right += mScrollAreaOffset;
     }
 
-    @Override
-    public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
-            ArrayList<LauncherLogProto.Target> targets) {
-        child.gridX = childInfo.cellX;
-        child.gridY = childInfo.cellY;
-        child.pageIndex = mContent.getCurrentPage();
-
-        LauncherLogProto.Target target = newContainerTarget(LauncherLogProto.ContainerType.FOLDER);
-        target.pageIndex = mInfo.screenId;
-        target.gridX = mInfo.cellX;
-        target.gridY = mInfo.cellY;
-        targets.add(target);
-
-        // continue to parent
-        if (mInfo.container == CONTAINER_HOTSEAT) {
-            mLauncher.getHotseat().fillInLogContainerData(mInfo, target, targets);
-        } else {
-            mLauncher.getWorkspace().fillInLogContainerData(mInfo, target, targets);
-        }
-    }
-
     private class OnScrollHintListener implements OnAlarmListener {
 
         private final DragObject mDragObject;
@@ -1585,19 +1572,8 @@
     /**
      * Returns a folder which is already open or null
      */
-    public static Folder getOpen(Launcher launcher) {
-        return getOpenView(launcher, TYPE_FOLDER);
-    }
-
-    @Override
-    public void logActionCommand(int command) {
-        mLauncher.getUserEventDispatcher().logActionCommand(
-                command, getFolderIcon(), getLogContainerType());
-    }
-
-    @Override
-    public int getLogContainerType() {
-        return LauncherLogProto.ContainerType.FOLDER;
+    public static Folder getOpen(ActivityContext activityContext) {
+        return getOpenView(activityContext, TYPE_FOLDER);
     }
 
     /**
@@ -1616,7 +1592,7 @@
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            DragLayer dl = mLauncher.getDragLayer();
+            BaseDragLayer dl = (BaseDragLayer) getParent();
 
             if (isEditingName()) {
                 if (!dl.isEventOverView(mFolderName, ev)) {
@@ -1624,23 +1600,19 @@
                     return true;
                 }
                 return false;
-            } else if (!dl.isEventOverView(this, ev)) {
-                if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
-                    // Do not close the container if in drag and drop.
-                    if (!dl.isEventOverView(mLauncher.getDropTargetBar(), ev)) {
-                        return true;
-                    }
-                } else {
-                    mLauncher.getUserEventDispatcher().logActionTapOutside(
-                            newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
-                    close(true);
-                    return true;
-                }
+            } else if (!dl.isEventOverView(this, ev)
+                    && mLauncherDelegate.interceptOutsideTouch(ev, dl, this)) {
+                return true;
             }
         }
         return false;
     }
 
+    @Override
+    public boolean canInterceptEventsInSystemGestureRegion() {
+        return true;
+    }
+
     /**
      * Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
      * rounded rect.
@@ -1652,14 +1624,16 @@
     }
 
     @Override
-    public void draw(Canvas canvas) {
+    protected void dispatchDraw(Canvas canvas) {
         if (mClipPath != null) {
             int count = canvas.save();
             canvas.clipPath(mClipPath);
-            super.draw(canvas);
+            mBackground.draw(canvas);
             canvas.restoreToCount(count);
+            super.dispatchDraw(canvas);
         } else {
-            super.draw(canvas);
+            mBackground.draw(canvas);
+            super.dispatchDraw(canvas);
         }
     }
 
@@ -1667,14 +1641,12 @@
         return mContent;
     }
 
-    /**
-     * Logs current folder label info.
-     *
-     * @deprecated This method is only used for log validation and soon will be removed.
-     */
-    @Deprecated
-    public void logFolderLabelState(FromState fromState, ToState toState) {
-        mLauncher.getUserEventDispatcher()
-                .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent(fromState, toState));
+    /** Returns the height of the current folder's bottom edge from the bottom of the screen. */
+    private int getHeightFromBottom() {
+        BaseDragLayer.LayoutParams layoutParams = (BaseDragLayer.LayoutParams) getLayoutParams();
+        int folderBottomPx = layoutParams.y + layoutParams.height;
+        int windowBottomPx = mActivityContext.getDeviceProfile().heightPx;
+
+        return windowBottomPx - folderBottomPx;
     }
 }
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 3d72b49..cb3884d 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.folder;
 
+import static android.view.View.ALPHA;
+
 import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
@@ -35,18 +37,15 @@
 import android.view.View;
 import android.view.animation.AnimationUtils;
 
-import androidx.core.graphics.ColorUtils;
-
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PropertyResetListener;
-import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.List;
 
@@ -60,6 +59,7 @@
 public class FolderAnimationManager {
 
     private static final int FOLDER_NAME_ALPHA_DURATION = 32;
+    private static final int LARGE_FOLDER_FOOTER_DURATION = 128;
 
     private Folder mFolder;
     private FolderPagedView mContent;
@@ -69,7 +69,6 @@
     private PreviewBackground mPreviewBackground;
 
     private Context mContext;
-    private Launcher mLauncher;
 
     private final boolean mIsOpening;
 
@@ -80,9 +79,13 @@
     private final TimeInterpolator mLargeFolderPreviewItemOpenInterpolator;
     private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
 
-    private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+    private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0);
     private final FolderGridOrganizer mPreviewVerifier;
 
+    private ObjectAnimator mBgColorAnimator;
+
+    private DeviceProfile mDeviceProfile;
+
     public FolderAnimationManager(Folder folder, boolean isOpening) {
         mFolder = folder;
         mContent = folder.mContent;
@@ -92,8 +95,8 @@
         mPreviewBackground = mFolderIcon.mBackground;
 
         mContext = folder.getContext();
-        mLauncher = folder.mLauncher;
-        mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
+        mDeviceProfile = folder.mActivityContext.getDeviceProfile();
+        mPreviewVerifier = new FolderGridOrganizer(mDeviceProfile.inv);
 
         mIsOpening = isOpening;
 
@@ -109,19 +112,26 @@
                 R.interpolator.large_folder_preview_item_close_interpolator);
     }
 
+    /**
+     * Returns the animator that changes the background color.
+     */
+    public ObjectAnimator getBgColorAnimator() {
+        return mBgColorAnimator;
+    }
 
     /**
      * Prepares the Folder for animating between open / closed states.
      */
     public AnimatorSet getAnimator() {
-        final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
+        final BaseDragLayer.LayoutParams lp =
+                (BaseDragLayer.LayoutParams) mFolder.getLayoutParams();
         mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams();
         ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
         final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
 
         // Match position of the FolderIcon
         final Rect folderIconPos = new Rect();
-        float scaleRelativeToDragLayer = mLauncher.getDragLayer()
+        float scaleRelativeToDragLayer = mFolder.mActivityContext.getDragLayer()
                 .getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
         int scaledRadius = mPreviewBackground.getScaledRadius();
         float initialSize = (scaledRadius * 2) * scaleRelativeToDragLayer;
@@ -165,10 +175,15 @@
         final float yDistance = initialY - lp.y;
 
         // Set up the Folder background.
-        final int finalColor = ColorUtils.setAlphaComponent(
-                Themes.getAttrColor(mContext, R.attr.folderFillColor), 255);
+        final int finalColor;
+        int folderFillColor = Themes.getAttrColor(mContext, R.attr.folderFillColor);
+        if (mIsOpening) {
+            finalColor = folderFillColor;
+        } else {
+            finalColor = mFolderBackground.getColor().getDefaultColor();
+        }
         final int initialColor = setColorAlphaBound(
-                finalColor, mPreviewBackground.getBackgroundAlpha());
+                folderFillColor, mPreviewBackground.getBackgroundAlpha());
         mFolderBackground.mutate();
         mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
 
@@ -179,7 +194,7 @@
                 Math.round((totalOffsetX + initialSize)),
                 Math.round((paddingOffsetY + initialSize)));
         Rect endRect = new Rect(0, 0, lp.width, lp.height);
-        float finalRadius = ResourceUtils.pxFromDp(2, mContext.getResources().getDisplayMetrics());
+        float finalRadius = mFolderBackground.getCornerRadius();
 
         // Create the animators.
         AnimatorSet a = new AnimatorSet();
@@ -196,14 +211,46 @@
             play(a, anim);
         }
 
+        mBgColorAnimator = getAnimator(mFolderBackground, "color", initialColor, finalColor);
+        play(a, mBgColorAnimator);
         play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f));
         play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
         play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale));
         play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale));
-        play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
-        play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
+
+        final int footerAlphaDuration;
+        final int footerStartDelay;
+        if (isLargeFolder()) {
+            if (mIsOpening) {
+                footerAlphaDuration = LARGE_FOLDER_FOOTER_DURATION;
+                footerStartDelay = mDuration - footerAlphaDuration;
+            } else {
+                footerAlphaDuration = 0;
+                footerStartDelay = 0;
+            }
+        } else {
+            footerStartDelay = 0;
+            footerAlphaDuration = mDuration;
+        }
+        play(a, getAnimator(mFolder.mFooter, ALPHA, 0, 1f), footerStartDelay, footerAlphaDuration);
+
+        // Create reveal animator for the folder background
         play(a, getShape().createRevealAnimator(
                 mFolder, startRect, endRect, finalRadius, !mIsOpening));
+
+        // Create reveal animator for the folder content (capture the top 4 icons 2x2)
+        int width = mDeviceProfile.folderCellLayoutBorderSpacingPx
+                + mDeviceProfile.folderCellWidthPx * 2;
+        int height = mDeviceProfile.folderCellLayoutBorderSpacingPx
+                + mDeviceProfile.folderCellHeightPx * 2;
+        int page = mIsOpening ? mContent.getCurrentPage() : mContent.getDestinationPage();
+        int left = mContent.getPaddingLeft() + page * lp.width;
+        Rect contentStart = new Rect(left, 0, left + width, height);
+        Rect contentEnd = new Rect(left, 0, left + lp.width, lp.height);
+        play(a, getShape().createRevealAnimator(
+                mFolder.getContent(), contentStart, contentEnd, finalRadius, !mIsOpening));
+
+
         // Fade in the folder name, as the text can overlap the icons when grid size is small.
         mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f);
         play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1),
@@ -326,7 +373,9 @@
 
             final int previewPosX =
                     (int) ((mTmpParams.transX - iconOffsetX + previewItemOffsetX) / folderScale);
-            final int previewPosY = (int) ((mTmpParams.transY + previewItemOffsetY) / folderScale);
+            final float paddingTop = btv.getPaddingTop() * iconScale;
+            final int previewPosY = (int) ((mTmpParams.transY + previewItemOffsetY - paddingTop)
+                    / folderScale);
 
             final float xDistance = previewPosX - btvLp.x;
             final float yDistance = previewPosY - btvLp.y;
@@ -392,8 +441,12 @@
         as.play(a);
     }
 
+    private boolean isLargeFolder() {
+        return mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW;
+    }
+
     private TimeInterpolator getPreviewItemInterpolator() {
-        if (mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW) {
+        if (isLargeFolder()) {
             // With larger folders, we want the preview items to reach their final positions faster
             // (when opening) and later (when closing) so that they appear aligned with the rest of
             // the folder items when they are both visible.
@@ -410,7 +463,7 @@
                 : ObjectAnimator.ofFloat(view, property, v2, v1);
     }
 
-    private Animator getAnimator(GradientDrawable drawable, String property, int v1, int v2) {
+    private ObjectAnimator getAnimator(GradientDrawable drawable, String property, int v1, int v2) {
         return mIsOpening
                 ? ObjectAnimator.ofArgb(drawable, property, v1, v2)
                 : ObjectAnimator.ofArgb(drawable, property, v2, v1);
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 32d061c..96030f9 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.folder;
 
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELED;
@@ -113,7 +114,7 @@
     FolderGridOrganizer mPreviewVerifier;
     ClippedFolderIconLayoutRule mPreviewLayoutRule;
     private PreviewItemManager mPreviewItemManager;
-    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0);
     private List<WorkspaceItemInfo> mCurrentPreviewItems = new ArrayList<>();
 
     boolean mAnimating = false;
@@ -166,17 +167,14 @@
         mDotParams = new DotRenderer.DrawParams();
     }
 
-    public static FolderIcon inflateFolderAndIcon(int resId, Launcher launcher, ViewGroup group,
-            FolderInfo folderInfo) {
-        Folder folder = Folder.fromXml(launcher);
-        folder.setDragController(launcher.getDragController());
+    public static <T extends Context & ActivityContext> FolderIcon inflateFolderAndIcon(int resId,
+            T activityContext, ViewGroup group, FolderInfo folderInfo) {
+        Folder folder = Folder.fromXml(activityContext);
 
-        FolderIcon icon = inflateIcon(resId, launcher, group, folderInfo);
+        FolderIcon icon = inflateIcon(resId, activityContext, group, folderInfo);
         folder.setFolderIcon(icon);
         folder.bind(folderInfo);
         icon.setFolder(folder);
-
-        icon.setOnFocusChangeListener(launcher.getFocusHandler());
         return icon;
     }
 
@@ -239,6 +237,8 @@
     public void getPreviewBounds(Rect outBounds) {
         mPreviewItemManager.recomputePreviewDrawingParams();
         mBackground.getBounds(outBounds);
+        // The preview items go outside of the bounds of the background.
+        Utilities.scaleRectAboutCenter(outBounds, ICON_OVERLAP_FACTOR);
     }
 
     public float getBackgroundStrokeWidth() {
@@ -391,7 +391,7 @@
             to.offset(center[0] - animateView.getMeasuredWidth() / 2,
                     center[1] - animateView.getMeasuredHeight() / 2);
 
-            float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
+            float finalAlpha = index < MAX_NUM_ITEMS_IN_PREVIEW ? 1f : 0f;
 
             float finalScale = scale * scaleRelativeToDragLayer;
 
@@ -402,15 +402,18 @@
                 finalScale *= containerScale;
             }
 
+            final int finalIndex = index;
             dragLayer.animateView(animateView, from, to, finalAlpha,
                     1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
                     Interpolators.DEACCEL_2, Interpolators.ACCEL_2,
-                    null, DragLayer.ANIMATION_END_DISAPPEAR, null);
+                    () -> {
+                        mPreviewItemManager.hidePreviewItem(finalIndex, false);
+                        mFolder.showItem(item);
+                    }, DragLayer.ANIMATION_END_DISAPPEAR, null);
 
             mFolder.hideItem(item);
 
             if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
-            final int finalIndex = index;
 
             FolderNameInfos nameInfos = new FolderNameInfos();
             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
@@ -430,8 +433,6 @@
     private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
             FolderNameInfos nameInfos, InstanceId instanceId) {
         postDelayed(() -> {
-            mPreviewItemManager.hidePreviewItem(finalIndex, false);
-            mFolder.showItem(item);
             setLabelSuggestion(nameInfos, instanceId);
             invalidate();
         }, DROP_IN_ANIMATION_DURATION);
@@ -464,7 +465,7 @@
         CharSequence newTitle = nameInfos.getLabels()[0];
         FromState fromState = mInfo.getFromLabelState();
 
-        mInfo.setTitle(newTitle, mFolder.mLauncher.getModelWriter());
+        mInfo.setTitle(newTitle, mFolder.mLauncherDelegate.getModelWriter());
         onTitleChanged(mInfo.title);
         mFolder.mFolderName.setText(mInfo.title);
 
@@ -478,7 +479,6 @@
                 // event is assumed to be folder creation on the server side.
                 .withEditText(newTitle.toString())
                 .log(LAUNCHER_FOLDER_AUTO_LABELED);
-        mFolder.logFolderLabelState(fromState, ToState.TO_SUGGESTION0);
     }
 
 
@@ -614,10 +614,7 @@
 
         if (mCurrentPreviewItems.isEmpty() && !mAnimating) return;
 
-        final int saveCount = canvas.save();
-        canvas.clipPath(mBackground.getClipPath());
         mPreviewItemManager.draw(canvas);
-        canvas.restoreToCount(saveCount);
 
         if (!mBackground.drawingDelegated()) {
             mBackground.drawBackgroundStroke(canvas);
@@ -696,9 +693,9 @@
     }
 
     @Override
-    public void onRemove(WorkspaceItemInfo item) {
+    public void onRemove(List<WorkspaceItemInfo> items) {
         boolean wasDotted = mDotInfo.hasDot();
-        mDotInfo.subtractDotInfo(mActivity.getDotInfoForItem(item));
+        items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
         boolean isDotted = mDotInfo.hasDot();
         updateDotScale(wasDotted, isDotted);
         setContentDescription(getAccessiblityTitle(mInfo.title));
@@ -746,21 +743,19 @@
         mInfo.removeListener(mFolder);
     }
 
+    private boolean isInHotseat() {
+        return mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+    }
+
     public void clearLeaveBehindIfExists() {
-        ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
-        if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            CellLayout cl = (CellLayout) getParent().getParent();
-            cl.clearFolderLeaveBehind();
+        if (getParent() instanceof FolderIconParent) {
+            ((FolderIconParent) getParent()).clearFolderLeaveBehind(this);
         }
     }
 
     public void drawLeaveBehindIfExists() {
-        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
-        // While the folder is open, the position of the icon cannot change.
-        lp.canReorder = false;
-        if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            CellLayout cl = (CellLayout) getParent().getParent();
-            cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
+        if (getParent() instanceof FolderIconParent) {
+            ((FolderIconParent) getParent()).drawFolderLeaveBehindForIcon(this);
         }
     }
 
@@ -829,4 +824,19 @@
                     MAX_NUM_ITEMS_IN_PREVIEW);
         }
     }
+
+    /**
+     * Interface that provides callbacks to a parent ViewGroup that hosts this FolderIcon.
+     */
+    public interface FolderIconParent {
+        /**
+         * Tells the FolderIconParent to draw a "leave-behind" when the Folder is open and leaving a
+         * gap where the FolderIcon would be when the Folder is closed.
+         */
+        void drawFolderLeaveBehindForIcon(FolderIcon child);
+        /**
+         * Tells the FolderIconParent to stop drawing the "leave-behind" as the Folder is closed.
+         */
+        void clearFolderLeaveBehind(FolderIcon child);
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index d166e27..9c1b24d 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -48,7 +48,7 @@
 public class FolderNameProvider implements ResourceBasedOverride {
 
     private static final String TAG = "FolderNameProvider";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     /**
      * IME usually has up to 3 suggest slots. In total, there are 4 suggest slots as the folder
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index a08dd30..3d2884a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -22,6 +22,7 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Path;
 import android.graphics.drawable.Drawable;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
@@ -31,19 +32,16 @@
 import android.view.ViewDebug;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.PagedView;
 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.anim.Interpolators;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -51,6 +49,8 @@
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewCache;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.ClipPathView;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -59,7 +59,7 @@
 import java.util.function.ToIntFunction;
 import java.util.stream.Collectors;
 
-public class FolderPagedView extends PagedView<PageIndicatorDots> {
+public class FolderPagedView extends PagedView<PageIndicatorDots> implements ClipPathView {
 
     private static final String TAG = "FolderPagedView";
 
@@ -91,6 +91,8 @@
 
     private Folder mFolder;
 
+    private Path mClipPath;
+
     // If the views are attached to the folder or not. A folder should be bound when its
     // animating or is open.
     private boolean mViewsBound = false;
@@ -104,7 +106,7 @@
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
 
         mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
-        mViewCache = BaseActivity.fromContext(context).getViewCache();
+        mViewCache = ActivityContext.lookupContext(context).getViewCache();
     }
 
     public void setFolder(Folder folder) {
@@ -130,8 +132,16 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        mFocusIndicatorHelper.draw(canvas);
-        super.dispatchDraw(canvas);
+        if (mClipPath != null) {
+            int count = canvas.save();
+            canvas.clipPath(mClipPath);
+            mFocusIndicatorHelper.draw(canvas);
+            super.dispatchDraw(canvas);
+            canvas.restoreToCount(count);
+        } else {
+            mFocusIndicatorHelper.draw(canvas);
+            super.dispatchDraw(canvas);
+        }
     }
 
     /**
@@ -193,7 +203,7 @@
         int pageNo = rank / mOrganizer.getMaxItemsPerPage();
 
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
-        lp.setXY(mOrganizer.getPosForRank(rank));
+        lp.setCellXY(mOrganizer.getPosForRank(rank));
         getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
     }
 
@@ -230,7 +240,7 @@
     }
 
     private CellLayout createAndAddNewPage() {
-        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+        DeviceProfile grid = mFolder.mActivityContext.getDeviceProfile();
         CellLayout page = mViewCache.getView(R.layout.folder_page, getContext(), this);
         page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
         page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
@@ -306,7 +316,7 @@
             if (v != null) {
                 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
                 ItemInfo info = (ItemInfo) v.getTag();
-                lp.setXY(mOrganizer.getPosForRank(rank));
+                lp.setCellXY(mOrganizer.getPosForRank(rank));
                 currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
 
                 if (mOrganizer.isItemInPreview(rank) && v instanceof BubbleTextView) {
@@ -434,8 +444,7 @@
         int scroll = getScrollForPage(getNextPage()) + hint;
         int delta = scroll - getScrollX();
         if (delta != 0) {
-            mScroller.setInterpolator(Interpolators.DEACCEL);
-            mScroller.startScroll(getScrollX(), delta, Folder.SCROLL_HINT_DURATION);
+            mScroller.startScroll(getScrollX(), 0, delta, 0, Folder.SCROLL_HINT_DURATION);
             invalidate();
         }
     }
@@ -484,7 +493,7 @@
                 icon.verifyHighRes();
                 // Set the callback back to the actual icon, in case
                 // it was captured by the FolderIcon
-                Drawable d = icon.getCompoundDrawables()[1];
+                Drawable d = icon.getIcon();
                 if (d != null) {
                     d.setCallback(icon);
                 }
@@ -624,11 +633,17 @@
 
     @Override
     protected boolean canScroll(float absVScroll, float absHScroll) {
-        return AbstractFloatingView.getTopOpenViewWithType(mFolder.mLauncher,
+        return AbstractFloatingView.getTopOpenViewWithType(mFolder.mActivityContext,
                 TYPE_ALL & ~TYPE_FOLDER) == null;
     }
 
     public int itemsPerPage() {
         return mOrganizer.getMaxItemsPerPage();
     }
+
+    @Override
+    public void setClipPath(Path clipPath) {
+        mClipPath = clipPath;
+        invalidate();
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
index 22f7333..e20bafb 100644
--- a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
+++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
@@ -45,7 +45,7 @@
             };
 
     private static final PreviewItemDrawingParams sTmpParams =
-            new PreviewItemDrawingParams(0, 0, 0, 0);
+            new PreviewItemDrawingParams(0, 0, 0);
     private static final float[] sTempParamsArray = new float[3];
 
     private final ObjectAnimator mAnimator;
@@ -68,6 +68,7 @@
             int duration, final Runnable onCompleteRunnable) {
         mItemManager = itemManager;
         mParams = params;
+        mParams.index = index1;
 
         mItemManager.computePreviewItemDrawingParams(index1, items1, sTmpParams);
         finalState = new float[] {sTmpParams.scale, sTmpParams.transX, sTmpParams.transY};
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
new file mode 100644
index 0000000..f7d8e8c
--- /dev/null
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -0,0 +1,225 @@
+/*
+ * 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.folder;
+
+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;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.data.FolderInfo;
+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;
+
+/**
+ * Wrapper around Launcher methods to allow folders in non-launcher context
+ */
+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;
+    }
+
+    void init(Folder folder, FolderIcon icon) {
+        folder.setDragController(mLauncher.getDragController());
+        icon.setOnFocusChangeListener(mLauncher.getFocusHandler());
+    }
+
+    boolean isDraggingEnabled() {
+        return mLauncher.isDraggingEnabled();
+    }
+
+    void beginDragShared(View child, DragSource source, DragOptions options) {
+        mLauncher.getWorkspace().beginDragShared(child, source, options);
+    }
+
+    ModelWriter getModelWriter() {
+        return mLauncher.getModelWriter();
+    }
+
+    void forEachVisibleWorkspacePage(Consumer<View> callback) {
+        mLauncher.getWorkspace().forEachVisiblePage(callback);
+    }
+
+    @Nullable
+    Launcher getLauncher() {
+        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) {
+        // Add the last remaining child to the workspace in place of the folder
+        Runnable onCompleteRunnable = new Runnable() {
+            @Override
+            public void run() {
+                int itemCount = folder.getItemCount();
+                FolderInfo info = folder.mInfo;
+                if (itemCount <= 1) {
+                    View newIcon = null;
+                    WorkspaceItemInfo finalItem = null;
+
+                    if (itemCount == 1) {
+                        // Move the item from the folder to the workspace, in the position of the
+                        // folder
+                        CellLayout cellLayout = mLauncher.getCellLayout(info.container,
+                                info.screenId);
+                        finalItem =  info.contents.remove(0);
+                        newIcon = mLauncher.createShortcut(cellLayout, finalItem);
+                        mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
+                                info.container, info.screenId, info.cellX, info.cellY);
+                    }
+
+                    // Remove the folder
+                    mLauncher.removeItem(folder.mFolderIcon, info, true /* deleteFromDb */);
+                    if (folder.mFolderIcon instanceof DropTarget) {
+                        folder.mDragController.removeDropTarget((DropTarget) folder.mFolderIcon);
+                    }
+
+                    if (newIcon != null) {
+                        // We add the child after removing the folder to prevent both from existing
+                        // at the same time in the CellLayout.  We need to add the new item with
+                        // addInScreenFromBind() to ensure that hotseat items are placed correctly.
+                        mLauncher.getWorkspace().addInScreenFromBind(newIcon, info);
+
+                        // Focus the newly created child
+                        newIcon.requestFocus();
+                    }
+                    if (finalItem != null) {
+                        StatsLogger logger = mLauncher.getStatsLogManager().logger()
+                                .withItemInfo(finalItem);
+                        ((Optional<InstanceId>) folder.mDragController.getLogInstanceId())
+                                .map(logger::withInstanceId)
+                                .orElse(logger)
+                                .log(LAUNCHER_FOLDER_CONVERTED_TO_ICON);
+                    }
+                }
+            }
+        };
+        View finalChild = folder.mContent.getLastItem();
+        if (finalChild != null) {
+            folder.mFolderIcon.performDestroyAnimation(onCompleteRunnable);
+        } else {
+            onCompleteRunnable.run();
+        }
+    }
+
+
+    boolean interceptOutsideTouch(MotionEvent ev, BaseDragLayer dl, Folder folder) {
+        if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
+            // Do not close the container if in drag and drop.
+            if (!dl.isEventOverView(mLauncher.getDropTargetBar(), ev)) {
+                return true;
+            }
+        } else {
+            // TODO: add ww log if need to gather tap outside to close folder
+            folder.close(true);
+            return true;
+        }
+        return false;
+    }
+
+    private static class FallbackDelegate extends LauncherDelegate {
+
+        private final ActivityContext mContext;
+        private ModelWriter mWriter;
+
+        FallbackDelegate(ActivityContext context) {
+            super(null);
+            mContext = context;
+        }
+
+        @Override
+        void init(Folder folder, FolderIcon icon) {
+            folder.setDragController(mContext.getDragController());
+        }
+
+        @Override
+        boolean isDraggingEnabled() {
+            return false;
+        }
+
+        @Override
+        void beginDragShared(View child, DragSource source, DragOptions options) { }
+
+        @Override
+        ModelWriter getModelWriter() {
+            if (mWriter == null) {
+                mWriter = LauncherAppState.getInstance((Context) mContext).getModel()
+                        .getWriter(false, false);
+            }
+            return mWriter;
+        }
+
+        @Override
+        void forEachVisibleWorkspacePage(Consumer<View> callback) { }
+
+        @Override
+        Launcher getLauncher() {
+            return null;
+        }
+
+        @Override
+        void replaceFolderWithFinalItem(Folder folder) { }
+
+        @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) {
+        return context instanceof Launcher
+                ? new LauncherDelegate((Launcher) context)
+                : new FallbackDelegate(context);
+    }
+}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 27b906b..18d0b10 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.folder;
 
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
 import static com.android.launcher3.graphics.IconShape.getShape;
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
@@ -50,6 +51,9 @@
  */
 public class PreviewBackground extends CellLayout.DelegatedCellDrawing {
 
+    private static final boolean DRAW_SHADOW = false;
+    private static final boolean DRAW_STROKE = false;
+
     private static final int CONSUMPTION_ANIMATION_DURATION = 100;
 
     private final PorterDuffXfermode mShadowPorterDuffXfermode
@@ -86,8 +90,8 @@
     private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
 
     // Expressed on a scale from 0 to 255.
-    private static final int BG_OPACITY = 160;
-    private static final int MAX_BG_OPACITY = 225;
+    private static final int BG_OPACITY = 255;
+    private static final int MAX_BG_OPACITY = 255;
     private static final int SHADOW_OPACITY = 40;
 
     private ValueAnimator mScaleAnimator;
@@ -162,13 +166,15 @@
         // Stroke width is 1dp
         mStrokeWidth = context.getResources().getDisplayMetrics().density;
 
-        float radius = getScaledRadius();
-        float shadowRadius = radius + mStrokeWidth;
-        int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
-        mShadowShader = new RadialGradient(0, 0, 1,
-                new int[] {shadowColor, Color.TRANSPARENT},
-                new float[] {radius / shadowRadius, 1},
-                Shader.TileMode.CLAMP);
+        if (DRAW_SHADOW) {
+            float radius = getScaledRadius();
+            float shadowRadius = radius + mStrokeWidth;
+            int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
+            mShadowShader = new RadialGradient(0, 0, 1,
+                    new int[]{shadowColor, Color.TRANSPARENT},
+                    new float[]{radius / shadowRadius, 1},
+                    Shader.TileMode.CLAMP);
+        }
 
         invalidate();
     }
@@ -181,7 +187,7 @@
         outBounds.set(left, top, right, bottom);
     }
 
-    int getRadius() {
+    public int getRadius() {
         return previewSize / 2;
     }
 
@@ -238,6 +244,9 @@
     }
 
     public void drawShadow(Canvas canvas) {
+        if (!DRAW_SHADOW) {
+            return;
+        }
         if (mShadowShader == null) {
             return;
         }
@@ -276,6 +285,9 @@
     }
 
     public void fadeInBackgroundShadow() {
+        if (!DRAW_SHADOW) {
+            return;
+        }
         if (mShadowAnimator != null) {
             mShadowAnimator.cancel();
         }
@@ -292,6 +304,10 @@
     }
 
     public void animateBackgroundStroke() {
+        if (!DRAW_STROKE) {
+            return;
+        }
+
         if (mStrokeAlphaAnimator != null) {
             mStrokeAlphaAnimator.cancel();
         }
@@ -308,6 +324,9 @@
     }
 
     public void drawBackgroundStroke(Canvas canvas) {
+        if (!DRAW_STROKE) {
+            return;
+        }
         mPaint.setColor(setColorAlphaBound(mStrokeColor, mStrokeAlpha));
         mPaint.setStyle(Paint.Style.STROKE);
         mPaint.setStrokeWidth(mStrokeWidth);
@@ -330,7 +349,12 @@
 
     public Path getClipPath() {
         mPath.reset();
-        getShape().addToPath(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
+        float radius = getScaledRadius() * ICON_OVERLAP_FACTOR;
+        // Find the difference in radius so that the clip path remains centered.
+        float radiusDifference = radius - getRadius();
+        float offsetX = basePreviewOffsetX - radiusDifference;
+        float offsetY = basePreviewOffsetY - radiusDifference;
+        getShape().addToPath(mPath, offsetX, offsetY, radius);
         return mPath;
     }
 
@@ -352,7 +376,7 @@
         }
 
         mDrawingDelegate = null;
-        isClipping = true;
+        isClipping = false;
         invalidate();
     }
 
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
index a14a0d8..58efdc1 100644
--- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -23,20 +23,19 @@
  * Manages the parameters used to draw a Folder preview item.
  */
 class PreviewItemDrawingParams {
+    float index;
     float transX;
     float transY;
     float scale;
-    float overlayAlpha;
     public FolderPreviewItemAnim anim;
     public boolean hidden;
     public Drawable drawable;
     public WorkspaceItemInfo item;
 
-    PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
+    PreviewItemDrawingParams(float transX, float transY, float scale) {
         this.transX = transX;
         this.transY = transY;
         this.scale = scale;
-        this.overlayAlpha = overlayAlpha;
     }
 
     public void update(float transX, float transY, float scale) {
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 7f8a15c..8bef6ad 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.folder;
 
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
@@ -29,16 +28,19 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.FloatProperty;
 import android.view.View;
-import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
 
@@ -81,6 +83,10 @@
     // These hold the current page preview items. It is empty if the current page is the first page.
     private ArrayList<PreviewItemDrawingParams> mCurrentPageParams = new ArrayList<>();
 
+    // We clip the preview items during the middle of the animation, so that it does not go outside
+    // of the visual shape. We stop clipping at this threshold, since the preview items ultimately
+    // do not get cropped in their resting state.
+    private final float mClipThreshold;
     private float mCurrentPageItemsTransX = 0;
     private boolean mShouldSlideInFirstPage;
 
@@ -96,6 +102,7 @@
         mIcon = icon;
         mIconSize = ActivityContext.lookupContext(
                 mContext).getDeviceProfile().folderChildIconSizePx;
+        mClipThreshold = Utilities.dpToPx(1f);
     }
 
     /**
@@ -112,7 +119,7 @@
     }
 
     Drawable prepareCreateAnimation(final View destView) {
-        Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
+        Drawable animateDrawable = ((BubbleTextView) destView).getIcon();
         computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
                 destView.getMeasuredWidth());
         mReferenceDrawable = animateDrawable;
@@ -163,41 +170,60 @@
     }
 
     public void drawParams(Canvas canvas, ArrayList<PreviewItemDrawingParams> params,
-            float transX) {
-        canvas.translate(transX, 0);
+            PointF offset, boolean shouldClipPath, Path clipPath) {
         // The first item should be drawn last (ie. on top of later items)
         for (int i = params.size() - 1; i >= 0; i--) {
             PreviewItemDrawingParams p = params.get(i);
             if (!p.hidden) {
-                drawPreviewItem(canvas, p);
+                // Exiting param should always be clipped.
+                boolean isExiting = p.index == EXIT_INDEX;
+                drawPreviewItem(canvas, p, offset, isExiting | shouldClipPath, clipPath);
             }
         }
-        canvas.translate(-transX, 0);
     }
 
+    /**
+     * Draws the preview items on {@param canvas}.
+     */
     public void draw(Canvas canvas) {
+        int saveCount = canvas.getSaveCount();
         // The items are drawn in coordinates relative to the preview offset
         PreviewBackground bg = mIcon.getFolderBackground();
-        canvas.translate(bg.basePreviewOffsetX, bg.basePreviewOffsetY);
-
+        Path clipPath = bg.getClipPath();
         float firstPageItemsTransX = 0;
         if (mShouldSlideInFirstPage) {
-            drawParams(canvas, mCurrentPageParams, mCurrentPageItemsTransX);
-
+            PointF firstPageOffset = new PointF(bg.basePreviewOffsetX + mCurrentPageItemsTransX,
+                    bg.basePreviewOffsetY);
+            boolean shouldClip = mCurrentPageItemsTransX > mClipThreshold;
+            drawParams(canvas, mCurrentPageParams, firstPageOffset, shouldClip, clipPath);
             firstPageItemsTransX = -ITEM_SLIDE_IN_OUT_DISTANCE_PX + mCurrentPageItemsTransX;
         }
 
-        drawParams(canvas, mFirstPageParams, firstPageItemsTransX);
-        canvas.translate(-bg.basePreviewOffsetX, -bg.basePreviewOffsetY);
+        PointF firstPageOffset = new PointF(bg.basePreviewOffsetX + firstPageItemsTransX,
+                bg.basePreviewOffsetY);
+        boolean shouldClipFirstPage = firstPageItemsTransX < -mClipThreshold;
+        drawParams(canvas, mFirstPageParams, firstPageOffset, shouldClipFirstPage, clipPath);
+        canvas.restoreToCount(saveCount);
     }
 
     public void onParamsChanged() {
         mIcon.invalidate();
     }
 
-    private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
+    /**
+     * Draws each preview item.
+     *
+     * @param offset The offset needed to draw the preview items.
+     * @param shouldClipPath Iff true, clip path using {@param clipPath}.
+     * @param clipPath The clip path of the folder icon.
+     */
+    private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params, PointF offset,
+            boolean shouldClipPath, Path clipPath) {
         canvas.save();
-        canvas.translate(params.transX, params.transY);
+        if (shouldClipPath) {
+            canvas.clipPath(clipPath);
+        }
+        canvas.translate(offset.x + params.transX, offset.y + params.transY);
         canvas.scale(params.scale, params.scale);
         Drawable d = params.drawable;
 
@@ -234,7 +260,7 @@
             params.remove(params.size() - 1);
         }
         while (items.size() > params.size()) {
-            params.add(new PreviewItemDrawingParams(0, 0, 0, 0));
+            params.add(new PreviewItemDrawingParams(0, 0, 0));
         }
 
         int numItemsInFirstPagePreview = page == 0 ? items.size() : MAX_NUM_ITEMS_IN_PREVIEW;
@@ -243,6 +269,9 @@
             setDrawable(p, items.get(i));
 
             if (!animate) {
+                if (p.anim != null) {
+                    p.anim.cancel();
+                }
                 computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, p);
                 if (mReferenceDrawable == null) {
                     mReferenceDrawable = p.drawable;
@@ -394,12 +423,13 @@
     }
 
     private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
-        if (item.hasPromiseIconUi()) {
+        if (item.hasPromiseIconUi() || (item.runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
             PreloadIconDrawable drawable = newPendingIcon(mContext, item);
-            drawable.setLevel(item.getInstallProgress());
+            drawable.setLevel(item.getProgressLevel());
             p.drawable = drawable;
         } else {
-            p.drawable = newIcon(mContext, item);
+            p.drawable = item.newIcon(mContext, true);
         }
         p.drawable.setBounds(0, 0, mIconSize, mIconSize);
         p.item = item;
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 21822a3..a549750 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -29,12 +29,15 @@
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
+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.widget.LauncherAppWidgetHostView;
 
@@ -88,10 +91,14 @@
     }
 
     /**
-     * Returns a new bitmap to show when the {@link #mView} is being dragged around.
-     * Responsibility for the bitmap is transferred to the caller.
+     * Returns a new drawable to show when the {@link #mView} is being dragged around.
+     * Responsibility for the drawable is transferred to the caller.
      */
-    public Bitmap createDragBitmap() {
+    public Drawable createDrawable() {
+        if (mView instanceof LauncherAppWidgetHostView) {
+            return null;
+        }
+
         int width = 0;
         int height = 0;
         // Assume scaleX == scaleY, which is always the case for workspace items.
@@ -105,8 +112,21 @@
             height = mView.getHeight();
         }
 
-        return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
-                height + blurSizeOutline, (c) -> drawDragView(c, scale));
+        return new FastBitmapDrawable(
+                BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
+                        height + blurSizeOutline, (c) -> drawDragView(c, scale)));
+    }
+
+    /**
+     * Returns the content view if the content should be rendered directly in
+     * {@link com.android.launcher3.dragndrop.DragView}. Otherwise, returns null.
+     */
+    @Nullable
+    public View getContentView() {
+        if (mView instanceof LauncherAppWidgetHostView) {
+            return mView;
+        }
+        return null;
     }
 
     public final void generateDragOutline(Bitmap preview) {
@@ -129,7 +149,7 @@
         return bounds;
     }
 
-    public float getScaleAndPosition(Bitmap preview, int[] outPos) {
+    public float getScaleAndPosition(Drawable preview, int[] outPos) {
         float scale = Launcher.getLauncher(mView.getContext())
                 .getDragLayer().getLocationInDragLayer(mView, outPos);
         if (mView instanceof LauncherAppWidgetHostView) {
@@ -139,12 +159,28 @@
         }
 
         outPos[0] = Math.round(outPos[0] -
-                (preview.getWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2);
-        outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getHeight() / 2
+                (preview.getIntrinsicWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2);
+        outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getIntrinsicHeight() / 2
                 - previewPadding / 2);
         return scale;
     }
 
+    /** 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())
+                .getDragLayer().getLocationInDragLayer(mView, outPos);
+        if (mView instanceof LauncherAppWidgetHostView) {
+            // App widgets are technically scaled, but are drawn at their expected size -- so the
+            // app widget scale should not affect the scale of the preview.
+            scale /= ((LauncherAppWidgetHostView) mView).getScaleToFit();
+        }
+
+        outPos[0] = Math.round(outPos[0]
+                - (view.getWidth() - scale * mView.getWidth() * mView.getScaleX()) / 2);
+        outPos[1] = Math.round(outPos[1] - (1 - scale) * view.getHeight() / 2 - previewPadding / 2);
+        return scale;
+    }
+
     protected Bitmap convertPreviewToAlphaBitmap(Bitmap preview) {
         return preview.copy(Bitmap.Config.ALPHA_8, true);
     }
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index cb42e7a..e4f5539 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -1,5 +1,11 @@
 package com.android.launcher3.graphics;
 
+import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
+import static com.android.launcher3.util.Themes.isThemedIconEnabled;
+
+import android.annotation.TargetApi;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.pm.PackageManager;
@@ -8,13 +14,23 @@
 import android.database.MatrixCursor;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.Message;
+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;
@@ -55,6 +71,16 @@
 
     private static final String METHOD_GET_PREVIEW = "get_preview";
 
+    private static final String GET_ICON_THEMED = "/get_icon_themed";
+    private static final String SET_ICON_THEMED = "/set_icon_themed";
+    private static final String ICON_THEMED = "/icon_themed";
+    private static final String BOOLEAN_VALUE = "boolean_value";
+
+    private static final String KEY_SURFACE_PACKAGE = "surface_package";
+    private static final String KEY_CALLBACK = "callback";
+
+    private final ArrayMap<IBinder, PreviewLifecycleObserver> mActivePreviews = new ArrayMap<>();
+
     @Override
     public boolean onCreate() {
         return true;
@@ -63,22 +89,31 @@
     @Override
     public Cursor query(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
-        if (!KEY_LIST_OPTIONS.equals(uri.getPath())) {
-            return null;
+        switch (uri.getPath()) {
+            case KEY_LIST_OPTIONS: {
+                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()) {
+                    cursor.newRow()
+                            .add(KEY_NAME, gridOption.name)
+                            .add(KEY_ROWS, gridOption.numRows)
+                            .add(KEY_COLS, gridOption.numColumns)
+                            .add(KEY_PREVIEW_COUNT, 1)
+                            .add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns
+                                    && idp.numRows == gridOption.numRows);
+                }
+                return cursor;
+            }
+            case GET_ICON_THEMED:
+            case ICON_THEMED: {
+                MatrixCursor cursor = new MatrixCursor(new String[] {BOOLEAN_VALUE});
+                cursor.newRow().add(BOOLEAN_VALUE, isThemedIconEnabled(getContext()) ? 1 : 0);
+                return cursor;
+            }
+            default:
+                return null;
         }
-        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()) {
-            cursor.newRow()
-                    .add(KEY_NAME, gridOption.name)
-                    .add(KEY_ROWS, gridOption.numRows)
-                    .add(KEY_COLS, gridOption.numColumns)
-                    .add(KEY_PREVIEW_COUNT, 1)
-                    .add(KEY_IS_DEFAULT, idp.numColumns == gridOption.numColumns
-                            && idp.numRows == gridOption.numRows);
-        }
-        return cursor;
     }
 
     private List<GridOption> parseAllGridOptions() {
@@ -117,25 +152,37 @@
 
     @Override
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        if (!KEY_DEFAULT_GRID.equals(uri.getPath())) {
-            return 0;
-        }
+        switch (uri.getPath()) {
+            case KEY_DEFAULT_GRID: {
+                String gridName = values.getAsString(KEY_NAME);
+                // Verify that this is a valid grid option
+                GridOption match = null;
+                for (GridOption option : parseAllGridOptions()) {
+                    if (option.name.equals(gridName)) {
+                        match = option;
+                        break;
+                    }
+                }
+                if (match == null) {
+                    return 0;
+                }
 
-        String gridName = values.getAsString(KEY_NAME);
-        // Verify that this is a valid grid option
-        GridOption match = null;
-        for (GridOption option : parseAllGridOptions()) {
-            if (option.name.equals(gridName)) {
-                match = option;
-                break;
+                InvariantDeviceProfile.INSTANCE.get(getContext())
+                        .setCurrentGrid(getContext(), gridName);
+                return 1;
             }
+            case ICON_THEMED:
+            case SET_ICON_THEMED: {
+                if (FeatureFlags.ENABLE_THEMED_ICONS.get()) {
+                    getPrefs(getContext()).edit()
+                            .putBoolean(KEY_THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE))
+                            .apply();
+                }
+                return 1;
+            }
+            default:
+                return 0;
         }
-        if (match == null) {
-            return 0;
-        }
-
-        InvariantDeviceProfile.INSTANCE.get(getContext()).setCurrentGrid(getContext(), gridName);
-        return 1;
     }
 
     @Override
@@ -146,10 +193,76 @@
             return null;
         }
 
-        if (!METHOD_GET_PREVIEW.equals(method)) {
+        if (!Utilities.ATLEAST_R || !METHOD_GET_PREVIEW.equals(method)) {
             return null;
         }
+        return getPreview(extras);
+    }
 
-        return new PreviewSurfaceRenderer(getContext(), extras).render();
+    @TargetApi(Build.VERSION_CODES.R)
+    private synchronized Bundle getPreview(Bundle request) {
+        PreviewLifecycleObserver observer = null;
+        try {
+            PreviewSurfaceRenderer renderer = new PreviewSurfaceRenderer(getContext(), request);
+
+            // Destroy previous
+            destroyObserver(mActivePreviews.get(renderer.getHostToken()));
+
+            observer = new PreviewLifecycleObserver(renderer);
+            mActivePreviews.put(renderer.getHostToken(), observer);
+
+            renderer.loadAsync();
+            renderer.getHostToken().linkToDeath(observer, 0);
+
+            Bundle result = new Bundle();
+            result.putParcelable(KEY_SURFACE_PACKAGE, renderer.getSurfacePackage());
+
+            Messenger messenger =
+                    new Messenger(new Handler(UI_HELPER_EXECUTOR.getLooper(), observer));
+            Message msg = Message.obtain();
+            msg.replyTo = messenger;
+            result.putParcelable(KEY_CALLBACK, msg);
+            return result;
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to generate preview", e);
+            if (observer != null) {
+                destroyObserver(observer);
+            }
+            return null;
+        }
+    }
+
+    private synchronized void destroyObserver(PreviewLifecycleObserver observer) {
+        if (observer == null || observer.destroyed) {
+            return;
+        }
+        observer.destroyed = true;
+        observer.renderer.getHostToken().unlinkToDeath(observer, 0);
+        Executors.MAIN_EXECUTOR.execute(observer.renderer::destroy);
+        PreviewLifecycleObserver cached = mActivePreviews.get(observer.renderer.getHostToken());
+        if (cached == observer) {
+            mActivePreviews.remove(observer.renderer.getHostToken());
+        }
+    }
+
+    private class PreviewLifecycleObserver implements Handler.Callback, DeathRecipient {
+
+        public final PreviewSurfaceRenderer renderer;
+        public boolean destroyed = false;
+
+        PreviewLifecycleObserver(PreviewSurfaceRenderer renderer) {
+            this.renderer = renderer;
+        }
+
+        @Override
+        public boolean handleMessage(Message message) {
+            destroyObserver(this);
+            return true;
+        }
+
+        @Override
+        public void binderDied() {
+            destroyObserver(this);
+        }
     }
 }
diff --git a/src/com/android/launcher3/graphics/IconShape.java b/src/com/android/launcher3/graphics/IconShape.java
index 4369385..f82b07e 100644
--- a/src/com/android/launcher3/graphics/IconShape.java
+++ b/src/com/android/launcher3/graphics/IconShape.java
@@ -37,19 +37,14 @@
 import android.graphics.drawable.ColorDrawable;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.util.SparseArray;
-import android.util.TypedValue;
 import android.util.Xml;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.IconNormalizer;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ClipPathView;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -59,38 +54,22 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.Nullable;
-
 /**
  * Abstract representation of the shape of an icon shape
  */
 public abstract class IconShape {
 
     private static IconShape sInstance = new Circle();
-    private static Path sShapePath;
     private static float sNormalizationScale = ICON_VISIBLE_AREA_FACTOR;
 
-    public static final int DEFAULT_PATH_SIZE = 100;
-
     public static IconShape getShape() {
         return sInstance;
     }
 
-    public static Path getShapePath() {
-        if (sShapePath == null) {
-            Path p = new Path();
-            getShape().addToPath(p, 0, 0, DEFAULT_PATH_SIZE * 0.5f);
-            sShapePath = p;
-        }
-        return sShapePath;
-    }
-
     public static float getNormalizationScale() {
         return sNormalizationScale;
     }
 
-    private SparseArray<TypedValue> mAttrs;
-
     public boolean enableShapeDetection(){
         return false;
     };
@@ -103,11 +82,6 @@
     public abstract <T extends View & ClipPathView> Animator createRevealAnimator(T target,
             Rect startRect, Rect endRect, float endRadius, boolean isReversed);
 
-    @Nullable
-    public TypedValue getAttrValue(int attr) {
-        return mAttrs == null ? null : mAttrs.get(attr);
-    }
-
     /**
      * Abstract shape where the reveal animation is a derivative of a round rect animation
      */
@@ -182,19 +156,43 @@
         }
     }
 
-    public static final class Circle extends SimpleRectShape {
+    public static final class Circle extends PathShape {
 
-        @Override
-        public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
-            canvas.drawCircle(radius + offsetX, radius + offsetY, radius, p);
+        private final float[] mTempRadii = new float[8];
+
+        protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
+                float endRadius, Path outPath) {
+            float r1 = getStartRadius(startRect);
+
+            float[] startValues = new float[] {
+                    startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r1};
+            float[] endValues = new float[] {
+                    endRect.left, endRect.top, endRect.right, endRect.bottom, endRadius, endRadius};
+
+            FloatArrayEvaluator evaluator = new FloatArrayEvaluator(new float[6]);
+
+            return (anim) -> {
+                float progress = (Float) anim.getAnimatedValue();
+                float[] values = evaluator.evaluate(progress, startValues, endValues);
+                outPath.addRoundRect(
+                        values[0], values[1], values[2], values[3],
+                        getRadiiArray(values[4], values[5]), Path.Direction.CW);
+            };
         }
 
+        private float[] getRadiiArray(float r1, float r2) {
+            mTempRadii[0] = mTempRadii [1] = mTempRadii[2] = mTempRadii[3] =
+                    mTempRadii[6] = mTempRadii[7] = r1;
+            mTempRadii[4] = mTempRadii[5] = r2;
+            return mTempRadii;
+        }
+
+
         @Override
         public void addToPath(Path path, float offsetX, float offsetY, float radius) {
             path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW);
         }
 
-        @Override
         protected float getStartRadius(Rect startRect) {
             return startRect.width() / 2f;
         }
@@ -381,9 +379,6 @@
      * Initializes the shape which is closest to the {@link AdaptiveIconDrawable}
      */
     public static void init(Context context) {
-        if (!Utilities.ATLEAST_OREO) {
-            return;
-        }
         pickBestShape(context);
     }
 
@@ -414,7 +409,6 @@
 
             final int depth = parser.getDepth();
             int[] radiusAttr = new int[] {R.attr.folderIconRadius};
-            IntArray keysToIgnore = new IntArray(0);
 
             while (((type = parser.next()) != XmlPullParser.END_TAG ||
                     parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
@@ -425,7 +419,6 @@
                     IconShape shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1));
                     a.recycle();
 
-                    shape.mAttrs = Themes.createValueMap(context, attrs, keysToIgnore);
                     result.add(shape);
                 }
             }
@@ -471,8 +464,6 @@
         }
 
         // Initialize shape properties
-        drawable.setBounds(0, 0, DEFAULT_PATH_SIZE, DEFAULT_PATH_SIZE);
-        sShapePath = new Path(drawable.getIconMask());
         sNormalizationScale = IconNormalizer.normalizeAdaptiveIcon(drawable, size, null);
     }
 }
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 7b769b8..7a31ea0 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -15,24 +15,25 @@
  */
 package com.android.launcher3.graphics;
 
+import static android.app.WallpaperManager.FLAG_SYSTEM;
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 import static android.view.View.VISIBLE;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
 import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
 import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.annotation.TargetApi;
 import android.app.Fragment;
+import android.app.WallpaperColors;
+import android.app.WallpaperManager;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
-import android.content.pm.ShortcutInfo;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Rect;
@@ -43,11 +44,14 @@
 import android.os.Looper;
 import android.os.Process;
 import android.util.AttributeSet;
-import android.util.Log;
+import android.util.SparseIntArray;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowManager;
 import android.widget.TextClock;
 
 import com.android.launcher3.BubbleTextView;
@@ -57,26 +61,19 @@
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.WorkspaceLayoutManager;
-import com.android.launcher3.allapps.SearchUiManager;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.BgDataModel.Callbacks;
-import com.android.launcher3.model.LoaderResults;
-import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.WidgetsModel;
-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;
@@ -90,22 +87,19 @@
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.LocalColorExtractor;
 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.Callable;
 import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 /**
  * Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
@@ -116,9 +110,8 @@
  *   4) Measure and draw the view on a canvas
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class LauncherPreviewRenderer {
-
-    private static final String TAG = "LauncherPreviewRenderer";
+public class LauncherPreviewRenderer extends ContextWrapper
+        implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
 
     /**
      * Context used just for preview. It also provides a few objects (e.g. UserCache) just for
@@ -136,9 +129,15 @@
         private final ConcurrentLinkedQueue<LauncherIconsForPreview> mIconPool =
                 new ConcurrentLinkedQueue<>();
 
+        private boolean mDestroyed = false;
+
         public PreviewContext(Context base, InvariantDeviceProfile idp) {
             super(base);
             mIdp = idp;
+            mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
+            mObjectMap.put(LauncherAppState.INSTANCE,
+                    new LauncherAppState(this, null /* iconCacheFileName */));
+
         }
 
         @Override
@@ -147,11 +146,9 @@
         }
 
         public void onDestroy() {
-            CustomWidgetManager customWidgetManager = (CustomWidgetManager) mObjectMap.get(
-                    CustomWidgetManager.INSTANCE);
-            if (customWidgetManager != null) {
-                customWidgetManager.onDestroy();
-            }
+            CustomWidgetManager.INSTANCE.get(this).onDestroy();
+            LauncherAppState.INSTANCE.get(this).onTerminate();
+            mDestroyed = true;
         }
 
         /**
@@ -160,17 +157,12 @@
          */
         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 (mainThreadInitializedObject == LauncherAppState.INSTANCE) {
-                throw new IllegalStateException(
-                        "Should not use MainThreadInitializedObject to initialize this with "
-                                + "PreviewContext");
-            }
-            if (mainThreadInitializedObject == InvariantDeviceProfile.INSTANCE) {
-                return (T) mIdp;
-            }
             if (mObjectMap.containsKey(mainThreadInitializedObject)) {
                 return (T) mObjectMap.get(mainThreadInitializedObject);
             }
@@ -208,22 +200,37 @@
     private final Context mContext;
     private final InvariantDeviceProfile mIdp;
     private final DeviceProfile mDp;
-    private final boolean mMigrated;
     private final Rect mInsets;
-
     private final WorkspaceItemInfo mWorkspaceItemInfo;
+    private final LayoutInflater mHomeElementInflater;
+    private final InsettableFrameLayout mRootView;
+    private final Hotseat mHotseat;
+    private final CellLayout mWorkspace;
+    private final SparseIntArray mWallpaperColorResources;
 
-    public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) {
+    public LauncherPreviewRenderer(Context context,
+            InvariantDeviceProfile idp,
+            WallpaperColors wallpaperColorsOverride) {
+
+        super(context);
         mUiHandler = new Handler(Looper.getMainLooper());
         mContext = context;
         mIdp = idp;
-        mDp = idp.portraitProfile.copy(context);
-        mMigrated = migrated;
+        mDp = idp.getDeviceProfile(context).copy(context);
 
-        // TODO: get correct insets once display cutout API is available.
-        mInsets = new Rect();
-        mInsets.left = mInsets.right = (mDp.widthPx - mDp.availableWidthPx) / 2;
-        mInsets.top = mInsets.bottom = (mDp.heightPx - mDp.availableHeightPx) / 2;
+        if (Utilities.ATLEAST_R) {
+            WindowInsets currentWindowInsets = context.getSystemService(WindowManager.class)
+                    .getCurrentWindowMetrics().getWindowInsets();
+            mInsets = new Rect(
+                    currentWindowInsets.getSystemWindowInsetLeft(),
+                    currentWindowInsets.getSystemWindowInsetTop(),
+                    currentWindowInsets.getSystemWindowInsetRight(),
+                    currentWindowInsets.getSystemWindowInsetBottom());
+        } else {
+            mInsets = new Rect();
+            mInsets.left = mInsets.right = (mDp.widthPx - mDp.availableWidthPx) / 2;
+            mInsets.top = mInsets.bottom = (mDp.heightPx - mDp.availableHeightPx) / 2;
+        }
         mDp.updateInsets(mInsets);
 
         BaseIconFactory iconFactory =
@@ -238,291 +245,247 @@
         mWorkspaceItemInfo.intent = new Intent();
         mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
                 context.getString(R.string.label_application);
+
+        mHomeElementInflater = LayoutInflater.from(
+                new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
+        mHomeElementInflater.setFactory2(this);
+
+        mRootView = (InsettableFrameLayout) mHomeElementInflater.inflate(
+                R.layout.launcher_preview_layout, 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,
+                mDp.workspacePadding.top,
+                mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
+                mDp.workspacePadding.bottom);
+
+        if (Utilities.ATLEAST_S) {
+            WallpaperColors wallpaperColors = wallpaperColorsOverride != null
+                    ? wallpaperColorsOverride
+                    : WallpaperManager.getInstance(context).getWallpaperColors(FLAG_SYSTEM);
+            mWallpaperColorResources = wallpaperColors != null ? LocalColorExtractor.newInstance(
+                    context).generateColorsOverride(wallpaperColors) : null;
+        } else {
+            mWallpaperColorResources = null;
+        }
     }
 
     /** Populate preview and render it. */
-    public View getRenderedView() {
-        MainThreadRenderer renderer = new MainThreadRenderer(mContext);
-        renderer.populate();
-        return renderer.mRootView;
+    public View getRenderedView(BgDataModel dataModel,
+            Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+        populate(dataModel, widgetProviderInfoMap);
+        return mRootView;
     }
 
-    private class MainThreadRenderer extends ContextThemeWrapper
-            implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
+    @Override
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        if ("TextClock".equals(name)) {
+            // Workaround for TextClock accessing handler for unregistering ticker.
+            return new TextClock(context, attrs) {
 
-        private final LayoutInflater mHomeElementInflater;
-        private final InsettableFrameLayout mRootView;
-
-        private final Hotseat mHotseat;
-        private final CellLayout mWorkspace;
-
-        MainThreadRenderer(Context context) {
-            super(context, R.style.AppTheme);
-
-            mHomeElementInflater = LayoutInflater.from(
-                    new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
-            mHomeElementInflater.setFactory2(this);
-
-            mRootView = (InsettableFrameLayout) mHomeElementInflater.inflate(
-                    R.layout.launcher_preview_layout, 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,
-                    mDp.workspacePadding.top,
-                    mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
-                    mDp.workspacePadding.bottom);
+                @Override
+                public Handler getHandler() {
+                    return mUiHandler;
+                }
+            };
+        } else if (!"fragment".equals(name)) {
+            return null;
         }
 
-        @Override
-        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-            if ("TextClock".equals(name)) {
-                // Workaround for TextClock accessing handler for unregistering ticker.
-                return new TextClock(context, attrs) {
+        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PreviewFragment);
+        FragmentWithPreview f = (FragmentWithPreview) Fragment.instantiate(
+                context, ta.getString(R.styleable.PreviewFragment_android_name));
+        f.enterPreviewMode(context);
+        f.onInit(null);
 
-                    @Override
-                    public Handler getHandler() {
-                        return mUiHandler;
-                    }
-                };
-            } else if (!"fragment".equals(name)) {
-                return null;
-            }
+        View view = f.onCreateView(LayoutInflater.from(context), (ViewGroup) parent, null);
+        view.setId(ta.getInt(R.styleable.PreviewFragment_android_id, View.NO_ID));
+        return view;
+    }
 
-            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PreviewFragment);
-            FragmentWithPreview f = (FragmentWithPreview) Fragment.instantiate(
-                    context, ta.getString(R.styleable.PreviewFragment_android_name));
-            f.enterPreviewMode(context);
-            f.onInit(null);
+    @Override
+    public View onCreateView(String name, Context context, AttributeSet attrs) {
+        return onCreateView(null, name, context, attrs);
+    }
 
-            View view = f.onCreateView(LayoutInflater.from(context), (ViewGroup) parent, null);
-            view.setId(ta.getInt(R.styleable.PreviewFragment_android_id, View.NO_ID));
-            return view;
+    @Override
+    public BaseDragLayer getDragLayer() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DeviceProfile getDeviceProfile() {
+        return mDp;
+    }
+
+    @Override
+    public Hotseat getHotseat() {
+        return mHotseat;
+    }
+
+    @Override
+    public CellLayout getScreenWithId(int screenId) {
+        return mWorkspace;
+    }
+
+    private void inflateAndAddIcon(WorkspaceItemInfo info) {
+        BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
+                R.layout.app_icon, mWorkspace, false);
+        icon.applyFromWorkspaceItem(info);
+        addInScreenFromBind(icon, info);
+    }
+
+    private void inflateAndAddFolder(FolderInfo info) {
+        FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, mWorkspace,
+                info);
+        addInScreenFromBind(folderIcon, info);
+    }
+
+    private void inflateAndAddWidgets(
+            LauncherAppWidgetInfo info,
+            Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+        if (widgetProviderInfoMap == null) {
+            return;
+        }
+        AppWidgetProviderInfo providerInfo = widgetProviderInfoMap.get(
+                new ComponentKey(info.providerName, info.user));
+        if (providerInfo == null) {
+            return;
+        }
+        inflateAndAddWidgets(info, LauncherAppWidgetProviderInfo.fromProviderInfo(
+                getApplicationContext(), providerInfo));
+    }
+
+    private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
+        WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
+                info.providerName);
+        if (widgetItem == null) {
+            return;
+        }
+        inflateAndAddWidgets(info, widgetItem.widgetInfo);
+    }
+
+    private void inflateAndAddWidgets(
+            LauncherAppWidgetInfo info, LauncherAppWidgetProviderInfo providerInfo) {
+        AppWidgetHostView view = new AppWidgetHostView(mContext);
+        view.setAppWidget(-1, providerInfo);
+        view.updateAppWidget(null);
+        view.setTag(info);
+
+        if (mWallpaperColorResources != null) {
+            view.setColorResources(mWallpaperColorResources);
         }
 
-        @Override
-        public View onCreateView(String name, Context context, AttributeSet attrs) {
-            return onCreateView(null, name, context, attrs);
-        }
+        addInScreenFromBind(view, info);
+    }
 
-        @Override
-        public BaseDragLayer getDragLayer() {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public DeviceProfile getDeviceProfile() {
-            return mDp;
-        }
-
-        @Override
-        public Hotseat getHotseat() {
-            return mHotseat;
-        }
-
-        @Override
-        public CellLayout getScreenWithId(int screenId) {
-            return mWorkspace;
-        }
-
-        private void inflateAndAddIcon(WorkspaceItemInfo info) {
-            BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
-                    R.layout.app_icon, mWorkspace, false);
-            icon.applyFromWorkspaceItem(info);
-            addInScreenFromBind(icon, info);
-        }
-
-        private void inflateAndAddFolder(FolderInfo info) {
-            FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, mWorkspace,
-                    info);
-            addInScreenFromBind(folderIcon, info);
-        }
-
-        private void inflateAndAddWidgets(LauncherAppWidgetInfo info,
-                Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
-            if (widgetProviderInfoMap == null) {
-                return;
-            }
-            AppWidgetProviderInfo providerInfo = widgetProviderInfoMap.get(
-                    new ComponentKey(info.providerName, info.user));
-            if (providerInfo == null) {
-                return;
-            }
-            inflateAndAddWidgets(info, LauncherAppWidgetProviderInfo.fromProviderInfo(
-                    getApplicationContext(), providerInfo));
-        }
-
-        private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
-            WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
-                    info.providerName);
-            if (widgetItem == null) {
-                return;
-            }
-            inflateAndAddWidgets(info, widgetItem.widgetInfo);
-        }
-
-        private void inflateAndAddWidgets(LauncherAppWidgetInfo info,
-                LauncherAppWidgetProviderInfo providerInfo) {
-            AppWidgetHostView view = new AppWidgetHostView(mContext);
-            view.setAppWidget(-1, providerInfo);
-            view.updateAppWidget(null);
-            view.setTag(info);
+    private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
+        View view = PredictedAppIconInflater.inflate(mHomeElementInflater, mWorkspace, info);
+        if (view != null) {
             addInScreenFromBind(view, info);
         }
+    }
 
-        private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
-            View view = PredictedAppIconInflater.inflate(mHomeElementInflater, mWorkspace, info);
-            if (view != null) {
-                addInScreenFromBind(view, info);
-            }
+    private void dispatchVisibilityAggregated(View view, boolean isVisible) {
+        // Similar to View.dispatchVisibilityAggregated implementation.
+        final boolean thisVisible = view.getVisibility() == VISIBLE;
+        if (thisVisible || !isVisible) {
+            view.onVisibilityAggregated(isVisible);
         }
 
-        private void dispatchVisibilityAggregated(View view, boolean isVisible) {
-            // Similar to View.dispatchVisibilityAggregated implementation.
-            final boolean thisVisible = view.getVisibility() == VISIBLE;
-            if (thisVisible || !isVisible) {
-                view.onVisibilityAggregated(isVisible);
-            }
+        if (view instanceof ViewGroup) {
+            isVisible = thisVisible && isVisible;
+            ViewGroup vg = (ViewGroup) view;
+            int count = vg.getChildCount();
 
-            if (view instanceof ViewGroup) {
-                isVisible = thisVisible && isVisible;
-                ViewGroup vg = (ViewGroup) view;
-                int count = vg.getChildCount();
-
-                for (int i = 0; i < count; i++) {
-                    dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
-                }
+            for (int i = 0; i < count; i++) {
+                dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
             }
         }
+    }
 
-        private void populate() {
-            if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
-                WorkspaceFetcher fetcher;
-                PreviewContext previewContext = null;
-                if (mMigrated) {
-                    previewContext = new PreviewContext(mContext, mIdp);
-                    LauncherAppState appForPreview = new LauncherAppState(
-                            previewContext, null /* iconCacheFileName */);
-                    fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
-                    MODEL_EXECUTOR.execute(fetcher);
-                } else {
-                    fetcher = new WorkspaceItemsInfoFetcher();
-                    LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
-                            (LauncherModel.ModelUpdateTask) fetcher);
-                }
-                WorkspaceResult workspaceResult = fetcher.get();
-                if (previewContext != null) {
-                    previewContext.onDestroy();
-                }
-
-                if (workspaceResult == null) {
-                    return;
-                }
-
-                // Separate the items that are on the current screen, and all the other remaining
-                // items
-                ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
-                ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
-                ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
-                ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
-
-                filterCurrentWorkspaceItems(0 /* currentScreenId */,
-                        workspaceResult.mWorkspaceItems, currentWorkspaceItems,
-                        otherWorkspaceItems);
-                filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
-                        currentAppWidgets, otherAppWidgets);
-                sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
-
-                for (ItemInfo itemInfo : currentWorkspaceItems) {
-                    switch (itemInfo.itemType) {
-                        case Favorites.ITEM_TYPE_APPLICATION:
-                        case Favorites.ITEM_TYPE_SHORTCUT:
-                        case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                            inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
-                            break;
-                        case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                            inflateAndAddFolder((FolderInfo) itemInfo);
-                            break;
-                        default:
-                            break;
+    private void populate(BgDataModel dataModel,
+            Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+        // Separate the items that are on the current screen, and the other remaining items.
+        ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
+        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);
+        sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
+        for (ItemInfo itemInfo : currentWorkspaceItems) {
+            switch (itemInfo.itemType) {
+                case Favorites.ITEM_TYPE_APPLICATION:
+                case Favorites.ITEM_TYPE_SHORTCUT:
+                case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                    inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
+                    break;
+                case Favorites.ITEM_TYPE_FOLDER:
+                    inflateAndAddFolder((FolderInfo) itemInfo);
+                    break;
+                default:
+                    break;
+            }
+        }
+        for (ItemInfo itemInfo : currentAppWidgets) {
+            switch (itemInfo.itemType) {
+                case Favorites.ITEM_TYPE_APPWIDGET:
+                case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+                    if (widgetProviderInfoMap != null) {
+                        inflateAndAddWidgets(
+                                (LauncherAppWidgetInfo) itemInfo, widgetProviderInfoMap);
+                    } else {
+                        inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
+                                dataModel.widgetsModel);
                     }
-                }
-                for (ItemInfo itemInfo : currentAppWidgets) {
-                    switch (itemInfo.itemType) {
-                        case Favorites.ITEM_TYPE_APPWIDGET:
-                        case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
-                            if (mMigrated) {
-                                inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
-                                        workspaceResult.mWidgetProvidersMap);
-                            } else {
-                                inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
-                                        workspaceResult.mWidgetsModel);
-                            }
-                            break;
-                        default:
-                            break;
-                    }
-                }
-
-                IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
-                        mIdp.numHotseatIcons);
-                int count = Math.min(ranks.size(), workspaceResult.mCachedPredictedItems.size());
-                for (int i = 0; i < count; i++) {
-                    AppInfo appInfo = workspaceResult.mCachedPredictedItems.get(i);
-                    int rank = ranks.get(i);
-                    WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(appInfo);
-                    itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
-                    itemInfo.rank = rank;
-                    itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
-                    itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
-                    itemInfo.screenId = rank;
-                    inflateAndAddPredictedIcon(itemInfo);
-                }
-            } else {
-                // Add hotseat icons
-                for (int i = 0; i < mIdp.numHotseatIcons; i++) {
-                    WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
-                    info.container = Favorites.CONTAINER_HOTSEAT;
-                    info.screenId = i;
-                    inflateAndAddIcon(info);
-                }
-                // Add workspace icons
-                for (int i = 0; i < mIdp.numColumns; i++) {
-                    WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
-                    info.container = Favorites.CONTAINER_DESKTOP;
-                    info.screenId = 0;
-                    info.cellX = i;
-                    info.cellY = mIdp.numRows - 1;
-                    inflateAndAddIcon(info);
-                }
+                    break;
+                default:
+                    break;
             }
-
-            // Add first page QSB
-            if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
-                View qsb = mHomeElementInflater.inflate(
-                        R.layout.search_container_workspace, mWorkspace, false);
-                CellLayout.LayoutParams lp =
-                        new CellLayout.LayoutParams(0, 0, mWorkspace.getCountX(), 1);
-                lp.canReorder = false;
-                mWorkspace.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
-            }
-
-            // Setup search view
-            SearchUiManager searchUiManager =
-                    mRootView.findViewById(R.id.search_container_all_apps);
-            mRootView.findViewById(R.id.apps_view).setTranslationY(
-                    mDp.heightPx - searchUiManager.getScrollRangeDelta(mInsets));
-
-            measureView(mRootView, mDp.widthPx, mDp.heightPx);
-            dispatchVisibilityAggregated(mRootView, true);
-            measureView(mRootView, mDp.widthPx, mDp.heightPx);
-            // Additional measure for views which use auto text size API
-            measureView(mRootView, mDp.widthPx, mDp.heightPx);
         }
+        IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
+                mDp.numShownHotseatIcons);
+        FixedContainerItems hotseatpredictions =
+                dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
+        List<ItemInfo> predictions = hotseatpredictions == null
+                ? Collections.emptyList() : hotseatpredictions.items;
+        int count = Math.min(ranks.size(), predictions.size());
+        for (int i = 0; i < count; i++) {
+            int rank = ranks.get(i);
+            WorkspaceItemInfo itemInfo =
+                    new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i));
+            itemInfo.container = CONTAINER_HOTSEAT_PREDICTION;
+            itemInfo.rank = rank;
+            itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
+            itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
+            itemInfo.screenId = rank;
+            inflateAndAddPredictedIcon(itemInfo);
+        }
+
+        // Add first page QSB
+        if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            View qsb = mHomeElementInflater.inflate(
+                    R.layout.search_container_workspace, mWorkspace, false);
+            CellLayout.LayoutParams lp =
+                    new CellLayout.LayoutParams(0, 0, mWorkspace.getCountX(), 1);
+            lp.canReorder = false;
+            mWorkspace.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
+        }
+
+        measureView(mRootView, mDp.widthPx, mDp.heightPx);
+        dispatchVisibilityAggregated(mRootView, true);
+        measureView(mRootView, mDp.widthPx, mDp.heightPx);
+        // Additional measure for views which use auto text size API
+        measureView(mRootView, mDp.widthPx, mDp.heightPx);
     }
 
     private static void measureView(View view, int width, int height) {
@@ -530,109 +493,15 @@
         view.layout(0, 0, width, height);
     }
 
-    private static class WorkspaceItemsInfoFetcher implements LauncherModel.ModelUpdateTask,
-            WorkspaceFetcher {
-
-        private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
-
-        private LauncherAppState mApp;
-        private LauncherModel mModel;
-        private BgDataModel mBgDataModel;
-        private AllAppsList mAllAppsList;
-
-        @Override
-        public void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
-                AllAppsList allAppsList, Executor uiExecutor) {
-            mApp = app;
-            mModel = model;
-            mBgDataModel = dataModel;
-            mAllAppsList = allAppsList;
+    /** 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 FutureTask<WorkspaceResult> getTask() {
-            return mTask;
-        }
-
-        @Override
-        public void run() {
-            mTask.run();
-        }
-
-        @Override
-        public WorkspaceResult call() throws Exception {
-            if (!mModel.isModelLoaded()) {
-                Log.d(TAG, "Workspace not loaded, loading now");
-                mModel.startLoaderForResults(
-                        new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
-                return null;
-            }
-
-            return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
-                    mBgDataModel.cachedPredictedItems, mBgDataModel.widgetsModel, null);
-        }
-    }
-
-    private static class WorkspaceItemsInfoFromPreviewFetcher extends LoaderTask implements
-            WorkspaceFetcher {
-
-        private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
-
-        WorkspaceItemsInfoFromPreviewFetcher(LauncherAppState app) {
-            super(app, null, new BgDataModel(), null);
-        }
-
-        @Override
-        public FutureTask<WorkspaceResult> getTask() {
-            return mTask;
-        }
-
-        @Override
-        public void run() {
-            mTask.run();
-        }
-
-        @Override
-        public WorkspaceResult call() throws Exception {
-            List<ShortcutInfo> allShortcuts = new ArrayList<>();
-            loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
-                    LauncherSettings.Favorites.SCREEN + " = 0 or "
-                    + LauncherSettings.Favorites.CONTAINER + " = "
-                    + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
-            return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
-                    mBgDataModel.cachedPredictedItems, null, mWidgetProvidersMap);
-        }
-    }
-
-    private interface WorkspaceFetcher extends Runnable, Callable<WorkspaceResult> {
-        FutureTask<WorkspaceResult> getTask();
-
-        default WorkspaceResult get() {
-            try {
-                return getTask().get(5, TimeUnit.SECONDS);
-            } catch (InterruptedException | ExecutionException | TimeoutException e) {
-                Log.d(TAG, "Error fetching workspace items info", e);
-                return null;
-            }
-        }
-    }
-
-    private static class WorkspaceResult {
-        private final ArrayList<ItemInfo> mWorkspaceItems;
-        private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
-        private final ArrayList<AppInfo> mCachedPredictedItems;
-        private final WidgetsModel mWidgetsModel;
-        private final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap;
-
-        private WorkspaceResult(ArrayList<ItemInfo> workspaceItems,
-                ArrayList<LauncherAppWidgetInfo> appWidgets,
-                ArrayList<AppInfo> cachedPredictedItems, WidgetsModel widgetsModel,
-                Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
-            mWorkspaceItems = workspaceItems;
-            mAppWidgets = appWidgets;
-            mCachedPredictedItems = cachedPredictedItems;
-            mWidgetsModel = widgetsModel;
-            mWidgetProvidersMap = widgetProviderInfoMap;
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            return true;
         }
     }
 }
diff --git a/src/com/android/launcher3/graphics/NinePatchDrawHelper.java b/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
deleted file mode 100644
index 5872689..0000000
--- a/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.graphics;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-
-/**
- * Utility class which draws a bitmap by dissecting it into 3 segments and stretching
- * the middle segment.
- */
-public class NinePatchDrawHelper {
-
-    // The extra width used for the bitmap. This portion of the bitmap is stretched to match the
-    // width of the draw region. Randomly chosen, any value > 4 will be sufficient.
-    public static final int EXTENSION_PX = 20;
-
-    private final Rect mSrc = new Rect();
-    private final RectF mDst = new RectF();
-    // Enable filtering to always get a nice edge. This avoids jagged line, when bitmap is
-    // translated by half pixel.
-    public final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-
-    /**
-     * Draws the bitmap split into three parts horizontally, with the middle part having width
-     * as {@link #EXTENSION_PX} in the center of the bitmap.
-     */
-    public void draw(Bitmap bitmap, Canvas canvas, float left, float top, float right) {
-        int height = bitmap.getHeight();
-
-        mSrc.top = 0;
-        mSrc.bottom = height;
-        mDst.top = top;
-        mDst.bottom = top + height;
-        draw3Patch(bitmap, canvas, left, right);
-    }
-
-
-    /**
-     * Draws the bitmap split horizontally into 3 parts (same as {@link #draw}) and split
-     * vertically into two parts, bottom part of size {@link #EXTENSION_PX} / 2 which is
-     * stretched vertically.
-     */
-    public void drawVerticallyStretched(Bitmap bitmap, Canvas canvas, float left, float top,
-            float right, float bottom) {
-        draw(bitmap, canvas, left, top, right);
-
-        // Draw bottom stretched region.
-        int height = bitmap.getHeight();
-        mSrc.top = height - EXTENSION_PX / 4;
-        mSrc.bottom = height;
-        mDst.top = top + height;
-        mDst.bottom = bottom;
-        draw3Patch(bitmap, canvas, left, right);
-    }
-
-
-
-    private void draw3Patch(Bitmap bitmap, Canvas canvas, float left, float right) {
-        int width = bitmap.getWidth();
-        int halfWidth = width / 2;
-
-        // Draw left edge
-        drawRegion(bitmap, canvas, 0, halfWidth, left, left + halfWidth);
-
-        // Draw right edge
-        drawRegion(bitmap, canvas, halfWidth, width, right - halfWidth, right);
-
-        // Draw middle stretched region
-        int halfExt = EXTENSION_PX / 4;
-        drawRegion(bitmap, canvas, halfWidth - halfExt, halfWidth + halfExt,
-                left + halfWidth, right - halfWidth);
-    }
-
-    private void drawRegion(Bitmap bitmap, Canvas c,
-            int srcLeft, int srcRight, float dstLeft, float dstRight) {
-        mSrc.left = srcLeft;
-        mSrc.right = srcRight;
-
-        mDst.left = dstLeft;
-        mDst.right = dstRight;
-        c.drawBitmap(bitmap, mSrc, mDst, paint);
-    }
-}
diff --git a/src/com/android/launcher3/graphics/OverviewScrim.java b/src/com/android/launcher3/graphics/OverviewScrim.java
deleted file mode 100644
index c0c3e5e..0000000
--- a/src/com/android/launcher3/graphics/OverviewScrim.java
+++ /dev/null
@@ -1,99 +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.graphics;
-
-import static android.view.View.VISIBLE;
-
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.graphics.Rect;
-import android.util.FloatProperty;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * View scrim which draws behind overview (recent apps).
- */
-public class OverviewScrim extends Scrim {
-
-    public static final FloatProperty<OverviewScrim> SCRIM_MULTIPLIER =
-            new FloatProperty<OverviewScrim>("scrimMultiplier") {
-                @Override
-                public Float get(OverviewScrim scrim) {
-                    return scrim.mScrimMultiplier;
-                }
-
-                @Override
-                public void setValue(OverviewScrim scrim, float v) {
-                    scrim.setScrimMultiplier(v);
-                }
-            };
-
-    private @NonNull View mStableScrimmedView;
-    // Might be higher up if mStableScrimmedView is invisible.
-    private @Nullable View mCurrentScrimmedView;
-
-    private float mScrimMultiplier = 1f;
-
-    public OverviewScrim(View view) {
-        super(view);
-        mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel();
-
-        onExtractedColorsChanged(mWallpaperColorInfo);
-    }
-
-    public void onInsetsChanged(Rect insets) {
-        mStableScrimmedView = (OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0
-                ? mLauncher.getHotseat()
-                : mLauncher.getOverviewPanel();
-    }
-
-    public void updateCurrentScrimmedView(ViewGroup root) {
-        // Find the lowest view that is at or above the view we want to show the scrim behind.
-        mCurrentScrimmedView = mStableScrimmedView;
-        int currentIndex = root.indexOfChild(mCurrentScrimmedView);
-        final int childCount = root.getChildCount();
-        while (mCurrentScrimmedView != null && mCurrentScrimmedView.getVisibility() != VISIBLE
-                && currentIndex < childCount) {
-            currentIndex++;
-            mCurrentScrimmedView = root.getChildAt(currentIndex);
-        }
-    }
-
-    /**
-     * @return The view to draw the scrim behind, or null if all visible views should be scrimmed.
-     */
-    public @Nullable View getScrimmedView() {
-        return mCurrentScrimmedView;
-    }
-
-    private void setScrimMultiplier(float scrimMultiplier) {
-        if (Float.compare(mScrimMultiplier, scrimMultiplier) != 0) {
-            mScrimMultiplier = scrimMultiplier;
-            invalidate();
-        }
-    }
-
-    @Override
-    protected int getScrimAlpha() {
-        return Math.round(super.getScrimAlpha() * mScrimMultiplier);
-    }
-}
diff --git a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
deleted file mode 100644
index d347e8f..0000000
--- a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
+++ /dev/null
@@ -1,56 +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.graphics;
-
-import static androidx.core.graphics.ColorUtils.compositeColors;
-
-import static com.android.launcher3.graphics.IconShape.getShapePath;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Path;
-import android.graphics.Rect;
-
-import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.util.Themes;
-
-/**
- * Subclass which draws a placeholder icon when the actual icon is not yet loaded
- */
-public class PlaceHolderIconDrawable extends FastBitmapDrawable {
-
-    // Path in [0, 100] bounds.
-    private final Path mProgressPath;
-
-    public PlaceHolderIconDrawable(BitmapInfo info, Context context) {
-        super(info);
-
-        mProgressPath = getShapePath();
-        mPaint.setColor(compositeColors(
-                Themes.getAttrColor(context, R.attr.loadingIconColor), info.color));
-    }
-
-    @Override
-    protected void drawInternal(Canvas canvas, Rect bounds) {
-        int saveCount = canvas.save();
-        canvas.translate(bounds.left, bounds.top);
-        canvas.scale(bounds.width() / 100f, bounds.height() / 100f);
-        canvas.drawPath(mProgressPath, mPaint);
-        canvas.restoreToCount(saveCount);
-    }
-}
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index e85b056..e45b8f7 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -17,9 +17,6 @@
 
 package com.android.launcher3.graphics;
 
-import static com.android.launcher3.graphics.IconShape.DEFAULT_PATH_SIZE;
-import static com.android.launcher3.graphics.IconShape.getShapePath;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -34,10 +31,14 @@
 import android.util.Pair;
 import android.util.Property;
 import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
 
-import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.util.Themes;
 
 import java.lang.ref.WeakReference;
 
@@ -59,6 +60,7 @@
                 }
             };
 
+    private static final int DEFAULT_PATH_SIZE = 100;
     private static final float PROGRESS_WIDTH = 7;
     private static final float PROGRESS_GAP = 2;
     private static final int MAX_PAINT_ALPHA = 255;
@@ -77,6 +79,9 @@
     private static final SparseArray<WeakReference<Pair<Path, Bitmap>>> sShadowCache =
             new SparseArray<>();
 
+    private static final int PRELOAD_ACCENT_COLOR_INDEX = 0;
+    private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
+
     private final Matrix mTmpMatrix = new Matrix();
     private final PathMeasure mPathMeasure = new PathMeasure();
 
@@ -91,6 +96,9 @@
 
     private Bitmap mShadowBitmap;
     private final int mIndicatorColor;
+    private final int mSystemAccentColor;
+    private final int mSystemBackgroundColor;
+    private final boolean mIsDarkMode;
 
     private int mTrackAlpha;
     private float mTrackLength;
@@ -104,19 +112,38 @@
 
     private ObjectAnimator mCurrentAnim;
 
+    private boolean mIsStartable;
+
     public PreloadIconDrawable(ItemInfoWithIcon info, Context context) {
+        this(
+                info,
+                IconPalette.getPreloadProgressColor(context, info.bitmap.color),
+                getPreloadColors(context),
+                Utilities.isDarkTheme(context));
+    }
+
+    public PreloadIconDrawable(
+            ItemInfoWithIcon info,
+            int indicatorColor,
+            int[] preloadColors,
+            boolean isDarkMode) {
         super(info.bitmap);
         mItem = info;
-        mShapePath = getShapePath();
+        mShapePath = GraphicsUtils.getShapePath(DEFAULT_PATH_SIZE);
         mScaledTrackPath = new Path();
         mScaledProgressPath = new Path();
 
         mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
         mProgressPaint.setStyle(Paint.Style.STROKE);
         mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
-        mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
+        mIndicatorColor = indicatorColor;
 
-        setInternalProgress(0);
+        mSystemAccentColor = preloadColors[PRELOAD_ACCENT_COLOR_INDEX];
+        mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX];
+        mIsDarkMode = isDarkMode;
+
+        setInternalProgress(info.getProgressLevel());
+        setIsStartable(info.isAppStartable());
     }
 
     @Override
@@ -142,7 +169,7 @@
     }
 
     private Bitmap getShadowBitmap(int width, int height, float shadowRadius) {
-        int key = (width << 16) | height;
+        int key = ((width << 16) | height) * (mIsDarkMode ? -1 : 1);
         WeakReference<Pair<Path, Bitmap>> shadowRef = sShadowCache.get(key);
         Pair<Path, Bitmap> cache = shadowRef != null ? shadowRef.get() : null;
         Bitmap shadow = cache != null && cache.first.equals(mShapePath) ? cache.second : null;
@@ -151,8 +178,9 @@
         }
         shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         Canvas c = new Canvas(shadow);
-        mProgressPaint.setShadowLayer(shadowRadius, 0, 0, COLOR_SHADOW);
-        mProgressPaint.setColor(COLOR_TRACK);
+        mProgressPaint.setShadowLayer(shadowRadius, 0, 0, mIsStartable
+                ? COLOR_SHADOW : mSystemAccentColor);
+        mProgressPaint.setColor(mIsStartable ? COLOR_TRACK : mSystemBackgroundColor);
         mProgressPaint.setAlpha(MAX_PAINT_ALPHA);
         c.drawPath(mScaledTrackPath, mProgressPaint);
         mProgressPaint.clearShadowLayer();
@@ -170,7 +198,7 @@
         }
 
         // Draw track.
-        mProgressPaint.setColor(mIndicatorColor);
+        mProgressPaint.setColor(mIsStartable ? mIndicatorColor : mSystemAccentColor);
         mProgressPaint.setAlpha(mTrackAlpha);
         if (mShadowBitmap != null) {
             canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint);
@@ -209,6 +237,14 @@
         return !mRanFinishAnimation;
     }
 
+    /** Sets whether this icon should display the startable app UI. */
+    public void setIsStartable(boolean isStartable) {
+        if (mIsStartable != isStartable) {
+            mIsStartable = isStartable;
+            setIsDisabled(!isStartable);
+        }
+    }
+
     private void updateInternalState(float finalProgress, boolean shouldAnimate, boolean isFinish) {
         if (mCurrentAnim != null) {
             mCurrentAnim.cancel();
@@ -266,14 +302,12 @@
             mIconScale = SMALL_SCALE;
             mScaledTrackPath.reset();
             mTrackAlpha = MAX_PAINT_ALPHA;
-            setIsDisabled(true);
         }
 
         if (progress < 1 && progress > 0) {
             mPathMeasure.getSegment(0, progress * mTrackLength, mScaledProgressPath, true);
             mIconScale = SMALL_SCALE;
             mTrackAlpha = MAX_PAINT_ALPHA;
-            setIsDisabled(true);
         } else if (progress >= 1) {
             setIsDisabled(mItem.isDisabled());
             mScaledTrackPath.set(mScaledProgressPath);
@@ -291,10 +325,73 @@
         invalidateSelf();
     }
 
+    private static int[] getPreloadColors(Context context) {
+        Context dayNightThemeContext = new ContextThemeWrapper(
+                context, android.R.style.Theme_DeviceDefault_DayNight);
+        int[] preloadColors = new int[2];
+
+        preloadColors[PRELOAD_ACCENT_COLOR_INDEX] = Themes.getColorAccent(dayNightThemeContext);
+        preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX] = Themes.getColorBackgroundFloating(
+                dayNightThemeContext);
+
+        return preloadColors;
+    }
+
     /**
      * Returns a FastBitmapDrawable with the icon.
      */
     public static PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
         return new PreloadIconDrawable(info, context);
     }
+
+    @Override
+    public ConstantState getConstantState() {
+        return new PreloadIconConstantState(
+                mBitmap,
+                mIconColor,
+                !mItem.isAppStartable(),
+                mItem,
+                mIndicatorColor,
+                new int[] {mSystemAccentColor, mSystemBackgroundColor},
+                mIsDarkMode);
+    }
+
+    protected static class PreloadIconConstantState extends FastBitmapConstantState {
+
+        protected final ItemInfoWithIcon mInfo;
+        protected final int mIndicatorColor;
+        protected final int[] mPreloadColors;
+        protected final boolean mIsDarkMode;
+        protected final int mLevel;
+
+        public PreloadIconConstantState(
+                Bitmap bitmap,
+                int iconColor,
+                boolean isDisabled,
+                ItemInfoWithIcon info,
+                int indicatorColor,
+                int[] preloadColors,
+                boolean isDarkMode) {
+            super(bitmap, iconColor, isDisabled);
+            mInfo = info;
+            mIndicatorColor = indicatorColor;
+            mPreloadColors = preloadColors;
+            mIsDarkMode = isDarkMode;
+            mLevel = info.getProgressLevel();
+        }
+
+        @Override
+        public PreloadIconDrawable newDrawable() {
+            return new PreloadIconDrawable(
+                    mInfo,
+                    mIndicatorColor,
+                    mPreloadColors,
+                    mIsDarkMode);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return 0;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index fdc3a94..df49359 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -20,27 +20,50 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.app.WallpaperColors;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.SurfaceControlViewHost;
+import android.view.SurfaceControlViewHost.SurfacePackage;
 import android.view.View;
+import android.view.WindowManager.LayoutParams;
 import android.view.animation.AccelerateDecelerateInterpolator;
 
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
 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.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;
+import com.android.launcher3.model.ModelPreload;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.widget.LocalColorExtractor;
 
+import java.util.ArrayList;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 /** Render preview using surface view. */
-public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
+@SuppressWarnings("NewApi")
+public class PreviewSurfaceRenderer {
+
+    private static final String TAG = "PreviewSurfaceRenderer";
 
     private static final int FADE_IN_ANIMATION_DURATION = 200;
 
@@ -48,8 +71,7 @@
     private static final String KEY_VIEW_WIDTH = "width";
     private static final String KEY_VIEW_HEIGHT = "height";
     private static final String KEY_DISPLAY_ID = "display_id";
-    private static final String KEY_SURFACE_PACKAGE = "surface_package";
-    private static final String KEY_CALLBACK = "callback";
+    private static final String KEY_COLORS = "wallpaper_colors";
 
     private final Context mContext;
     private final InvariantDeviceProfile mIdp;
@@ -57,10 +79,14 @@
     private final int mWidth;
     private final int mHeight;
     private final Display mDisplay;
+    private final WallpaperColors mWallpaperColors;
+    private final RunnableList mOnDestroyCallbacks = new RunnableList();
 
-    private SurfaceControlViewHost mSurfaceControlViewHost;
+    private final SurfaceControlViewHost mSurfaceControlViewHost;
 
-    PreviewSurfaceRenderer(Context context, Bundle bundle) {
+    private boolean mDestroyed = false;
+
+    public PreviewSurfaceRenderer(Context context, Bundle bundle) throws Exception {
         mContext = context;
 
         String gridName = bundle.getString("name");
@@ -68,91 +94,102 @@
         if (gridName == null) {
             gridName = InvariantDeviceProfile.getCurrentGridName(context);
         }
+        mWallpaperColors = bundle.getParcelable(KEY_COLORS);
         mIdp = new InvariantDeviceProfile(context, gridName);
 
         mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
         mWidth = bundle.getInt(KEY_VIEW_WIDTH);
         mHeight = bundle.getInt(KEY_VIEW_HEIGHT);
+        mDisplay = context.getSystemService(DisplayManager.class)
+                .getDisplay(bundle.getInt(KEY_DISPLAY_ID));
 
-        final DisplayManager displayManager = (DisplayManager) context.getSystemService(
-                Context.DISPLAY_SERVICE);
-        mDisplay = displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID));
+        mSurfaceControlViewHost = MAIN_EXECUTOR
+                .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))
+                .get(5, TimeUnit.SECONDS);
+        mOnDestroyCallbacks.add(mSurfaceControlViewHost::release);
     }
 
-    /** Handle a received surface view request. */
-    Bundle render() {
-        if (mSurfaceControlViewHost != null) {
-            binderDied();
+    public IBinder getHostToken() {
+        return mHostToken;
+    }
+
+    public SurfacePackage getSurfacePackage() {
+        return mSurfaceControlViewHost.getSurfacePackage();
+    }
+
+    /**
+     * Destroys the preview and all associated data
+     */
+    @UiThread
+    public void destroy() {
+        mDestroyed = true;
+        mOnDestroyCallbacks.executeAllAndDestroy();
+    }
+
+    /**
+     * Generates the preview in background
+     */
+    public void loadAsync() {
+        MODEL_EXECUTOR.execute(this::loadModelData);
+    }
+
+    @WorkerThread
+    private void loadModelData() {
+        final boolean migrated = doGridMigrationIfNecessary();
+
+        final Context inflationContext;
+        if (mWallpaperColors != null) {
+            // Create a themed context, without affecting the main application context
+            Context context = mContext.createDisplayContext(mDisplay);
+            if (Utilities.ATLEAST_R) {
+                context = context.createWindowContext(
+                        LayoutParams.TYPE_APPLICATION_OVERLAY, null);
+            }
+            LocalColorExtractor.newInstance(mContext)
+                    .applyColorsOverride(context, mWallpaperColors);
+            inflationContext = new ContextThemeWrapper(context,
+                    Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
+        } else {
+            inflationContext = new ContextThemeWrapper(mContext,  R.style.AppTheme);
         }
 
-        SurfaceControlViewHost.SurfacePackage surfacePackage;
-        try {
-            mSurfaceControlViewHost = MAIN_EXECUTOR
-                    .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))
-                    .get(5, TimeUnit.SECONDS);
-            surfacePackage = mSurfaceControlViewHost.getSurfacePackage();
-            mHostToken.linkToDeath(this, 0);
-        } catch (Exception e) {
-            e.printStackTrace();
-            return null;
-        }
+        if (migrated) {
+            PreviewContext previewContext = new PreviewContext(inflationContext, mIdp);
+            new LoaderTask(
+                    LauncherAppState.getInstance(previewContext),
+                    null,
+                    new BgDataModel(),
+                    new ModelDelegate(), null) {
 
-        MODEL_EXECUTOR.post(() -> {
-            final boolean success = doGridMigrationIfNecessary();
-
-            MAIN_EXECUTOR.post(() -> {
-                // If mSurfaceControlViewHost is null due to any reason (e.g. binder died,
-                // happening when user leaves the preview screen before preview rendering finishes),
-                // we should return here.
-                SurfaceControlViewHost host = mSurfaceControlViewHost;
-                if (host == null) {
-                    return;
+                @Override
+                public void run() {
+                    loadWorkspace(new ArrayList<>(), LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
+                            LauncherSettings.Favorites.SCREEN + " = 0 or "
+                                    + LauncherSettings.Favorites.CONTAINER + " = "
+                                    + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+                    MAIN_EXECUTOR.execute(() -> {
+                        renderView(previewContext, mBgDataModel, mWidgetProvidersMap);
+                        mOnDestroyCallbacks.add(previewContext::onDestroy);
+                    });
                 }
+            }.run();
+        } else {
+            new ModelPreload() {
 
-                View view = new LauncherPreviewRenderer(mContext, mIdp, success).getRenderedView();
-                // This aspect scales the view to fit in the surface and centers it
-                final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
-                        mHeight / (float) view.getMeasuredHeight());
-                view.setScaleX(scale);
-                view.setScaleY(scale);
-                view.setPivotX(0);
-                view.setPivotY(0);
-                view.setTranslationX((mWidth - scale * view.getWidth()) / 2);
-                view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
-                view.setAlpha(0);
-                view.animate().alpha(1)
-                        .setInterpolator(new AccelerateDecelerateInterpolator())
-                        .setDuration(FADE_IN_ANIMATION_DURATION)
-                        .start();
-                host.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight());
-            });
-        });
-
-        Bundle result = new Bundle();
-        result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);
-
-        Handler handler = new Handler(Looper.getMainLooper(), message -> {
-            binderDied();
-            return true;
-        });
-        Messenger messenger = new Messenger(handler);
-        Message msg = Message.obtain();
-        msg.replyTo = messenger;
-        result.putParcelable(KEY_CALLBACK, msg);
-        return result;
-    }
-
-    @Override
-    public void binderDied() {
-        if (mSurfaceControlViewHost != null) {
-            MAIN_EXECUTOR.execute(() -> {
-                mSurfaceControlViewHost.release();
-                mSurfaceControlViewHost = null;
-            });
+                @Override
+                public void onComplete(boolean isSuccess) {
+                    if (isSuccess) {
+                        MAIN_EXECUTOR.execute(() ->
+                                renderView(inflationContext, getBgDataModel(), null));
+                    } else {
+                        Log.e(TAG, "Model loading failed");
+                    }
+                }
+            }.start(inflationContext);
         }
-        mHostToken.unlinkToDeath(this, 0);
     }
 
+    @WorkerThread
     private boolean doGridMigrationIfNecessary() {
         boolean needsToMigrate =
                 MULTI_DB_GRID_MIRATION_ALGO.get()
@@ -165,4 +202,29 @@
                 ? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp)
                 : GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp);
     }
+
+    @UiThread
+    private void renderView(Context inflationContext, BgDataModel dataModel,
+            Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+        if (mDestroyed) {
+            return;
+        }
+        View view = new LauncherPreviewRenderer(inflationContext, mIdp, mWallpaperColors)
+                .getRenderedView(dataModel, widgetProviderInfoMap);
+        // This aspect scales the view to fit in the surface and centers it
+        final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
+                mHeight / (float) view.getMeasuredHeight());
+        view.setScaleX(scale);
+        view.setScaleY(scale);
+        view.setPivotX(0);
+        view.setPivotY(0);
+        view.setTranslationX((mWidth - scale * view.getWidth()) / 2);
+        view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
+        view.setAlpha(0);
+        view.animate().alpha(1)
+                .setInterpolator(new AccelerateDecelerateInterpolator())
+                .setDuration(FADE_IN_ANIMATION_DURATION)
+                .start();
+        mSurfaceControlViewHost.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight());
+    }
 }
diff --git a/src/com/android/launcher3/graphics/Scrim.java b/src/com/android/launcher3/graphics/Scrim.java
index a151cba..a77e058 100644
--- a/src/com/android/launcher3/graphics/Scrim.java
+++ b/src/com/android/launcher3/graphics/Scrim.java
@@ -22,14 +22,12 @@
 import android.util.FloatProperty;
 import android.view.View;
 
-import com.android.launcher3.Launcher;
-import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.R;
 
 /**
  * Contains general scrim properties such as wallpaper-extracted color that subclasses can use.
  */
-public class Scrim implements View.OnAttachStateChangeListener,
-        WallpaperColorInfo.OnChangeListener {
+public class Scrim {
 
     public static final FloatProperty<Scrim> SCRIM_PROGRESS =
             new FloatProperty<Scrim>("scrimProgress") {
@@ -44,8 +42,6 @@
                 }
             };
 
-    protected final Launcher mLauncher;
-    protected final WallpaperColorInfo mWallpaperColorInfo;
     protected final View mRoot;
 
     protected float mScrimProgress;
@@ -54,48 +50,18 @@
 
     public Scrim(View view) {
         mRoot = view;
-        mLauncher = Launcher.getLauncher(view.getContext());
-        mWallpaperColorInfo = WallpaperColorInfo.INSTANCE.get(mLauncher);
-
-        view.addOnAttachStateChangeListener(this);
+        mScrimColor = mRoot.getContext().getColor(R.color.wallpaper_popup_scrim);
     }
 
     public void draw(Canvas canvas) {
-        canvas.drawColor(setColorAlphaBound(mScrimColor, getScrimAlpha()));
-    }
-
-    protected int getScrimAlpha() {
-        return mScrimAlpha;
+        canvas.drawColor(setColorAlphaBound(mScrimColor, mScrimAlpha));
     }
 
     private void setScrimProgress(float progress) {
         if (mScrimProgress != progress) {
             mScrimProgress = progress;
             mScrimAlpha = Math.round(255 * mScrimProgress);
-            invalidate();
+            mRoot.invalidate();
         }
     }
-
-    @Override
-    public void onViewAttachedToWindow(View view) {
-        mWallpaperColorInfo.addOnChangeListener(this);
-        onExtractedColorsChanged(mWallpaperColorInfo);
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(View view) {
-        mWallpaperColorInfo.removeOnChangeListener(this);
-    }
-
-    @Override
-    public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
-        mScrimColor = wallpaperColorInfo.getMainColor();
-        if (mScrimAlpha > 0) {
-            invalidate();
-        }
-    }
-
-    public void invalidate() {
-        mRoot.invalidate();
-    }
 }
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
similarity index 69%
rename from src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
rename to src/com/android/launcher3/graphics/SysUiScrim.java
index 7b7ab5e..f0766c5 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -34,7 +34,6 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Region;
 import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
@@ -42,43 +41,42 @@
 import android.view.View;
 import android.view.WindowInsets;
 
-import androidx.core.graphics.ColorUtils;
-
-import com.android.launcher3.CellLayout;
+import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.DynamicResource;
 import com.android.launcher3.util.Themes;
+import com.android.systemui.plugins.ResourceProvider;
 
 /**
  * View scrim which draws behind hotseat and workspace
  */
-public class WorkspaceAndHotseatScrim extends Scrim {
+public class SysUiScrim implements View.OnAttachStateChangeListener {
 
-    public static final FloatProperty<WorkspaceAndHotseatScrim> SYSUI_PROGRESS =
-            new FloatProperty<WorkspaceAndHotseatScrim>("sysUiProgress") {
+    public static final FloatProperty<SysUiScrim> SYSUI_PROGRESS =
+            new FloatProperty<SysUiScrim>("sysUiProgress") {
                 @Override
-                public Float get(WorkspaceAndHotseatScrim scrim) {
+                public Float get(SysUiScrim scrim) {
                     return scrim.mSysUiProgress;
                 }
 
                 @Override
-                public void setValue(WorkspaceAndHotseatScrim scrim, float value) {
+                public void setValue(SysUiScrim scrim, float value) {
                     scrim.setSysUiProgress(value);
                 }
             };
 
-    private static final FloatProperty<WorkspaceAndHotseatScrim> SYSUI_ANIM_MULTIPLIER =
-            new FloatProperty<WorkspaceAndHotseatScrim>("sysUiAnimMultiplier") {
+    private static final FloatProperty<SysUiScrim> SYSUI_ANIM_MULTIPLIER =
+            new FloatProperty<SysUiScrim>("sysUiAnimMultiplier") {
                 @Override
-                public Float get(WorkspaceAndHotseatScrim scrim) {
+                public Float get(SysUiScrim scrim) {
                     return scrim.mSysUiAnimMultiplier;
                 }
 
                 @Override
-                public void setValue(WorkspaceAndHotseatScrim scrim, float value) {
+                public void setValue(SysUiScrim scrim, float value) {
                     scrim.mSysUiAnimMultiplier = value;
                     scrim.reapplySysUiAlpha();
                 }
@@ -86,7 +84,6 @@
 
     /**
      * Receiver used to get a signal that the user unlocked their device.
-     * @see KEYGUARD_ANIMATION For proper signal.
      */
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -102,23 +99,22 @@
         }
     };
 
-    private static final int DARK_SCRIM_COLOR = 0x55000000;
     private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
     private static final int ALPHA_MASK_HEIGHT_DP = 500;
     private static final int ALPHA_MASK_BITMAP_DP = 200;
     private static final int ALPHA_MASK_WIDTH_DP = 2;
 
-    private final Rect mHighlightRect = new Rect();
+    private boolean mDrawTopScrim, mDrawBottomScrim, mDrawWallpaperScrim;
 
-    private Workspace mWorkspace;
-
-    private boolean mDrawTopScrim, mDrawBottomScrim;
-
+    private final RectF mWallpaperScrimRect = new RectF();
+    private final Paint mWallpaperScrimPaint = new Paint();
     private final RectF mFinalMaskRect = new RectF();
     private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
     private final Bitmap mBottomMask;
     private final int mMaskHeight;
 
+    private final View mRoot;
+    private final BaseDraggingActivity mActivity;
     private final Drawable mTopScrim;
 
     private float mSysUiProgress = 1;
@@ -126,41 +122,32 @@
 
     private boolean mAnimateScrimOnNextDraw = false;
     private float mSysUiAnimMultiplier = 1;
+    private int mWallpaperScrimMaxAlpha;
 
-    public WorkspaceAndHotseatScrim(View view) {
-        super(view);
-
+    public SysUiScrim(View view) {
+        mRoot = view;
+        mActivity = BaseDraggingActivity.fromContext(view.getContext());
         mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP,
                 view.getResources().getDisplayMetrics());
         mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
         mBottomMask = mTopScrim == null ? null : createDitheredAlphaMask();
         mHideSysUiScrim = mTopScrim == null;
 
-        onExtractedColorsChanged(mWallpaperColorInfo);
+        mDrawWallpaperScrim = FeatureFlags.ENABLE_WALLPAPER_SCRIM.get()
+                && !Themes.getAttrBoolean(view.getContext(), R.attr.isMainColorDark)
+                && !Themes.getAttrBoolean(view.getContext(), R.attr.isWorkspaceDarkText);
+        ResourceProvider rp = DynamicResource.provider(view.getContext());
+        int wallpaperScrimColor = rp.getColor(R.color.wallpaper_scrim_color);
+        mWallpaperScrimMaxAlpha = Color.alpha(wallpaperScrimColor);
+        mWallpaperScrimPaint.setColor(wallpaperScrimColor);
+
+        view.addOnAttachStateChangeListener(this);
     }
 
-    public void setWorkspace(Workspace workspace)  {
-        mWorkspace = workspace;
-    }
-
+    /**
+     * Draw the top and bottom scrims
+     */
     public void draw(Canvas canvas) {
-        // Draw the background below children.
-        if (mScrimAlpha > 0) {
-            // Update the scroll position first to ensure scrim cutout is in the right place.
-            mWorkspace.computeScrollWithoutInvalidation();
-            CellLayout currCellLayout = mWorkspace.getCurrentDragOverlappingLayout();
-            canvas.save();
-            if (currCellLayout != null && currCellLayout != mLauncher.getHotseat()) {
-                // Cut a hole in the darkening scrim on the page that should be highlighted, if any.
-                mLauncher.getDragLayer()
-                        .getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
-                canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
-            }
-
-            super.draw(canvas);
-            canvas.restore();
-        }
-
         if (!mHideSysUiScrim) {
             if (mSysUiProgress <= 0) {
                 mAnimateScrimOnNextDraw = false;
@@ -173,11 +160,14 @@
 
                 ObjectAnimator oa = createSysuiMultiplierAnim(1);
                 oa.setDuration(600);
-                oa.setStartDelay(mLauncher.getWindow().getTransitionBackgroundFadeDuration());
+                oa.setStartDelay(mActivity.getWindow().getTransitionBackgroundFadeDuration());
                 oa.start();
                 mAnimateScrimOnNextDraw = false;
             }
 
+            if (mDrawWallpaperScrim) {
+                canvas.drawRect(mWallpaperScrimRect, mWallpaperScrimPaint);
+            }
             if (mDrawTopScrim) {
                 mTopScrim.draw(canvas);
             }
@@ -199,20 +189,17 @@
     /**
      * Determines whether to draw the top and/or bottom scrim based on new insets.
      */
-    public void onInsetsChanged(Rect insets, boolean allowSysuiScrims) {
-        mDrawTopScrim = allowSysuiScrims
-                && mTopScrim != null
-                && insets.top > 0;
-        mDrawBottomScrim = allowSysuiScrims
-                && mBottomMask != null
-                && !mLauncher.getDeviceProfile().isVerticalBarLayout()
+    public void onInsetsChanged(Rect insets) {
+        mDrawTopScrim = mTopScrim != null && insets.top > 0;
+        mDrawBottomScrim = mBottomMask != null
+                && !mActivity.getDeviceProfile().isVerticalBarLayout()
                 && hasBottomNavButtons();
     }
 
     private boolean hasBottomNavButtons() {
-        if (Utilities.ATLEAST_Q && mLauncher.getRootView() != null
-                && mLauncher.getRootView().getRootWindowInsets() != null) {
-            WindowInsets windowInsets = mLauncher.getRootView().getRootWindowInsets();
+        if (Utilities.ATLEAST_Q && mActivity.getRootView() != null
+                && mActivity.getRootView().getRootWindowInsets() != null) {
+            WindowInsets windowInsets = mActivity.getRootView().getRootWindowInsets();
             return windowInsets.getTappableElementInsets().bottom > 0;
         }
         return true;
@@ -220,8 +207,6 @@
 
     @Override
     public void onViewAttachedToWindow(View view) {
-        super.onViewAttachedToWindow(view);
-
         if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
             IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
             filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
@@ -231,27 +216,22 @@
 
     @Override
     public void onViewDetachedFromWindow(View view) {
-        super.onViewDetachedFromWindow(view);
         if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
             mRoot.getContext().unregisterReceiver(mReceiver);
         }
     }
 
-    @Override
-    public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
-        // for super light wallpaper it needs to be darken for contrast to workspace
-        // for dark wallpapers the text is white so darkening works as well
-        mBottomMaskPaint.setColor(ColorUtils.compositeColors(DARK_SCRIM_COLOR,
-                wallpaperColorInfo.getMainColor()));
-        reapplySysUiAlpha();
-        super.onExtractedColorsChanged(wallpaperColorInfo);
-    }
-
+    /**
+     * Set the width and height of the view being scrimmed
+     * @param w
+     * @param h
+     */
     public void setSize(int w, int h) {
         if (mTopScrim != null) {
             mTopScrim.setBounds(0, 0, w, h);
             mFinalMaskRect.set(0, h - mMaskHeight, w, h);
         }
+        mWallpaperScrimRect.set(0, 0, w, h);
     }
 
     private void setSysUiProgress(float progress) {
@@ -264,7 +244,7 @@
     private void reapplySysUiAlpha() {
         reapplySysUiAlphaNoInvalidate();
         if (!mHideSysUiScrim) {
-            invalidate();
+            mRoot.invalidate();
         }
     }
 
@@ -274,10 +254,11 @@
         if (mTopScrim != null) {
             mTopScrim.setAlpha(Math.round(255 * factor));
         }
+        mWallpaperScrimPaint.setAlpha(Math.round(mWallpaperScrimMaxAlpha * factor));
     }
 
     private Bitmap createDitheredAlphaMask() {
-        DisplayMetrics dm = mLauncher.getResources().getDisplayMetrics();
+        DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
         int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
         int gradientHeight = ResourceUtils.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
         Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8);
diff --git a/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
deleted file mode 100644
index b7dd092..0000000
--- a/src/com/android/launcher3/icons/ClockDrawableWrapper.java
+++ /dev/null
@@ -1,328 +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.icons;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.launcher3.FastBitmapDrawable;
-
-import java.util.Calendar;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Wrapper over {@link AdaptiveIconDrawable} to intercept icon flattening logic for dynamic
- * clock icons
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class ClockDrawableWrapper extends AdaptiveIconDrawable implements BitmapInfo.Extender {
-
-    private static final String TAG = "ClockDrawableWrapper";
-
-    private static final boolean DISABLE_SECONDS = true;
-
-    // Time after which the clock icon should check for an update. The actual invalidate
-    // will only happen in case of any change.
-    public static final long TICK_MS = DISABLE_SECONDS ? TimeUnit.MINUTES.toMillis(1) : 200L;
-
-    private static final String LAUNCHER_PACKAGE = "com.android.launcher3";
-    private static final String ROUND_ICON_METADATA_KEY = LAUNCHER_PACKAGE
-            + ".LEVEL_PER_TICK_ICON_ROUND";
-    private static final String HOUR_INDEX_METADATA_KEY = LAUNCHER_PACKAGE + ".HOUR_LAYER_INDEX";
-    private static final String MINUTE_INDEX_METADATA_KEY = LAUNCHER_PACKAGE
-            + ".MINUTE_LAYER_INDEX";
-    private static final String SECOND_INDEX_METADATA_KEY = LAUNCHER_PACKAGE
-            + ".SECOND_LAYER_INDEX";
-    private static final String DEFAULT_HOUR_METADATA_KEY = LAUNCHER_PACKAGE
-            + ".DEFAULT_HOUR";
-    private static final String DEFAULT_MINUTE_METADATA_KEY = LAUNCHER_PACKAGE
-            + ".DEFAULT_MINUTE";
-    private static final String DEFAULT_SECOND_METADATA_KEY = LAUNCHER_PACKAGE
-            + ".DEFAULT_SECOND";
-
-    /* Number of levels to jump per second for the second hand */
-    private static final int LEVELS_PER_SECOND = 10;
-
-    public static final int INVALID_VALUE = -1;
-
-    private final AnimationInfo mAnimationInfo = new AnimationInfo();
-    private int mTargetSdkVersion;
-
-    public ClockDrawableWrapper(AdaptiveIconDrawable base) {
-        super(base.getBackground(), base.getForeground());
-    }
-
-    /**
-     * Loads and returns the wrapper from the provided package, or returns null
-     * if it is unable to load.
-     */
-    public static ClockDrawableWrapper forPackage(Context context, String pkg, int iconDpi) {
-        try {
-            PackageManager pm = context.getPackageManager();
-            ApplicationInfo appInfo =  pm.getApplicationInfo(pkg,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
-            final Bundle metadata = appInfo.metaData;
-            if (metadata == null) {
-                return null;
-            }
-            int drawableId = metadata.getInt(ROUND_ICON_METADATA_KEY, 0);
-            if (drawableId == 0) {
-                return null;
-            }
-
-            Drawable drawable = pm.getResourcesForApplication(appInfo).getDrawableForDensity(
-                    drawableId, iconDpi).mutate();
-            if (!(drawable instanceof AdaptiveIconDrawable)) {
-                return null;
-            }
-
-            ClockDrawableWrapper wrapper =
-                    new ClockDrawableWrapper((AdaptiveIconDrawable) drawable);
-            wrapper.mTargetSdkVersion = appInfo.targetSdkVersion;
-            AnimationInfo info = wrapper.mAnimationInfo;
-
-            info.baseDrawableState = drawable.getConstantState();
-
-            info.hourLayerIndex = metadata.getInt(HOUR_INDEX_METADATA_KEY, INVALID_VALUE);
-            info.minuteLayerIndex = metadata.getInt(MINUTE_INDEX_METADATA_KEY, INVALID_VALUE);
-            info.secondLayerIndex = metadata.getInt(SECOND_INDEX_METADATA_KEY, INVALID_VALUE);
-
-            info.defaultHour = metadata.getInt(DEFAULT_HOUR_METADATA_KEY, 0);
-            info.defaultMinute = metadata.getInt(DEFAULT_MINUTE_METADATA_KEY, 0);
-            info.defaultSecond = metadata.getInt(DEFAULT_SECOND_METADATA_KEY, 0);
-
-            LayerDrawable foreground = (LayerDrawable) wrapper.getForeground();
-            int layerCount = foreground.getNumberOfLayers();
-            if (info.hourLayerIndex < 0 || info.hourLayerIndex >= layerCount) {
-                info.hourLayerIndex = INVALID_VALUE;
-            }
-            if (info.minuteLayerIndex < 0 || info.minuteLayerIndex >= layerCount) {
-                info.minuteLayerIndex = INVALID_VALUE;
-            }
-            if (info.secondLayerIndex < 0 || info.secondLayerIndex >= layerCount) {
-                info.secondLayerIndex = INVALID_VALUE;
-            } else if (DISABLE_SECONDS) {
-                foreground.setDrawable(info.secondLayerIndex, null);
-                info.secondLayerIndex = INVALID_VALUE;
-            }
-            return wrapper;
-        } catch (Exception e) {
-            Log.d(TAG, "Unable to load clock drawable info", e);
-        }
-        return null;
-    }
-
-    @Override
-    public BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory) {
-        iconFactory.disableColorExtraction();
-        float [] scale = new float[1];
-        AdaptiveIconDrawable background = new AdaptiveIconDrawable(
-                getBackground().getConstantState().newDrawable(), null);
-        BitmapInfo bitmapInfo = iconFactory.createBadgedIconBitmap(background,
-                Process.myUserHandle(), mTargetSdkVersion, false, scale);
-
-        return new ClockBitmapInfo(bitmap, color, scale[0], mAnimationInfo, bitmapInfo.icon);
-    }
-
-    @Override
-    public void prepareToDrawOnUi() {
-        mAnimationInfo.applyTime(Calendar.getInstance(), (LayerDrawable) getForeground());
-    }
-
-    private static class AnimationInfo {
-
-        public ConstantState baseDrawableState;
-
-        public int hourLayerIndex;
-        public int minuteLayerIndex;
-        public int secondLayerIndex;
-        public int defaultHour;
-        public int defaultMinute;
-        public int defaultSecond;
-
-        boolean applyTime(Calendar time, LayerDrawable foregroundDrawable) {
-            time.setTimeInMillis(System.currentTimeMillis());
-
-            // We need to rotate by the difference from the default time if one is specified.
-            int convertedHour = (time.get(Calendar.HOUR) + (12 - defaultHour)) % 12;
-            int convertedMinute = (time.get(Calendar.MINUTE) + (60 - defaultMinute)) % 60;
-            int convertedSecond = (time.get(Calendar.SECOND) + (60 - defaultSecond)) % 60;
-
-            boolean invalidate = false;
-            if (hourLayerIndex != INVALID_VALUE) {
-                final Drawable hour = foregroundDrawable.getDrawable(hourLayerIndex);
-                if (hour.setLevel(convertedHour * 60 + time.get(Calendar.MINUTE))) {
-                    invalidate = true;
-                }
-            }
-
-            if (minuteLayerIndex != INVALID_VALUE) {
-                final Drawable minute = foregroundDrawable.getDrawable(minuteLayerIndex);
-                if (minute.setLevel(time.get(Calendar.HOUR) * 60 + convertedMinute)) {
-                    invalidate = true;
-                }
-            }
-
-            if (secondLayerIndex != INVALID_VALUE) {
-                final Drawable second = foregroundDrawable.getDrawable(secondLayerIndex);
-                if (second.setLevel(convertedSecond * LEVELS_PER_SECOND)) {
-                    invalidate = true;
-                }
-            }
-
-            return invalidate;
-        }
-    }
-
-    private static class ClockBitmapInfo extends BitmapInfo implements FastBitmapDrawable.Factory {
-
-        public final float scale;
-        public final int offset;
-        public final AnimationInfo animInfo;
-        public final Bitmap mFlattenedBackground;
-
-        ClockBitmapInfo(Bitmap icon, int color, float scale, AnimationInfo animInfo,
-                Bitmap background) {
-            super(icon, color);
-            this.scale = scale;
-            this.animInfo = animInfo;
-            this.offset = (int) Math.ceil(ShadowGenerator.BLUR_FACTOR * icon.getWidth());
-            this.mFlattenedBackground = background;
-        }
-
-        @Override
-        public FastBitmapDrawable newDrawable() {
-            return new ClockIconDrawable(this);
-        }
-    }
-
-    private static class ClockIconDrawable extends FastBitmapDrawable implements Runnable {
-
-        private final Calendar mTime = Calendar.getInstance();
-
-        private final ClockBitmapInfo mInfo;
-
-        private final AdaptiveIconDrawable mFullDrawable;
-        private final LayerDrawable mForeground;
-
-        ClockIconDrawable(ClockBitmapInfo clockInfo) {
-            super(clockInfo);
-
-            mInfo = clockInfo;
-
-            mFullDrawable = (AdaptiveIconDrawable) mInfo.animInfo.baseDrawableState.newDrawable();
-            mForeground = (LayerDrawable) mFullDrawable.getForeground();
-        }
-
-        @Override
-        protected void onBoundsChange(Rect bounds) {
-            super.onBoundsChange(bounds);
-            mFullDrawable.setBounds(bounds);
-        }
-
-        @Override
-        public void drawInternal(Canvas canvas, Rect bounds) {
-            if (mInfo == null) {
-                super.drawInternal(canvas, bounds);
-                return;
-            }
-            // draw the background that is already flattened to a bitmap
-            canvas.drawBitmap(mInfo.mFlattenedBackground, null, bounds, mPaint);
-
-            // prepare and draw the foreground
-            mInfo.animInfo.applyTime(mTime, mForeground);
-
-            canvas.scale(mInfo.scale, mInfo.scale,
-                    bounds.exactCenterX() + mInfo.offset, bounds.exactCenterY() + mInfo.offset);
-            canvas.clipPath(mFullDrawable.getIconMask());
-            mForeground.draw(canvas);
-
-            reschedule();
-        }
-
-        @Override
-        protected void updateFilter() {
-            super.updateFilter();
-            mFullDrawable.setColorFilter(mPaint.getColorFilter());
-        }
-
-        @Override
-        public void run() {
-            if (mInfo.animInfo.applyTime(mTime, mForeground)) {
-                invalidateSelf();
-            } else {
-                reschedule();
-            }
-        }
-
-        @Override
-        public boolean setVisible(boolean visible, boolean restart) {
-            boolean result = super.setVisible(visible, restart);
-            if (visible) {
-                reschedule();
-            } else {
-                unscheduleSelf(this);
-            }
-            return result;
-        }
-
-        private void reschedule() {
-            if (!isVisible()) {
-                return;
-            }
-
-            unscheduleSelf(this);
-            final long upTime = SystemClock.uptimeMillis();
-            final long step = TICK_MS; /* tick every 200 ms */
-            scheduleSelf(this, upTime - ((upTime % step)) + step);
-        }
-
-        @Override
-        public ConstantState getConstantState() {
-            return new ClockConstantState(mInfo, isDisabled());
-        }
-
-        private static class ClockConstantState extends MyConstantState {
-
-            private final ClockBitmapInfo mInfo;
-
-            ClockConstantState(ClockBitmapInfo info, boolean isDisabled) {
-                super(info.icon, info.color, isDisabled);
-                mInfo = info;
-            }
-
-            @Override
-            public FastBitmapDrawable newDrawable() {
-                ClockIconDrawable drawable = new ClockIconDrawable(mInfo);
-                drawable.setIsDisabled(mIsDisabled);
-                return drawable;
-            }
-        }
-    }
-}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index a20bec2..cd13cd0 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -31,7 +31,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
@@ -40,6 +39,7 @@
 
 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;
@@ -52,7 +52,6 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
@@ -82,10 +81,11 @@
     private int mPendingIconRequestCount = 0;
 
     public IconCache(Context context, InvariantDeviceProfile idp) {
-        this(context, idp, LauncherFiles.APP_ICONS_DB);
+        this(context, idp, LauncherFiles.APP_ICONS_DB, new IconProvider(context));
     }
 
-    public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName) {
+    public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName,
+            IconProvider iconProvider) {
         super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
                 idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */);
         mComponentWithLabelCachingLogic = new ComponentCachingLogic(context, false);
@@ -94,7 +94,7 @@
         mLauncherApps = mContext.getSystemService(LauncherApps.class);
         mUserManager = UserCache.INSTANCE.get(mContext);
         mInstantAppResolver = InstantAppResolver.newInstance(mContext);
-        mIconProvider = new IconProvider(context);
+        mIconProvider = iconProvider;
     }
 
     @Override
@@ -108,7 +108,7 @@
     }
 
     @Override
-    protected BaseIconFactory getIconFactory() {
+    public BaseIconFactory getIconFactory() {
         return LauncherIcons.obtain(mContext);
     }
 
@@ -131,37 +131,43 @@
     }
 
     /**
+     * Closes the cache DB. This will clear any in-memory cache.
+     */
+    public void close() {
+        mIconDb.close();
+    }
+
+    /**
      * Fetches high-res icon for the provided ItemInfo and updates the caller when done.
+     *
      * @return a request ID that can be used to cancel the request.
      */
-    public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
+    public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller,
             final ItemInfoWithIcon info) {
         Preconditions.assertUIThread();
         if (mPendingIconRequestCount <= 0) {
             MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
         }
-        mPendingIconRequestCount ++;
+        mPendingIconRequestCount++;
 
-        IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
-            @Override
-            public void run() {
-                if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
-                    getTitleAndIcon(info, false);
-                } else if (info instanceof PackageItemInfo) {
-                    getTitleAndIconForApp((PackageItemInfo) info, false);
-                }
-                MAIN_EXECUTOR.execute(() -> {
-                    caller.reapplyItemInfo(info);
-                    onEnd();
-                });
-            }
-        };
+        HandlerRunnable<ItemInfoWithIcon> request = new HandlerRunnable<>(mWorkerHandler,
+                () -> {
+                    if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
+                        getTitleAndIcon(info, false);
+                    } else if (info instanceof PackageItemInfo) {
+                        getTitleAndIconForApp((PackageItemInfo) info, false);
+                    }
+                    return info;
+                },
+                MAIN_EXECUTOR,
+                caller::reapplyItemInfo,
+                this::onIconRequestEnd);
         Utilities.postAsyncCallback(mWorkerHandler, request);
         return request;
     }
 
     private void onIconRequestEnd() {
-        mPendingIconRequestCount --;
+        mPendingIconRequestCount--;
         if (mPendingIconRequestCount <= 0) {
             MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
         }
@@ -260,14 +266,6 @@
     }
 
     /**
-     * Fill in info with the icon and label for deep shortcut.
-     */
-    public synchronized CacheEntry getDeepShortcutTitleAndIcon(ShortcutInfo info) {
-        return cacheLocked(ShortcutKey.fromInfo(info).componentName, info.getUserHandle(),
-                () -> info, mShortcutCachingLogic, false, false);
-    }
-
-    /**
      * Fill in {@param info} with the icon and label. If the
      * corresponding activity is not found, it reverts to the package icon.
      */
@@ -295,12 +293,13 @@
     /**
      * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
      */
-    private synchronized void getTitleAndIcon(
+    public synchronized void getTitleAndIcon(
             @NonNull ItemInfoWithIcon infoInOut,
             @NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
             boolean usePkgIcon, boolean useLowResIcon) {
         CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
-                activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon, useLowResIcon);
+                activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon,
+                useLowResIcon);
         applyCacheEntry(entry, infoInOut);
     }
 
@@ -313,6 +312,11 @@
         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);
+            infoInOut.contentDescription = mPackageManager.getUserBadgedLabel(
+                    infoInOut.title, infoInOut.user);
+        }
     }
 
     protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
@@ -326,28 +330,13 @@
     }
 
     public void updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info) {
-        cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(), info.getAppLabel());
+        cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(),
+                info.getAppLabel());
     }
 
     @Override
     protected String getIconSystemState(String packageName) {
-        return mIconProvider.getSystemStateForPackage(mSystemState, packageName)
-                + ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
-    }
-
-    @Override
-    protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
-        if (mIconProvider.isClockIcon(cacheKey)) {
-            // For clock icon, we always load the dynamic icon
-            return false;
-        }
-        return super.getEntryFromDB(cacheKey, entry, lowRes);
-    }
-
-    public static abstract class IconLoadRequest extends HandlerRunnable {
-        IconLoadRequest(Handler handler, Runnable endRunnable) {
-            super(handler, endRunnable);
-        }
+        return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
     }
 
     /**
diff --git a/src/com/android/launcher3/icons/IconProvider.java b/src/com/android/launcher3/icons/IconProvider.java
deleted file mode 100644
index 1468b27..0000000
--- a/src/com/android/launcher3/icons/IconProvider.java
+++ /dev/null
@@ -1,251 +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.icons;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Process;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo.Extender;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.SafeCloseable;
-
-import java.util.Calendar;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-
-/**
- * Class to handle icon loading from different packages
- */
-public class IconProvider {
-
-    private static final String TAG = "IconProvider";
-    private static final boolean DEBUG = false;
-
-    private static final String ICON_METADATA_KEY_PREFIX = ".dynamic_icons";
-
-    private static final String SYSTEM_STATE_SEPARATOR = " ";
-
-    // Default value returned if there are problems getting resources.
-    private static final int NO_ID = 0;
-
-    private static final BiFunction<LauncherActivityInfo, Integer, Drawable> LAI_LOADER =
-            LauncherActivityInfo::getIcon;
-
-    private static final BiFunction<ActivityInfo, PackageManager, Drawable> AI_LOADER =
-            ActivityInfo::loadUnbadgedIcon;
-
-
-    private final Context mContext;
-    private final ComponentName mCalendar;
-    private final ComponentName mClock;
-
-    public IconProvider(Context context) {
-        mContext = context;
-        mCalendar = parseComponentOrNull(context, R.string.calendar_component_name);
-        mClock = parseComponentOrNull(context, R.string.clock_component_name);
-    }
-
-    /**
-     * Adds any modification to the provided systemState for dynamic icons. This system state
-     * is used by caches to check for icon invalidation.
-     */
-    public String getSystemStateForPackage(String systemState, String packageName) {
-        if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) {
-            return systemState + SYSTEM_STATE_SEPARATOR + getDay();
-        } else {
-            return systemState;
-        }
-    }
-
-    /**
-     * Loads the icon for the provided LauncherActivityInfo such that it can be drawn directly
-     * on the UI
-     */
-    public Drawable getIconForUI(LauncherActivityInfo info, int iconDpi) {
-        Drawable icon = getIcon(info, iconDpi);
-        if (icon instanceof BitmapInfo.Extender) {
-            ((Extender) icon).prepareToDrawOnUi();
-        }
-        return icon;
-    }
-
-    /**
-     * Loads the icon for the provided LauncherActivityInfo
-     */
-    public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
-        return getIcon(info.getApplicationInfo().packageName, info.getUser(),
-                info, iconDpi, LAI_LOADER);
-    }
-
-    /**
-     * Loads the icon for the provided activity info
-     */
-    public Drawable getIcon(ActivityInfo info, UserHandle user) {
-        return getIcon(info.applicationInfo.packageName, user, info, mContext.getPackageManager(),
-                AI_LOADER);
-    }
-
-    private <T, P> Drawable getIcon(String packageName, UserHandle user, T obj, P param,
-            BiFunction<T, P, Drawable> loader) {
-        Drawable icon = null;
-        if (mCalendar != null && mCalendar.getPackageName().equals(packageName)) {
-            icon = loadCalendarDrawable(0);
-        } else if (mClock != null
-                && mClock.getPackageName().equals(packageName)
-                && Process.myUserHandle().equals(user)) {
-            icon = loadClockDrawable(0);
-        }
-        return icon == null ? loader.apply(obj, param) : icon;
-    }
-
-    private Drawable loadCalendarDrawable(int iconDpi) {
-        PackageManager pm = mContext.getPackageManager();
-        try {
-            final Bundle metadata = pm.getActivityInfo(
-                    mCalendar,
-                    PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA)
-                    .metaData;
-            final Resources resources = pm.getResourcesForApplication(mCalendar.getPackageName());
-            final int id = getDynamicIconId(metadata, resources);
-            if (id != NO_ID) {
-                if (DEBUG) Log.d(TAG, "Got icon #" + id);
-                return resources.getDrawableForDensity(id, iconDpi, null /* theme */);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            if (DEBUG) {
-                Log.d(TAG, "Could not get activityinfo or resources for package: "
-                        + mCalendar.getPackageName());
-            }
-        }
-        return null;
-    }
-
-    private Drawable loadClockDrawable(int iconDpi) {
-        return ClockDrawableWrapper.forPackage(mContext, mClock.getPackageName(), iconDpi);
-    }
-
-    protected boolean isClockIcon(ComponentKey key) {
-        return mClock != null && mClock.equals(key.componentName)
-                && Process.myUserHandle().equals(key.user);
-    }
-
-    /**
-     * @param metadata metadata of the default activity of Calendar
-     * @param resources from the Calendar package
-     * @return the resource id for today's Calendar icon; 0 if resources cannot be found.
-     */
-    private int getDynamicIconId(Bundle metadata, Resources resources) {
-        if (metadata == null) {
-            return NO_ID;
-        }
-        String key = mCalendar.getPackageName() + ICON_METADATA_KEY_PREFIX;
-        final int arrayId = metadata.getInt(key, NO_ID);
-        if (arrayId == NO_ID) {
-            return NO_ID;
-        }
-        try {
-            return resources.obtainTypedArray(arrayId).getResourceId(getDay(), NO_ID);
-        } catch (Resources.NotFoundException e) {
-            if (DEBUG) {
-                Log.d(TAG, "package defines '" + key + "' but corresponding array not found");
-            }
-            return NO_ID;
-        }
-    }
-
-    /**
-     * @return Today's day of the month, zero-indexed.
-     */
-    private int getDay() {
-        return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1;
-    }
-
-
-    /**
-     * Registers a callback to listen for calendar icon changes.
-     * The callback receives the packageName for the calendar icon
-     */
-    public static SafeCloseable registerIconChangeListener(Context context,
-            BiConsumer<String, UserHandle> callback, Handler handler) {
-        ComponentName calendar = parseComponentOrNull(context, R.string.calendar_component_name);
-        ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
-
-        if (calendar == null && clock == null) {
-            return () -> { };
-        }
-
-        BroadcastReceiver receiver = new DateTimeChangeReceiver(callback);
-        final IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
-        if (calendar != null) {
-            filter.addAction(Intent.ACTION_TIME_CHANGED);
-            filter.addAction(Intent.ACTION_DATE_CHANGED);
-        }
-        context.registerReceiver(receiver, filter, null, handler);
-
-        return () -> context.unregisterReceiver(receiver);
-    }
-
-    private static class DateTimeChangeReceiver extends BroadcastReceiver {
-
-        private final BiConsumer<String, UserHandle> mCallback;
-
-        DateTimeChangeReceiver(BiConsumer<String, UserHandle> callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
-                ComponentName clock = parseComponentOrNull(context, R.string.clock_component_name);
-                if (clock != null) {
-                    mCallback.accept(clock.getPackageName(), Process.myUserHandle());
-                }
-            }
-
-            ComponentName calendar =
-                    parseComponentOrNull(context, R.string.calendar_component_name);
-            if (calendar != null) {
-                for (UserHandle user : UserCache.INSTANCE.get(context).getUserProfiles()) {
-                    mCallback.accept(calendar.getPackageName(), user);
-                }
-            }
-
-        }
-    }
-
-    private static ComponentName parseComponentOrNull(Context context, int resId) {
-        String cn = context.getString(resId);
-        return TextUtils.isEmpty(cn) ? null : ComponentName.unflattenFromString(cn);
-
-    }
-}
diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 93de35a..e820ac4 100644
--- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -20,6 +20,7 @@
 import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.cache.CachingLogic;
 import com.android.launcher3.util.ResourceBasedOverride;
@@ -56,8 +57,8 @@
     @Override
     public BitmapInfo loadIcon(Context context, LauncherActivityInfo object) {
         try (LauncherIcons li = LauncherIcons.obtain(context)) {
-            return li.createBadgedIconBitmap(new IconProvider(context)
-                            .getIcon(object, li.mFillResIconDpi),
+            return li.createBadgedIconBitmap(LauncherAppState.getInstance(context)
+                            .getIconProvider().getIcon(object, li.mFillResIconDpi),
                     object.getUser(), object.getApplicationInfo().targetSdkVersion);
         }
     }
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
deleted file mode 100644
index 800598e..0000000
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ /dev/null
@@ -1,93 +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.keyboard;
-
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.widget.PopupMenu;
-import android.widget.PopupMenu.OnMenuItemClickListener;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.PopupContainerWithArrow;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Handles showing a popup menu with available custom actions for a launcher icon.
- * This allows exposing various custom actions using keyboard shortcuts.
- */
-public class CustomActionsPopup implements OnMenuItemClickListener {
-
-    private final Launcher mLauncher;
-    private final LauncherAccessibilityDelegate mDelegate;
-    private final View mIcon;
-
-    public CustomActionsPopup(Launcher launcher, View icon) {
-        mLauncher = launcher;
-        mIcon = icon;
-        PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher);
-        if (container != null) {
-            mDelegate = container.getAccessibilityDelegate();
-        } else {
-            mDelegate = launcher.getAccessibilityDelegate();
-        }
-    }
-
-    private List<AccessibilityAction> getActionList() {
-        if (mIcon == null || !(mIcon.getTag() instanceof ItemInfo)) {
-            return Collections.EMPTY_LIST;
-        }
-
-        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
-        mDelegate.addSupportedActions(mIcon, info, true);
-        List<AccessibilityAction> result = new ArrayList<>(info.getActionList());
-        info.recycle();
-        return result;
-    }
-
-    public boolean canShow() {
-        return !getActionList().isEmpty();
-    }
-
-    public boolean show() {
-        List<AccessibilityAction> actions = getActionList();
-        if (actions.isEmpty()) {
-            return false;
-        }
-
-        PopupMenu popup = new PopupMenu(mLauncher, mIcon);
-        popup.setOnMenuItemClickListener(this);
-        Menu menu = popup.getMenu();
-        for (AccessibilityAction action : actions) {
-            menu.add(Menu.NONE, action.getId(), Menu.NONE, action.getLabel());
-        }
-        popup.show();
-        return true;
-    }
-
-    @Override
-    public boolean onMenuItemClick(MenuItem menuItem) {
-        return mDelegate.performAction(mIcon, (ItemInfo) mIcon.getTag(), menuItem.getItemId());
-    }
-}
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index c50189c..83003ff 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -16,18 +16,7 @@
 
 package com.android.launcher3.keyboard;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
 import android.graphics.Rect;
-import android.util.Property;
 import android.view.View;
 import android.view.View.OnFocusChangeListener;
 
@@ -36,205 +25,21 @@
 /**
  * A helper class to draw background of a focused view.
  */
-public abstract class FocusIndicatorHelper implements
-        OnFocusChangeListener, AnimatorUpdateListener {
-
-    private static final float MIN_VISIBLE_ALPHA = 0.2f;
-    private static final long ANIM_DURATION = 150;
-
-    public static final Property<FocusIndicatorHelper, Float> ALPHA =
-            new Property<FocusIndicatorHelper, Float>(Float.TYPE, "alpha") {
-                @Override
-                public void set(FocusIndicatorHelper object, Float value) {
-                    object.setAlpha(value);
-                }
-
-                @Override
-                public Float get(FocusIndicatorHelper object) {
-                    return object.mAlpha;
-                }
-            };
-
-    public static final Property<FocusIndicatorHelper, Float> SHIFT =
-            new Property<FocusIndicatorHelper, Float>(
-                    Float.TYPE, "shift") {
-
-                @Override
-                public void set(FocusIndicatorHelper object, Float value) {
-                    object.mShift = value;
-                }
-
-                @Override
-                public Float get(FocusIndicatorHelper object) {
-                    return object.mShift;
-                }
-            };
-
-    private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
-    private static final Rect sTempRect1 = new Rect();
-    private static final Rect sTempRect2 = new Rect();
-
-    private final View mContainer;
-    private final Paint mPaint;
-    private final int mMaxAlpha;
-
-    private final Rect mDirtyRect = new Rect();
-    private boolean mIsDirty = false;
-
-    private View mLastFocusedView;
-
-    private View mCurrentView;
-    private View mTargetView;
-    /**
-     * The fraction indicating the position of the focusRect between {@link #mCurrentView}
-     * & {@link #mTargetView}
-     */
-    private float mShift;
-
-    private ObjectAnimator mCurrentAnimation;
-    private float mAlpha;
+public abstract class FocusIndicatorHelper extends ItemFocusIndicatorHelper<View>
+        implements OnFocusChangeListener {
 
     public FocusIndicatorHelper(View container) {
-        mContainer = container;
-
-        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        int color = container.getResources().getColor(R.color.focused_background);
-        mMaxAlpha = Color.alpha(color);
-        mPaint.setColor(0xFF000000 | color);
-
-        setAlpha(0);
-        mShift = 0;
-    }
-
-    protected void setAlpha(float alpha) {
-        mAlpha = alpha;
-        mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
-    }
-
-    @Override
-    public void onAnimationUpdate(ValueAnimator animation) {
-        invalidateDirty();
-    }
-
-    protected void invalidateDirty() {
-        if (mIsDirty) {
-            mContainer.invalidate(mDirtyRect);
-            mIsDirty = false;
-        }
-
-        Rect newRect = getDrawRect();
-        if (newRect != null) {
-            mContainer.invalidate(newRect);
-        }
-    }
-
-    public void draw(Canvas c) {
-        if (mAlpha > 0) {
-            Rect newRect = getDrawRect();
-            if (newRect != null) {
-                mDirtyRect.set(newRect);
-                c.drawRect(mDirtyRect, mPaint);
-                mIsDirty = true;
-            }
-        }
-    }
-
-    private Rect getDrawRect() {
-        if (mCurrentView != null && mCurrentView.isAttachedToWindow()) {
-            viewToRect(mCurrentView, sTempRect1);
-
-            if (mShift > 0 && mTargetView != null) {
-                viewToRect(mTargetView, sTempRect2);
-                return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
-            } else {
-                return sTempRect1;
-            }
-        }
-        return null;
+        super(container, container.getResources().getColor(R.color.focused_background));
     }
 
     @Override
     public void onFocusChange(View v, boolean hasFocus) {
-        if (hasFocus) {
-            endCurrentAnimation();
-
-            if (mAlpha > MIN_VISIBLE_ALPHA) {
-                mTargetView = v;
-
-                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
-                        PropertyValuesHolder.ofFloat(ALPHA, 1),
-                        PropertyValuesHolder.ofFloat(SHIFT, 1));
-                mCurrentAnimation.addListener(new ViewSetListener(v, true));
-            } else {
-                setCurrentView(v);
-
-                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
-                        PropertyValuesHolder.ofFloat(ALPHA, 1));
-            }
-
-            mLastFocusedView = v;
-        } else {
-            if (mLastFocusedView == v) {
-                mLastFocusedView = null;
-                endCurrentAnimation();
-                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
-                        PropertyValuesHolder.ofFloat(ALPHA, 0));
-                mCurrentAnimation.addListener(new ViewSetListener(null, false));
-            }
-        }
-
-        // invalidate once
-        invalidateDirty();
-
-        mLastFocusedView = hasFocus ? v : null;
-        if (mCurrentAnimation != null) {
-            mCurrentAnimation.addUpdateListener(this);
-            mCurrentAnimation.setDuration(ANIM_DURATION).start();
-        }
+        changeFocus(v, hasFocus);
     }
 
-    protected void endCurrentAnimation() {
-        if (mCurrentAnimation != null) {
-            mCurrentAnimation.cancel();
-            mCurrentAnimation = null;
-        }
-    }
-
-    protected void setCurrentView(View v) {
-        mCurrentView = v;
-        mShift = 0;
-        mTargetView = null;
-    }
-
-    /**
-     * Gets the position of {@param v} relative to {@link #mContainer}.
-     */
-    public abstract void viewToRect(View v, Rect outRect);
-
-    private class ViewSetListener extends AnimatorListenerAdapter {
-        private final View mViewToSet;
-        private final boolean mCallOnCancel;
-        private boolean mCalled = false;
-
-        public ViewSetListener(View v, boolean callOnCancel) {
-            mViewToSet = v;
-            mCallOnCancel = callOnCancel;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            if (!mCallOnCancel) {
-                mCalled = true;
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (!mCalled) {
-                setCurrentView(mViewToSet);
-                mCalled = true;
-            }
-        }
+    @Override
+    protected boolean shouldDraw(View item) {
+        return item.isAttachedToWindow();
     }
 
     /**
diff --git a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
new file mode 100644
index 0000000..57fab2d
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
@@ -0,0 +1,252 @@
+/*
+ * 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.keyboard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.FloatProperty;
+import android.view.View;
+
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.Themes;
+
+/**
+ * A helper class to draw background of a focused item.
+ * @param <T> Item type
+ */
+public abstract class ItemFocusIndicatorHelper<T> implements AnimatorUpdateListener {
+
+    private static final float MIN_VISIBLE_ALPHA = 0.2f;
+    private static final long ANIM_DURATION = 150;
+
+    public static final FloatProperty<ItemFocusIndicatorHelper> ALPHA =
+            new FloatProperty<ItemFocusIndicatorHelper>("alpha") {
+
+                @Override
+                public void setValue(ItemFocusIndicatorHelper object, float value) {
+                    object.setAlpha(value);
+                }
+
+                @Override
+                public Float get(ItemFocusIndicatorHelper object) {
+                    return object.mAlpha;
+                }
+            };
+
+    public static final FloatProperty<ItemFocusIndicatorHelper> SHIFT =
+            new FloatProperty<ItemFocusIndicatorHelper>("shift") {
+
+                @Override
+                public void setValue(ItemFocusIndicatorHelper object, float value) {
+                    object.mShift = value;
+                }
+
+                @Override
+                public Float get(ItemFocusIndicatorHelper object) {
+                    return object.mShift;
+                }
+            };
+
+    private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+    private static final Rect sTempRect1 = new Rect();
+    private static final Rect sTempRect2 = new Rect();
+
+    private final View mContainer;
+    protected final Paint mPaint;
+    private final int mMaxAlpha;
+
+    private final Rect mDirtyRect = new Rect();
+    private boolean mIsDirty = false;
+
+    private T mLastFocusedItem;
+
+    private T mCurrentItem;
+    private T mTargetItem;
+    /**
+     * The fraction indicating the position of the focusRect between {@link #mCurrentItem}
+     * & {@link #mTargetItem}
+     */
+    private float mShift;
+
+    private ObjectAnimator mCurrentAnimation;
+    private float mAlpha;
+    private float mRadius;
+
+    public ItemFocusIndicatorHelper(View container, int color) {
+        mContainer = container;
+
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mMaxAlpha = Color.alpha(color);
+        mPaint.setColor(0xFF000000 | color);
+
+        setAlpha(0);
+        mShift = 0;
+        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+            mRadius = Themes.getDialogCornerRadius(container.getContext());
+        }
+    }
+
+    protected void setAlpha(float alpha) {
+        mAlpha = alpha;
+        mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator animation) {
+        invalidateDirty();
+    }
+
+    protected void invalidateDirty() {
+        if (mIsDirty) {
+            mContainer.invalidate(mDirtyRect);
+            mIsDirty = false;
+        }
+
+        Rect newRect = getDrawRect();
+        if (newRect != null) {
+            mContainer.invalidate(newRect);
+        }
+    }
+
+    /**
+     * Draws the indicator on the canvas
+     */
+    public void draw(Canvas c) {
+        if (mAlpha <= 0) return;
+
+        Rect newRect = getDrawRect();
+        if (newRect != null) {
+            mDirtyRect.set(newRect);
+            c.drawRoundRect((float) mDirtyRect.left, (float) mDirtyRect.top,
+                    (float) mDirtyRect.right, (float) mDirtyRect.bottom,
+                    mRadius, mRadius, mPaint);
+            mIsDirty = true;
+        }
+    }
+
+    private Rect getDrawRect() {
+        if (mCurrentItem != null && shouldDraw(mCurrentItem)) {
+            viewToRect(mCurrentItem, sTempRect1);
+
+            if (mShift > 0 && mTargetItem != null) {
+                viewToRect(mTargetItem, sTempRect2);
+                return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
+            } else {
+                return sTempRect1;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns true if the provided item is valid
+     */
+    protected boolean shouldDraw(T item) {
+        return true;
+    }
+
+    protected void changeFocus(T item, boolean hasFocus) {
+        if (hasFocus) {
+            endCurrentAnimation();
+
+            if (mAlpha > MIN_VISIBLE_ALPHA) {
+                mTargetItem = item;
+
+                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+                        PropertyValuesHolder.ofFloat(ALPHA, 1),
+                        PropertyValuesHolder.ofFloat(SHIFT, 1));
+                mCurrentAnimation.addListener(new ViewSetListener(item, true));
+            } else {
+                setCurrentItem(item);
+
+                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+                        PropertyValuesHolder.ofFloat(ALPHA, 1));
+            }
+
+            mLastFocusedItem = item;
+        } else {
+            if (mLastFocusedItem == item) {
+                mLastFocusedItem = null;
+                endCurrentAnimation();
+                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+                        PropertyValuesHolder.ofFloat(ALPHA, 0));
+                mCurrentAnimation.addListener(new ViewSetListener(null, false));
+            }
+        }
+
+        // invalidate once
+        invalidateDirty();
+
+        mLastFocusedItem = hasFocus ? item : null;
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.addUpdateListener(this);
+            mCurrentAnimation.setDuration(ANIM_DURATION).start();
+        }
+    }
+
+    protected void endCurrentAnimation() {
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.cancel();
+            mCurrentAnimation = null;
+        }
+    }
+
+    protected void setCurrentItem(T item) {
+        mCurrentItem = item;
+        mShift = 0;
+        mTargetItem = null;
+    }
+
+    /**
+     * Gets the position of the item relative to {@link #mContainer}.
+     */
+    public abstract void viewToRect(T item, Rect outRect);
+
+    private class ViewSetListener extends AnimatorListenerAdapter {
+        private final T mItemToSet;
+        private final boolean mCallOnCancel;
+        private boolean mCalled = false;
+
+        ViewSetListener(T item, boolean callOnCancel) {
+            mItemToSet = item;
+            mCallOnCancel = callOnCancel;
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (!mCallOnCancel) {
+                mCalled = true;
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (!mCalled) {
+                setCurrentItem(mItemToSet);
+                mCalled = true;
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java b/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
new file mode 100644
index 0000000..a6c897f
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
@@ -0,0 +1,342 @@
+/*
+ * 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.keyboard;
+
+import static android.app.Activity.DEFAULT_KEYS_SEARCH_LOCAL;
+
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.TextView;
+
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.util.Themes;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.function.ToIntBiFunction;
+import java.util.function.ToIntFunction;
+
+/**
+ * A floating view to allow keyboard navigation across virtual nodes
+ */
+public class KeyboardDragAndDropView extends AbstractFloatingView
+        implements Insettable, StateListener<LauncherState> {
+
+    private static final long MINOR_AXIS_WEIGHT = 13;
+
+    private final ArrayList<Integer> mIntList = new ArrayList<>();
+    private final ArrayList<DragAndDropAccessibilityDelegate> mDelegates = new ArrayList<>();
+    private final ArrayList<VirtualNodeInfo> mNodes = new ArrayList<>();
+
+    private final Rect mTempRect = new Rect();
+    private final Rect mTempRect2 = new Rect();
+    private final AccessibilityNodeInfoCompat mTempNodeInfo = AccessibilityNodeInfoCompat.obtain();
+
+    private final RectFocusIndicator mFocusIndicator;
+
+    private final Launcher mLauncher;
+    private VirtualNodeInfo mCurrentSelection;
+
+
+    public KeyboardDragAndDropView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyboardDragAndDropView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+        mFocusIndicator = new RectFocusIndicator(this);
+        setWillNotDraw(false);
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        mLauncher.getDragLayer().removeView(this);
+        mLauncher.getStateManager().removeStateListener(this);
+        mLauncher.setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+        mIsOpen = false;
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_DRAG_DROP_POPUP) != 0;
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        // Consume all touch
+        return true;
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        setPadding(insets.left, insets.top, insets.right, insets.bottom);
+    }
+
+    @Override
+    public void onStateTransitionStart(LauncherState toState) {
+        if (toState != SPRING_LOADED) {
+            close(false);
+        }
+    }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        if (mCurrentSelection != null) {
+            setCurrentSelection(mCurrentSelection);
+        }
+    }
+
+    private void setCurrentSelection(VirtualNodeInfo nodeInfo) {
+        mCurrentSelection = nodeInfo;
+        ((TextView) findViewById(R.id.label))
+                .setText(nodeInfo.populate(mTempNodeInfo).getContentDescription());
+
+        Rect bounds = new Rect();
+        mTempNodeInfo.getBoundsInParent(bounds);
+        View host = nodeInfo.delegate.getHost();
+        ViewParent parent = host.getParent();
+        if (parent instanceof PagedView) {
+            PagedView pv = (PagedView) parent;
+            int pageIndex = pv.indexOfChild(host);
+
+            pv.setCurrentPage(pageIndex);
+            bounds.offset(pv.getScrollX() - pv.getScrollForPage(pageIndex), 0);
+        }
+        float[] pos = new float[] {bounds.left, bounds.top, bounds.right, bounds.bottom};
+        Utilities.getDescendantCoordRelativeToAncestor(host, mLauncher.getDragLayer(), pos, true);
+
+        new RectF(pos[0], pos[1], pos[2], pos[3]).roundOut(bounds);
+        mFocusIndicator.changeFocus(bounds, true);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        mFocusIndicator.draw(canvas);
+    }
+
+    @Override
+    public boolean dispatchUnhandledMove(View focused, int direction) {
+        VirtualNodeInfo nodeInfo = getNextSelection(direction);
+        if (nodeInfo == null) {
+            return false;
+        }
+        setCurrentSelection(nodeInfo);
+        return true;
+    }
+
+    /**
+     * Focus finding logic:
+     * Collect all virtual nodes in reading order (used for forward and backwards).
+     * Then find the closest view by comparing the distances spatially. Since it is a move
+     * operation. consider all cell sizes to be approximately of the same size.
+     */
+    private VirtualNodeInfo getNextSelection(int direction) {
+        // Collect all virtual nodes
+        mDelegates.clear();
+        mNodes.clear();
+
+        Folder openFolder = Folder.getOpen(mLauncher);
+        PagedView pv = openFolder == null ? mLauncher.getWorkspace() : openFolder.getContent();
+        int count = pv.getPageCount();
+        for (int i = 0; i < count; i++) {
+            mDelegates.add(((CellLayout) pv.getChildAt(i)).getDragAndDropAccessibilityDelegate());
+        }
+        if (openFolder == null) {
+            mDelegates.add(pv.getNextPage() + 1,
+                    mLauncher.getHotseat().getDragAndDropAccessibilityDelegate());
+        }
+        mDelegates.forEach(delegate -> {
+            mIntList.clear();
+            delegate.getVisibleVirtualViews(mIntList);
+            mIntList.forEach(id -> mNodes.add(new VirtualNodeInfo(delegate, id)));
+        });
+
+        if (mNodes.isEmpty()) {
+            return null;
+        }
+        int index = mNodes.indexOf(mCurrentSelection);
+        if (mCurrentSelection == null || index < 0) {
+            return null;
+        }
+        int totalNodes = mNodes.size();
+
+        final ToIntBiFunction<Rect, Rect> majorAxis;
+        final ToIntFunction<Rect> minorAxis;
+
+        switch (direction) {
+            case View.FOCUS_RIGHT:
+                majorAxis = (source, dest) -> dest.left - source.left;
+                minorAxis = Rect::centerY;
+                break;
+            case View.FOCUS_LEFT:
+                majorAxis = (source, dest) -> source.left - dest.left;
+                minorAxis = Rect::centerY;
+                break;
+            case View.FOCUS_UP:
+                majorAxis = (source, dest) -> source.top - dest.top;
+                minorAxis = Rect::centerX;
+                break;
+            case View.FOCUS_DOWN:
+                majorAxis = (source, dest) -> dest.top - source.top;
+                minorAxis = Rect::centerX;
+                break;
+            case View.FOCUS_FORWARD:
+                return mNodes.get((index + 1) % totalNodes);
+            case View.FOCUS_BACKWARD:
+                return mNodes.get((index + totalNodes - 1) % totalNodes);
+            default:
+                // Unknown direction
+                return null;
+        }
+        mCurrentSelection.populate(mTempNodeInfo).getBoundsInScreen(mTempRect);
+
+        float minWeight = Float.MAX_VALUE;
+        VirtualNodeInfo match = null;
+        for (int i = 0; i < totalNodes; i++) {
+            VirtualNodeInfo node = mNodes.get(i);
+            node.populate(mTempNodeInfo).getBoundsInScreen(mTempRect2);
+
+            int majorAxisWeight = majorAxis.applyAsInt(mTempRect, mTempRect2);
+            if (majorAxisWeight <= 0) {
+                continue;
+            }
+            int minorAxisWeight = minorAxis.applyAsInt(mTempRect2)
+                    - minorAxis.applyAsInt(mTempRect);
+
+            float weight = majorAxisWeight * majorAxisWeight
+                    + minorAxisWeight * minorAxisWeight * MINOR_AXIS_WEIGHT;
+            if (weight < minWeight) {
+                minWeight = weight;
+                match = node;
+            }
+        }
+        return match;
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_ENTER && mCurrentSelection != null) {
+            mCurrentSelection.delegate.onPerformActionForVirtualView(
+                    mCurrentSelection.id, AccessibilityNodeInfoCompat.ACTION_CLICK, null);
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    /**
+     * Shows the keyboard drag popup for the provided view
+     */
+    public void showForIcon(View icon, ItemInfo item, DragOptions dragOptions) {
+        mIsOpen = true;
+        mLauncher.getDragLayer().addView(this);
+        mLauncher.getStateManager().addStateListener(this);
+
+        // Find current selection
+        CellLayout currentParent = (CellLayout) icon.getParent().getParent();
+        float[] iconPos = new float[] {currentParent.getCellWidth() / 2,
+                currentParent.getCellHeight() / 2};
+        Utilities.getDescendantCoordRelativeToAncestor(icon, currentParent, iconPos, false);
+
+        ItemLongClickListener.beginDrag(icon, mLauncher, item, dragOptions);
+
+        DragAndDropAccessibilityDelegate dndDelegate =
+                currentParent.getDragAndDropAccessibilityDelegate();
+        setCurrentSelection(new VirtualNodeInfo(
+                dndDelegate, dndDelegate.getVirtualViewAt(iconPos[0], iconPos[1])));
+
+        mLauncher.setDefaultKeyMode(Activity.DEFAULT_KEYS_DISABLE);
+        requestFocus();
+    }
+
+    private static class VirtualNodeInfo {
+        public final DragAndDropAccessibilityDelegate delegate;
+        public final int id;
+
+        VirtualNodeInfo(DragAndDropAccessibilityDelegate delegate, int id) {
+            this.id = id;
+            this.delegate = delegate;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof VirtualNodeInfo)) {
+                return false;
+            }
+            VirtualNodeInfo that = (VirtualNodeInfo) o;
+            return id == that.id && delegate.equals(that.delegate);
+        }
+
+        public AccessibilityNodeInfoCompat populate(AccessibilityNodeInfoCompat nodeInfo) {
+            delegate.onPopulateNodeForVirtualView(id, nodeInfo);
+            return nodeInfo;
+        }
+
+        public void getBounds(AccessibilityNodeInfoCompat nodeInfo, Rect out) {
+            delegate.onPopulateNodeForVirtualView(id, nodeInfo);
+            nodeInfo.getBoundsInScreen(out);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(id, delegate);
+        }
+    }
+
+    private static class RectFocusIndicator extends ItemFocusIndicatorHelper<Rect> {
+
+        RectFocusIndicator(View container) {
+            super(container, Themes.getColorAccent(container.getContext()));
+            mPaint.setStrokeWidth(container.getResources()
+                    .getDimension(R.dimen.keyboard_drag_stroke_width));
+            mPaint.setStyle(Style.STROKE);
+        }
+
+        @Override
+        public void viewToRect(Rect item, Rect outRect) {
+            outRect.set(item);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 6bc1ecb..cdd0bda 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -10,7 +10,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.IOUtils;
 
 import java.io.BufferedReader;
@@ -43,7 +42,7 @@
     private static Handler sHandler = null;
     private static File sLogsDirectory = null;
 
-    public static final int LOG_DAYS = FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() ? 10 : 4;
+    public static final int LOG_DAYS = 4;
 
     public static void setDir(File logsDir) {
         if (ENABLED) {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
deleted file mode 100644
index cd4f034..0000000
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ /dev/null
@@ -1,190 +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.logging;
-
-import android.util.ArrayMap;
-import android.util.SparseArray;
-import android.view.View;
-
-import com.android.launcher3.ButtonDropTarget;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.InstantAppResolver;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-
-/**
- * Helper methods for logging.
- */
-public class LoggerUtils {
-    private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
-    private static final String UNKNOWN = "UNKNOWN";
-    private static final int DEFAULT_PREDICTED_RANK = 10000;
-    private static final String DELIMITER_DOT = "\\.";
-
-    public static String getFieldName(int value, Class c) {
-        SparseArray<String> cache;
-        synchronized (sNameCache) {
-            cache = sNameCache.get(c);
-            if (cache == null) {
-                cache = new SparseArray<>();
-                for (Field f : c.getDeclaredFields()) {
-                    if (f.getType() == int.class && Modifier.isStatic(f.getModifiers())) {
-                        try {
-                            f.setAccessible(true);
-                            cache.put(f.getInt(null), f.getName());
-                        } catch (IllegalAccessException e) {
-                            // Ignore
-                        }
-                    }
-                }
-                sNameCache.put(c, cache);
-            }
-        }
-        String result = cache.get(value);
-        return result != null ? result : UNKNOWN;
-    }
-
-    public static Target newItemTarget(int itemType) {
-        Target t = newTarget(Target.Type.ITEM);
-        t.itemType = itemType;
-        return t;
-    }
-
-    public static Target newItemTarget(View v, InstantAppResolver instantAppResolver) {
-        return (v != null) && (v.getTag() instanceof ItemInfo)
-                ? newItemTarget((ItemInfo) v.getTag(), instantAppResolver)
-                : newTarget(Target.Type.ITEM);
-    }
-
-    public static Target newItemTarget(ItemInfo info, InstantAppResolver instantAppResolver) {
-        Target t = newTarget(Target.Type.ITEM);
-        switch (info.itemType) {
-            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-                t.itemType = (instantAppResolver != null && info instanceof AppInfo
-                        && instantAppResolver.isInstantApp(((AppInfo) info)))
-                        ? ItemType.WEB_APP
-                        : ItemType.APP_ICON;
-                t.predictedRank = DEFAULT_PREDICTED_RANK;
-                break;
-            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                t.itemType = ItemType.SHORTCUT;
-                t.predictedRank = DEFAULT_PREDICTED_RANK;
-                break;
-            case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                t.itemType = ItemType.FOLDER_ICON;
-                break;
-            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                t.itemType = ItemType.WIDGET;
-                break;
-            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                t.itemType = ItemType.DEEPSHORTCUT;
-                t.predictedRank = DEFAULT_PREDICTED_RANK;
-                break;
-        }
-        return t;
-    }
-
-    public static Target newDropTarget(View v) {
-        if (!(v instanceof ButtonDropTarget)) {
-            return newTarget(Target.Type.CONTAINER);
-        }
-        if (v instanceof ButtonDropTarget) {
-            return ((ButtonDropTarget) v).getDropTargetForLogging();
-        }
-        return newTarget(Target.Type.CONTROL);
-    }
-
-    public static Target newTarget(int targetType, TargetExtension extension) {
-        Target t = new Target();
-        t.type = targetType;
-        t.extension = extension;
-        return t;
-    }
-
-    public static Target newTarget(int targetType) {
-        Target t = new Target();
-        t.type = targetType;
-        return t;
-    }
-
-    public static Target newControlTarget(int controlType) {
-        Target t = newTarget(Target.Type.CONTROL);
-        t.controlType = controlType;
-        return t;
-    }
-
-    public static Target newContainerTarget(int containerType) {
-        Target t = newTarget(Target.Type.CONTAINER);
-        t.containerType = containerType;
-        return t;
-    }
-
-    public static Action newAction(int type) {
-        Action a = new Action();
-        a.type = type;
-        return a;
-    }
-
-    public static Action newCommandAction(int command) {
-        Action a = newAction(Action.Type.COMMAND);
-        a.command = command;
-        return a;
-    }
-
-    public static Action newTouchAction(int touch) {
-        Action a = newAction(Action.Type.TOUCH);
-        a.touch = touch;
-        return a;
-    }
-
-    public static LauncherEvent newLauncherEvent(Action action, Target... srcTargets) {
-        LauncherEvent event = new LauncherEvent();
-        event.srcTarget = srcTargets;
-        event.action = action;
-        return event;
-    }
-
-    /**
-     * Creates LauncherEvent using Action and ArrayList of Targets
-     */
-    public static LauncherEvent newLauncherEvent(Action action, ArrayList<Target> targets) {
-        Target[] targetsArray = new Target[targets.size()];
-        targets.toArray(targetsArray);
-        return newLauncherEvent(action, targetsArray);
-    }
-
-    /**
-     * String conversion for only the helpful parts of {@link Object#toString()} method
-     * @param stringToExtract "foo.bar.baz.MyObject@1234"
-     * @return "MyObject@1234"
-     */
-    public static String extractObjectNameAndAddress(String stringToExtract) {
-        String[] superStringParts = stringToExtract.split(DELIMITER_DOT);
-        if (superStringParts.length == 0) {
-            return "";
-        }
-        return superStringParts[superStringParts.length - 1];
-    }
-}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index c4a4c98..79e5b5d 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -15,32 +15,31 @@
  */
 package com.android.launcher3.logging;
 
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_CLOSE_DOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_OPEN_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
 
 import android.content.Context;
+import android.view.View;
 
 import androidx.annotation.Nullable;
+import androidx.slice.SliceItem;
 
 import com.android.launcher3.R;
+import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.logger.LauncherAtom.FromState;
 import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.LauncherLogProto;
 import com.android.launcher3.util.ResourceBasedOverride;
 
-import java.util.List;
-
 /**
  * Handles the user event logging in R+.
  *
  * <pre>
  * All of the event ids are defined here.
- * Most of the methods are dummy methods for Launcher3
+ * Most of the methods are placeholder methods for Launcher3
  * Actual call happens only for Launcher variant that implements QuickStep.
  * </pre>
  */
@@ -53,41 +52,24 @@
     public static final int LAUNCHER_STATE_ALLAPPS = 4;
     public static final int LAUNCHER_STATE_UNCHANGED = 5;
 
+    private InstanceId mInstanceId;
     /**
-     * Returns proper launcher state enum for {@link StatsLogManager}(to be removed during
-     * UserEventDispatcher cleanup)
-     */
-    public static int containerTypeToAtomState(int containerType) {
-        switch (containerType) {
-            case LauncherLogProto.ContainerType.ALLAPPS_VALUE:
-                return LAUNCHER_STATE_ALLAPPS;
-            case LauncherLogProto.ContainerType.OVERVIEW_VALUE:
-                return LAUNCHER_STATE_OVERVIEW;
-            case LauncherLogProto.ContainerType.WORKSPACE_VALUE:
-                return LAUNCHER_STATE_HOME;
-            case LauncherLogProto.ContainerType.APP_VALUE:
-                return LAUNCHER_STATE_BACKGROUND;
-        }
-        return LAUNCHER_STATE_UNSPECIFIED;
-    }
-
-    /**
-     * Returns event enum based on the two {@link ContainerType} transition information when swipe
+     * Returns event enum based on the two state transition information when swipe
      * gesture happens(to be removed during UserEventDispatcher cleanup).
      */
-    public static EventEnum getLauncherAtomEvent(int startContainerType,
-            int targetContainerType, EventEnum fallbackEvent) {
-        if (startContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()
-                && targetContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()) {
+    public static EventEnum getLauncherAtomEvent(int startState,
+            int targetState, EventEnum fallbackEvent) {
+        if (startState == LAUNCHER_STATE_HOME
+                && targetState == LAUNCHER_STATE_HOME) {
             return LAUNCHER_HOME_GESTURE;
-        } else if (startContainerType != LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()
-                && targetContainerType == LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()) {
+        } else if (startState != LAUNCHER_STATE_OVERVIEW
+                && targetState == LAUNCHER_STATE_OVERVIEW) {
             return LAUNCHER_OVERVIEW_GESTURE;
-        } else if (startContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()
-                && targetContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
+        } else if (startState != LAUNCHER_STATE_ALLAPPS
+                && targetState == LAUNCHER_STATE_ALLAPPS) {
             return LAUNCHER_ALLAPPS_OPEN_UP;
-        } else if (startContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()
-                && targetContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
+        } else if (startState == LAUNCHER_STATE_ALLAPPS
+                && targetState != LAUNCHER_STATE_ALLAPPS) {
             return LAUNCHER_ALLAPPS_CLOSE_DOWN;
         }
         return fallbackEvent; // TODO fix
@@ -119,9 +101,13 @@
         @UiEvent(doc = "User dragged a launcher item")
         LAUNCHER_ITEM_DRAG_STARTED(383),
 
-        @UiEvent(doc = "A dragged launcher item is successfully dropped")
+        @UiEvent(doc = "A dragged launcher item is successfully dropped onto workspace, hotseat "
+                + "open folder etc")
         LAUNCHER_ITEM_DROP_COMPLETED(385),
 
+        @UiEvent(doc = "A dragged launcher item is successfully dropped onto a folder icon.")
+        LAUNCHER_ITEM_DROP_COMPLETED_ON_FOLDER_ICON(697),
+
         @UiEvent(doc = "A dragged launcher item is successfully dropped on another item "
                 + "resulting in a new folder creation")
         LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
@@ -150,6 +136,12 @@
         @UiEvent(doc = "User tapped or long pressed on widget tray icon inside launcher settings.")
         LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS(464),
 
+        @UiEvent(doc = "User expanded the list of widgets for a single app in the widget picker.")
+        LAUNCHER_WIDGETSTRAY_APP_EXPANDED(818),
+
+        @UiEvent(doc = "User searched for a widget in the widget picker.")
+        LAUNCHER_WIDGETSTRAY_SEARCHED(819),
+
         @UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
 
@@ -321,8 +313,182 @@
         @UiEvent(doc = "User tapped on image content in Overview Select mode.")
         LAUNCHER_SELECT_MODE_IMAGE(627),
 
+        @UiEvent(doc = "Activity to add external item was started")
+        LAUNCHER_ADD_EXTERNAL_ITEM_START(641),
+
+        @UiEvent(doc = "Activity to add external item was cancelled")
+        LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED(642),
+
+        @UiEvent(doc = "Activity to add external item was backed out")
+        LAUNCHER_ADD_EXTERNAL_ITEM_BACK(643),
+
+        @UiEvent(doc = "Item was placed automatically in external item addition flow")
+        LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY(644),
+
+        @UiEvent(doc = "Item was dragged in external item addition flow")
+        LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED(645),
+
+        @UiEvent(doc = "A folder was replaced by a single item")
+        LAUNCHER_FOLDER_CONVERTED_TO_ICON(646),
+
+        @UiEvent(doc = "A hotseat prediction item was pinned")
+        LAUNCHER_HOTSEAT_PREDICTION_PINNED(647),
+
+        @UiEvent(doc = "Undo event was tapped.")
+        LAUNCHER_UNDO(648),
+
+        @UiEvent(doc = "Task switcher clear all target was tapped.")
+        LAUNCHER_TASK_CLEAR_ALL(649),
+
+        @UiEvent(doc = "Task preview was long pressed.")
+        LAUNCHER_TASK_PREVIEW_LONGPRESS(650),
+
         @UiEvent(doc = "User swiped down on workspace (triggering noti shade to open).")
-        LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN(651);
+        LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN(651),
+
+        @UiEvent(doc = "Notification dismissed by swiping right.")
+        LAUNCHER_NOTIFICATION_DISMISSED(652),
+
+        @UiEvent(doc = "Current grid size is changed to 5.")
+        LAUNCHER_GRID_SIZE_5(662),
+
+        @UiEvent(doc = "Current grid size is changed to 4.")
+        LAUNCHER_GRID_SIZE_4(663),
+
+        @UiEvent(doc = "Current grid size is changed to 3.")
+        LAUNCHER_GRID_SIZE_3(664),
+
+        @UiEvent(doc = "Current grid size is changed to 2.")
+        LAUNCHER_GRID_SIZE_2(665),
+
+        @UiEvent(doc = "Launcher entered into AllApps state.")
+        LAUNCHER_ALLAPPS_ENTRY(692),
+
+        @UiEvent(doc = "Launcher exited from AllApps state.")
+        LAUNCHER_ALLAPPS_EXIT(693),
+
+        @UiEvent(doc = "User closed the AllApps keyboard.")
+        LAUNCHER_ALLAPPS_KEYBOARD_CLOSED(694),
+
+        @UiEvent(doc = "User switched to AllApps Main/Personal tab by swiping left.")
+        LAUNCHER_ALLAPPS_SWIPE_TO_PERSONAL_TAB(695),
+
+        @UiEvent(doc = "User switched to AllApps Work tab by swiping right.")
+        LAUNCHER_ALLAPPS_SWIPE_TO_WORK_TAB(696),
+
+        @UiEvent(doc = "Default event when dedicated UI event is not available for the user action"
+                + " on slice .")
+        LAUNCHER_SLICE_DEFAULT_ACTION(700),
+
+        @UiEvent(doc = "User toggled-on a Slice item.")
+        LAUNCHER_SLICE_TOGGLE_ON(701),
+
+        @UiEvent(doc = "User toggled-off a Slice item.")
+        LAUNCHER_SLICE_TOGGLE_OFF(702),
+
+        @UiEvent(doc = "User acted on a Slice item with a button.")
+        LAUNCHER_SLICE_BUTTON_ACTION(703),
+
+        @UiEvent(doc = "User acted on a Slice item with a slider.")
+        LAUNCHER_SLICE_SLIDER_ACTION(704),
+
+        @UiEvent(doc = "User tapped on the entire row of a Slice.")
+        LAUNCHER_SLICE_CONTENT_ACTION(705),
+
+        @UiEvent(doc = "User tapped on the see more button of a Slice.")
+        LAUNCHER_SLICE_SEE_MORE_ACTION(706),
+
+        @UiEvent(doc = "User selected from a selection row of Slice.")
+        LAUNCHER_SLICE_SELECTION_ACTION(707),
+
+        @UiEvent(doc = "IME is used for selecting the focused item on the AllApps screen.")
+        LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME(718),
+
+        @UiEvent(doc = "User long-pressed on an AllApps item.")
+        LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED(719),
+
+        @UiEvent(doc = "Launcher entered into AllApps state with device search enabled.")
+        LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH(720),
+
+        @UiEvent(doc = "User switched to AllApps Main/Personal tab by tapping on it.")
+        LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB(721),
+
+        @UiEvent(doc = "User switched to AllApps Work tab by tapping on it.")
+        LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB(722),
+
+        @UiEvent(doc = "All apps vertical fling started.")
+        LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN(724),
+
+        @UiEvent(doc = "All apps vertical fling ended.")
+        LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END(725),
+
+        @UiEvent(doc = "Show URL indicator for Overview Sharing")
+        LAUNCHER_OVERVIEW_SHARING_SHOW_URL_INDICATOR(764),
+
+        @UiEvent(doc = "Show image indicator for Overview Sharing")
+        LAUNCHER_OVERVIEW_SHARING_SHOW_IMAGE_INDICATOR(765),
+
+        @UiEvent(doc = "User taps URL indicator in Overview")
+        LAUNCHER_OVERVIEW_SHARING_URL_INDICATOR_TAP(766),
+
+        @UiEvent(doc = "User taps image indicator in Overview")
+        LAUNCHER_OVERVIEW_SHARING_IMAGE_INDICATOR_TAP(767),
+
+        @UiEvent(doc = "User long presses an image in Overview")
+        LAUNCHER_OVERVIEW_SHARING_IMAGE_LONG_PRESS(768),
+
+        @UiEvent(doc = "User drags a URL in Overview")
+        LAUNCHER_OVERVIEW_SHARING_URL_DRAG(769),
+
+        @UiEvent(doc = "User drags an image in Overview")
+        LAUNCHER_OVERVIEW_SHARING_IMAGE_DRAG(770),
+
+        @UiEvent(doc = "User drops URL to a direct share target")
+        LAUNCHER_OVERVIEW_SHARING_DROP_URL_TO_TARGET(771),
+
+        @UiEvent(doc = "User drops an image to a direct share target")
+        LAUNCHER_OVERVIEW_SHARING_DROP_IMAGE_TO_TARGET(772),
+
+        @UiEvent(doc = "User drops URL to the More button")
+        LAUNCHER_OVERVIEW_SHARING_DROP_URL_TO_MORE(773),
+
+        @UiEvent(doc = "User drops an image to the More button")
+        LAUNCHER_OVERVIEW_SHARING_DROP_IMAGE_TO_MORE(774),
+
+        @UiEvent(doc = "User taps a share target to share URL")
+        LAUNCHER_OVERVIEW_SHARING_TAP_TARGET_TO_SHARE_URL(775),
+
+        @UiEvent(doc = "User taps a share target to share an image")
+        LAUNCHER_OVERVIEW_SHARING_TAP_TARGET_TO_SHARE_IMAGE(776),
+
+        @UiEvent(doc = "User taps the More button to share URL")
+        LAUNCHER_OVERVIEW_SHARING_TAP_MORE_TO_SHARE_URL(777),
+
+        @UiEvent(doc = "User taps the More button to share an image")
+        LAUNCHER_OVERVIEW_SHARING_TAP_MORE_TO_SHARE_IMAGE(778),
+
+        @UiEvent(doc = "User started resizing a widget on their home screen.")
+        LAUNCHER_WIDGET_RESIZE_STARTED(820),
+
+        @UiEvent(doc = "User finished resizing a widget on their home screen.")
+        LAUNCHER_WIDGET_RESIZE_COMPLETED(824),
+
+        @UiEvent(doc = "User reconfigured a widget on their home screen.")
+        LAUNCHER_WIDGET_RECONFIGURED(821),
+
+        @UiEvent(doc = "User enabled themed icons option in wallpaper & style settings.")
+        LAUNCHER_THEMED_ICON_ENABLED(836),
+
+        @UiEvent(doc = "User disabled themed icons option in wallpaper & style settings.")
+        LAUNCHER_THEMED_ICON_DISABLED(837),
+
+        @UiEvent(doc = "User tapped on 'Turn on work apps' button in all apps window.")
+        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)
+        ;
+
         // ADD MORE
 
         private final int mId;
@@ -428,45 +594,63 @@
         }
 
         /**
+         * Sets logging fields from provided {@link SliceItem}.
+         */
+        default StatsLogger withSliceItem(SliceItem sliceItem) {
+            return this;
+        }
+
+        /**
+         * Sets logging fields from provided {@link LauncherAtom.Slice}.
+         */
+        default StatsLogger withSlice(LauncherAtom.Slice slice) {
+            return this;
+        }
+
+        /**
          * Builds the final message and logs it as {@link EventEnum}.
          */
         default void log(EventEnum event) {
         }
+
+        /**
+         * Builds the final message and logs it to two different atoms, one for
+         * event tracking and the other for jank tracking.
+         */
+        default void sendToInteractionJankMonitor(EventEnum event, View v) {
+        }
     }
 
     /**
      * Returns new logger object.
      */
     public StatsLogger logger() {
+        StatsLogger logger = createLogger();
+        if (mInstanceId != null) {
+            return logger.withInstanceId(mInstanceId);
+        }
+        return logger;
+    }
+
+    protected StatsLogger createLogger() {
         return new StatsLogger() {
         };
     }
 
     /**
+     * Sets InstanceId to every new {@link StatsLogger} object returned by {@link #logger()} when
+     * not-null.
+     */
+    public StatsLogManager withDefaultInstanceId(@Nullable InstanceId instanceId) {
+        this.mInstanceId = instanceId;
+        return this;
+    }
+
+    /**
      * Creates a new instance of {@link StatsLogManager} based on provided context.
      */
     public static StatsLogManager newInstance(Context context) {
-        StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
+        return Overrides.getObject(StatsLogManager.class,
                 context.getApplicationContext(), R.string.stats_log_manager_class);
-        return mgr;
-    }
-
-    /**
-     * Log an event with ranked-choice information along with package. Does nothing if event.getId()
-     * <= 0.
-     *
-     * @param rankingEvent an enum implementing EventEnum interface.
-     * @param instanceId An identifier obtained from an InstanceIdSequence.
-     * @param packageName the package name of the relevant app, if known (null otherwise).
-     * @param position the position picked.
-     */
-    public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
-            int position) {
-    }
-
-    /**
-     * Logs impression of the current workspace with additional launcher events.
-     */
-    public void logSnapshot(List<EventEnum> additionalEvents) {
     }
 }
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
deleted file mode 100644
index a5cc7ea..0000000
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.android.launcher3.logging;
-
-import android.view.View;
-import android.view.ViewParent;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-
-import java.util.ArrayList;
-
-public class StatsLogUtils {
-    private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
-
-    /**
-     * Implemented by containers to provide a container source for a given child.
-     */
-    public interface LogContainerProvider {
-
-        /**
-         * Populates parent container targets for an item
-         */
-        void fillInLogContainerData(ItemInfo childInfo, Target child, ArrayList<Target> parents);
-    }
-
-    /**
-     * Recursively finds the parent of the given child which implements IconLogInfoProvider
-     */
-    public static LogContainerProvider getLaunchProviderRecursive(@Nullable View v) {
-        ViewParent parent;
-        if (v != null) {
-            parent = v.getParent();
-        } else {
-            return null;
-        }
-
-        // Optimization to only check up to 5 parents.
-        int count = MAXIMUM_VIEW_HIERARCHY_LEVEL;
-        while (parent != null && count-- > 0) {
-            if (parent instanceof LogContainerProvider) {
-                return (LogContainerProvider) parent;
-            } else {
-                parent = parent.getParent();
-            }
-        }
-        return null;
-    }
-}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
deleted file mode 100644
index e094cab..0000000
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.logging;
-
-import static com.android.launcher3.logging.LoggerUtils.newAction;
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newControlTarget;
-import static com.android.launcher3.logging.LoggerUtils.newDropTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-import static com.android.launcher3.logging.LoggerUtils.newTarget;
-import static com.android.launcher3.logging.LoggerUtils.newTouchAction;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
-
-import static java.util.Optional.ofNullable;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Process;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.DropTarget;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.InstantAppResolver;
-import com.android.launcher3.util.LogConfig;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-import com.google.protobuf.nano.MessageNano;
-
-import java.util.ArrayList;
-import java.util.UUID;
-
-/**
- * Manages the creation of {@link LauncherEvent}.
- * To debug this class, execute following command before side loading a new apk.
- * <p>
- * $ adb shell setprop log.tag.UserEvent VERBOSE
- */
-public class UserEventDispatcher implements ResourceBasedOverride {
-
-    private static final String TAG = "UserEvent";
-    private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
-    private static final String UUID_STORAGE = "uuid";
-
-    /**
-     * A factory method for UserEventDispatcher
-     */
-    public static UserEventDispatcher newInstance(Context context) {
-        SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context);
-        String uuidStr = sharedPrefs.getString(UUID_STORAGE, null);
-        if (uuidStr == null) {
-            uuidStr = UUID.randomUUID().toString();
-            sharedPrefs.edit().putString(UUID_STORAGE, uuidStr).apply();
-        }
-        UserEventDispatcher ued = Overrides.getObject(UserEventDispatcher.class,
-                context.getApplicationContext(), R.string.user_event_dispatcher_class);
-        ued.mUuidStr = uuidStr;
-        ued.mInstantAppResolver = InstantAppResolver.newInstance(context);
-        return ued;
-    }
-
-
-    /**
-     * Fills in the container data on the given event if the given view is not null.
-     *
-     * @return whether container data was added.
-     */
-    public boolean fillLogContainer(@Nullable View v, Target child,
-            @Nullable ArrayList<Target> targets) {
-        LogContainerProvider firstParent = StatsLogUtils.getLaunchProviderRecursive(v);
-        if (v == null || !(v.getTag() instanceof ItemInfo) || firstParent == null) {
-            return false;
-        }
-        final ItemInfo itemInfo = (ItemInfo) v.getTag();
-        firstParent.fillInLogContainerData(itemInfo, child, targets);
-        return true;
-    }
-
-    protected void onFillInLogContainerData(@NonNull ItemInfo itemInfo, @NonNull Target target,
-            @NonNull ArrayList<Target> targets) {
-    }
-
-    private boolean mSessionStarted;
-    private long mElapsedContainerMillis;
-    private long mElapsedSessionMillis;
-    private long mActionDurationMillis;
-    private String mUuidStr;
-    protected InstantAppResolver mInstantAppResolver;
-    private boolean mAppOrTaskLaunch;
-    private boolean mPreviousHomeGesture;
-
-    //                      APP_ICON    SHORTCUT    WIDGET
-    // --------------------------------------------------------------
-    // packageNameHash      required    optional    required
-    // componentNameHash    required                required
-    // intentHash                       required
-    // --------------------------------------------------------------
-
-    @Deprecated
-    public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) {
-        Target itemTarget = newItemTarget(v, mInstantAppResolver);
-        Action action = newTouchAction(Action.Touch.TAP);
-        ArrayList<Target> targets = makeTargetsList(itemTarget);
-        if (fillLogContainer(v, itemTarget, targets)) {
-            onFillInLogContainerData((ItemInfo) v.getTag(), itemTarget, targets);
-            fillIntentInfo(itemTarget, intent, userHandle);
-        }
-        LauncherEvent event = newLauncherEvent(action,  targets);
-        dispatchUserEvent(event, intent);
-        mAppOrTaskLaunch = true;
-    }
-
-    /**
-     * Dummy method.
-     */
-    public void logActionTip(int actionType, int viewType) {
-    }
-
-    @Deprecated
-    public void logTaskLaunchOrDismiss(int action, int direction, int taskIndex,
-            ComponentKey componentKey) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(action), // TAP or SWIPE or FLING
-                newTarget(Target.Type.ITEM));
-        if (action == Action.Touch.SWIPE || action == Action.Touch.FLING) {
-            // Direction DOWN means the task was launched, UP means it was dismissed.
-            event.action.dir = direction;
-        }
-        event.srcTarget[0].itemType = ItemType.TASK;
-        event.srcTarget[0].pageIndex = taskIndex;
-        fillComponentInfo(event.srcTarget[0], componentKey.componentName);
-        dispatchUserEvent(event, null);
-        mAppOrTaskLaunch = true;
-    }
-
-    protected void fillIntentInfo(Target target, Intent intent, @Nullable UserHandle userHandle) {
-        target.intentHash = intent.hashCode();
-        target.isWorkApp = userHandle != null && !userHandle.equals(Process.myUserHandle());
-        fillComponentInfo(target, intent.getComponent());
-    }
-
-    private void fillComponentInfo(Target target, ComponentName cn) {
-        if (cn != null) {
-            target.packageNameHash = (mUuidStr + cn.getPackageName()).hashCode();
-            target.componentHash = (mUuidStr + cn.flattenToString()).hashCode();
-        }
-    }
-
-    public void logNotificationLaunch(View v, PendingIntent intent) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
-                newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
-        Target itemTarget = newItemTarget(v, mInstantAppResolver);
-        ArrayList<Target> targets = makeTargetsList(itemTarget);
-
-        if (fillLogContainer(v, itemTarget, targets)) {
-            itemTarget.packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode();
-        }
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionCommand(int command, Target srcTarget) {
-        logActionCommand(command, srcTarget, null);
-    }
-
-    public void logActionCommand(int command, int srcContainerType, int dstContainerType) {
-        logActionCommand(command, newContainerTarget(srcContainerType),
-                dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null);
-    }
-
-    public void logActionCommand(int command, int srcContainerType, int dstContainerType,
-            int pageIndex) {
-        Target srcTarget = newContainerTarget(srcContainerType);
-        srcTarget.pageIndex = pageIndex;
-        logActionCommand(command, srcTarget,
-                dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null);
-    }
-
-    public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
-        LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
-        if (command == Action.Command.STOP) {
-            if (mAppOrTaskLaunch || !mSessionStarted) {
-                mSessionStarted = false;
-                return;
-            }
-        }
-
-        if (dstTarget != null) {
-            event.destTarget = new Target[1];
-            event.destTarget[0] = dstTarget;
-            event.action.isStateChange = true;
-        }
-        dispatchUserEvent(event, null);
-    }
-
-    /**
-     * TODO: Make this function work when a container view is passed as the 2nd param.
-     */
-    public void logActionCommand(int command, View itemView, int srcContainerType) {
-        LauncherEvent event = newLauncherEvent(newCommandAction(command),
-                newItemTarget(itemView, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
-
-        Target itemTarget = newItemTarget(itemView, mInstantAppResolver);
-        ArrayList<Target> targets = makeTargetsList(itemTarget);
-
-        if (fillLogContainer(itemView, itemTarget, targets)) {
-            // TODO: Remove the following two lines once fillInLogContainerData can take in a
-            // container view.
-            itemTarget.type = Target.Type.CONTAINER;
-            itemTarget.containerType = srcContainerType;
-        }
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionOnControl(int action, int controlType) {
-        logActionOnControl(action, controlType, null);
-    }
-
-    public void logActionOnControl(int action, int controlType, int parentContainerType) {
-        logActionOnControl(action, controlType, null, parentContainerType);
-    }
-
-    /**
-     * Logs control action with proper parent hierarchy
-     */
-    public void logActionOnControl(int actionType, int controlType,
-            @Nullable View controlInContainer, int... parentTypes) {
-        Target control = newTarget(Target.Type.CONTROL);
-        control.controlType = controlType;
-        Action action = newAction(actionType);
-
-        ArrayList<Target> targets = makeTargetsList(control);
-        if (controlInContainer != null) {
-            fillLogContainer(controlInContainer, control, targets);
-        }
-        for (int parentContainerType : parentTypes) {
-            if (parentContainerType < 0) continue;
-            targets.add(newContainerTarget(parentContainerType));
-        }
-        LauncherEvent event = newLauncherEvent(action, targets);
-        if (actionType == Action.Touch.DRAGDROP) {
-            event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
-        }
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionTapOutside(Target target) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Type.TOUCH),
-                target);
-        event.action.isOutside = true;
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionBounceTip(int containerType) {
-        LauncherEvent event = newLauncherEvent(newAction(Action.Type.TIP),
-                newContainerTarget(containerType));
-        event.srcTarget[0].tipType = TipType.BOUNCE;
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionOnContainer(int action, int dir, int containerType) {
-        logActionOnContainer(action, dir, containerType, 0);
-    }
-
-    public void logActionOnContainer(int action, int dir, int containerType, int pageIndex) {
-        LauncherEvent event = newLauncherEvent(newTouchAction(action),
-                newContainerTarget(containerType));
-        event.action.dir = dir;
-        event.srcTarget[0].pageIndex = pageIndex;
-        dispatchUserEvent(event, null);
-    }
-
-    /**
-     * Used primarily for swipe up and down when state changes when swipe up happens from the
-     * navbar bezel, the {@param srcChildContainerType} is NAVBAR and
-     * {@param srcParentContainerType} is either one of the two
-     * (1) WORKSPACE: if the launcher is the foreground activity
-     * (2) APP: if another app was the foreground activity
-     */
-    public void logStateChangeAction(int action, int dir, int downX, int downY,
-            int srcChildTargetType, int srcParentContainerType, int dstContainerType,
-            int pageIndex) {
-        LauncherEvent event;
-        if (srcChildTargetType == ItemType.TASK) {
-            event = newLauncherEvent(newTouchAction(action),
-                    newItemTarget(srcChildTargetType),
-                    newContainerTarget(srcParentContainerType));
-        } else {
-            event = newLauncherEvent(newTouchAction(action),
-                    newContainerTarget(srcChildTargetType),
-                    newContainerTarget(srcParentContainerType));
-        }
-        event.destTarget = new Target[1];
-        event.destTarget[0] = newContainerTarget(dstContainerType);
-        event.action.dir = dir;
-        event.action.isStateChange = true;
-        event.srcTarget[0].pageIndex = pageIndex;
-        event.srcTarget[0].spanX = downX;
-        event.srcTarget[0].spanY = downY;
-        dispatchUserEvent(event, null);
-        resetElapsedContainerMillis("state changed");
-    }
-
-    public void logActionOnItem(int action, int dir, int itemType) {
-        logActionOnItem(action, dir, itemType, null, null);
-    }
-
-    /**
-     * Creates new {@link LauncherEvent} of ITEM target type with input arguments and dispatches it.
-     *
-     * @param touchAction ENUM value of {@link LauncherLogProto.Action.Touch} Action
-     * @param dir         ENUM value of {@link LauncherLogProto.Action.Direction} Action
-     * @param itemType    ENUM value of {@link LauncherLogProto.ItemType}
-     * @param gridX       Nullable X coordinate of item's position on the workspace grid
-     * @param gridY       Nullable Y coordinate of item's position on the workspace grid
-     */
-    public void logActionOnItem(int touchAction, int dir, int itemType,
-            @Nullable Integer gridX, @Nullable Integer gridY) {
-        Target itemTarget = newTarget(Target.Type.ITEM);
-        itemTarget.itemType = itemType;
-        ofNullable(gridX).ifPresent(value -> itemTarget.gridX = value);
-        ofNullable(gridY).ifPresent(value -> itemTarget.gridY = value);
-        LauncherEvent event = newLauncherEvent(newTouchAction(touchAction), itemTarget);
-        event.action.dir = dir;
-        dispatchUserEvent(event, null);
-    }
-
-    /**
-     * Logs proto lite version of LauncherEvent object to clearcut.
-     */
-    public void logLauncherEvent(
-            com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
-
-        if (mPreviousHomeGesture) {
-            mPreviousHomeGesture = false;
-        }
-        mAppOrTaskLaunch = false;
-        launcherEvent.toBuilder()
-                .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
-                .setElapsedSessionMillis(
-                        SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
-        try {
-            dispatchUserEvent(LauncherEvent.parseFrom(launcherEvent.toByteArray()), null);
-        } catch (InvalidProtocolBufferNanoException e) {
-            throw new RuntimeException("Cannot convert LauncherEvent from Lite to Nano version.");
-        }
-    }
-
-    public void logDeepShortcutsOpen(View icon) {
-        ItemInfo info = (ItemInfo) icon.getTag();
-        Target child = newItemTarget(info, mInstantAppResolver);
-        ArrayList<Target> targets = makeTargetsList(child);
-        fillLogContainer(icon, child, targets);
-        dispatchUserEvent(newLauncherEvent(newTouchAction(Action.Touch.TAP), targets), null);
-        resetElapsedContainerMillis("deep shortcut open");
-    }
-
-    public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
-        Target srcChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
-        ArrayList<Target> srcTargets = makeTargetsList(srcChild);
-
-
-        Target destChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
-        ArrayList<Target> destTargets = makeTargetsList(destChild);
-
-        dragObj.dragSource.fillInLogContainerData(dragObj.originalDragInfo, srcChild, srcTargets);
-        if (dropTargetAsView instanceof LogContainerProvider) {
-            ((LogContainerProvider) dropTargetAsView).fillInLogContainerData(dragObj.dragInfo,
-                    destChild, destTargets);
-        }
-        else {
-            destTargets.add(newDropTarget(dropTargetAsView));
-        }
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP), srcTargets);
-        Target[] destTargetsArray = new Target[destTargets.size()];
-        destTargets.toArray(destTargetsArray);
-        event.destTarget = destTargetsArray;
-
-        event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
-        dispatchUserEvent(event, null);
-    }
-
-    public void logActionBack(boolean completed, int downX, int downY, boolean isButton,
-            boolean gestureSwipeLeft, int containerType) {
-        int actionTouch = isButton ? Action.Touch.TAP : Action.Touch.SWIPE;
-        Action action = newCommandAction(actionTouch);
-        action.command = Action.Command.BACK;
-        action.dir = isButton ? Action.Direction.NONE :
-                gestureSwipeLeft ? Action.Direction.LEFT : Action.Direction.RIGHT;
-        Target target = newControlTarget(isButton ? ControlType.BACK_BUTTON :
-                ControlType.BACK_GESTURE);
-        target.spanX = downX;
-        target.spanY = downY;
-        target.cardinality = completed ? 1 : 0;
-        LauncherEvent event = newLauncherEvent(action, target, newContainerTarget(containerType));
-
-        dispatchUserEvent(event, null);
-    }
-
-    /**
-     * Currently logs following containers: workspace, allapps, widget tray.
-     */
-    public final void resetElapsedContainerMillis(String reason) {
-        mElapsedContainerMillis = SystemClock.uptimeMillis();
-        if (!IS_VERBOSE) {
-            return;
-        }
-        Log.d(TAG, "resetElapsedContainerMillis reason=" + reason);
-
-    }
-
-    public final void startSession() {
-        mSessionStarted = true;
-        mElapsedSessionMillis = SystemClock.uptimeMillis();
-        mElapsedContainerMillis = SystemClock.uptimeMillis();
-    }
-
-    public final void setPreviousHomeGesture(boolean homeGesture) {
-        mPreviousHomeGesture = homeGesture;
-    }
-
-    public final boolean isPreviousHomeGesture() {
-        return mPreviousHomeGesture;
-    }
-
-    public final void resetActionDurationMillis() {
-        mActionDurationMillis = SystemClock.uptimeMillis();
-    }
-
-    public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
-        if (mPreviousHomeGesture) {
-            mPreviousHomeGesture = false;
-        }
-        mAppOrTaskLaunch = false;
-        ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
-        ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
-        if (!IS_VERBOSE) {
-            return;
-        }
-        LauncherLogProto.LauncherEvent liteLauncherEvent;
-        try {
-            liteLauncherEvent =
-                    LauncherLogProto.LauncherEvent.parseFrom(MessageNano.toByteArray(ev));
-        } catch (InvalidProtocolBufferException e) {
-            throw new RuntimeException("Cannot parse LauncherEvent from Nano to Lite version");
-        }
-        Log.d(TAG, liteLauncherEvent.toString());
-    }
-
-    /**
-     * Constructs an ArrayList with targets
-     */
-    public static ArrayList<Target> makeTargetsList(Target... targets) {
-        ArrayList<Target> result = new ArrayList<>();
-        for (Target target : targets) {
-            result.add(target);
-        }
-        return result;
-    }
-}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index c236fa6..01b3e6e 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -27,6 +27,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
@@ -34,6 +35,7 @@
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -46,6 +48,8 @@
  */
 public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
 
+    private static final String LOG = "AddWorkspaceItemsTask";
+
     private final List<Pair<ItemInfo, Object>> mItemList;
 
     /**
@@ -122,6 +126,13 @@
                     }
                     SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
                             packageName);
+
+                    if (!packageInstaller.verifySessionInfo(sessionInfo)) {
+                        FileLog.d(LOG, "Item info failed session info verification. "
+                                + "Skipping : " + workspaceInfo);
+                        continue;
+                    }
+
                     List<LauncherActivityInfo> activities = launcherApps
                             .getActivityList(packageName, item.user);
                     boolean hasActivity = activities != null && !activities.isEmpty();
@@ -132,7 +143,9 @@
                             continue;
                         }
                     } else {
-                        workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
+                        workspaceInfo.setProgressLevel(
+                                (int) (sessionInfo.getProgress() * 100),
+                                PackageInstallInfo.STATUS_INSTALLING);
                     }
 
                     if (hasActivity) {
@@ -164,6 +177,9 @@
 
                 // Save the WorkspaceItemInfo for binding in the workspace
                 addedItemsFinal.add(itemInfo);
+
+                // log bitmap and label
+                FileLog.d(LOG, "Adding item info to workspace: " + itemInfo);
             }
         }
 
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index eb5d106..92b5885 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -21,11 +21,10 @@
 
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
+import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.os.LocaleList;
-import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
 
@@ -37,7 +36,6 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -138,7 +136,7 @@
         if (findAppInfo(info.componentName, info.user) != null) {
             return;
         }
-        mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
+        mIconCache.getTitleAndIcon(info, activityInfo, false /* useLowResIcon */);
         info.sectionName = mIndex.computeSectionName(info.title);
 
         data.add(info);
@@ -146,11 +144,10 @@
     }
 
     public void addPromiseApp(Context context, PackageInstallInfo installInfo) {
-        ApplicationInfo applicationInfo = new PackageManagerHelper(context)
-                .getApplicationInfo(installInfo.packageName, installInfo.user, 0);
         // only if not yet installed
-        if (applicationInfo == null) {
-            PromiseAppInfo info = new PromiseAppInfo(installInfo);
+        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);
 
@@ -159,24 +156,31 @@
         }
     }
 
-    public PromiseAppInfo updatePromiseInstallInfo(PackageInstallInfo installInfo) {
-        UserHandle user = Process.myUserHandle();
-        for (int i=0; i < data.size(); i++) {
+    /** Updates the given PackageInstallInfo's associated AppInfo's installation info. */
+    public List<AppInfo> updatePromiseInstallInfo(PackageInstallInfo installInfo) {
+        List<AppInfo> updatedAppInfos = new ArrayList<>();
+        UserHandle user = installInfo.user;
+        for (int i = data.size() - 1; i >= 0; i--) {
             final AppInfo appInfo = data.get(i);
             final ComponentName tgtComp = appInfo.getTargetComponent();
             if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName)
-                    && appInfo.user.equals(user)
-                    && appInfo instanceof PromiseAppInfo) {
-                final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
-                if (installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
-                    promiseAppInfo.level = installInfo.progress;
-                    return promiseAppInfo;
-                } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
+                    && appInfo.user.equals(user)) {
+                if (installInfo.state == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING
+                        || installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
+                    if (appInfo.isAppStartable()
+                            && installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
+                        continue;
+                    }
+                    appInfo.setProgressLevel(installInfo);
+
+                    updatedAppInfos.add(appInfo);
+                } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED
+                        && !appInfo.isAppStartable()) {
                     removeApp(i);
                 }
             }
         }
-        return null;
+        return updatedAppInfos;
     }
 
     private void removeApp(int index) {
@@ -197,11 +201,16 @@
     /**
      * Add the icons for the supplied apk called packageName.
      */
-    public void addPackage(Context context, String packageName, UserHandle user) {
-        for (LauncherActivityInfo info : context.getSystemService(LauncherApps.class)
-                .getActivityList(packageName, user)) {
+    public List<LauncherActivityInfo> addPackage(
+            Context context, String packageName, UserHandle user) {
+        List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
+                .getActivityList(packageName, user);
+
+        for (LauncherActivityInfo info : activities) {
             add(new AppInfo(context, info, user), info);
         }
+
+        return activities;
     }
 
     /**
@@ -244,7 +253,8 @@
     /**
      * Add and remove icons for this package which has been updated.
      */
-    public void updatePackage(Context context, String packageName, UserHandle user) {
+    public List<LauncherActivityInfo> updatePackage(
+            Context context, String packageName, UserHandle user) {
         final List<LauncherActivityInfo> matches = context.getSystemService(LauncherApps.class)
                 .getActivityList(packageName, user);
         if (matches.size() > 0) {
@@ -268,8 +278,14 @@
                 if (applicationInfo == null) {
                     add(new AppInfo(context, info, user), info);
                 } else {
-                    mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
+                    Intent launchIntent = AppInfo.makeLaunchIntent(info);
+
+                    mIconCache.getTitleAndIcon(applicationInfo, info, false /* useLowResIcon */);
                     applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title);
+                    applicationInfo.setProgressLevel(
+                            PackageManagerHelper.getLoadingProgress(info),
+                            PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+                    applicationInfo.intent = launchIntent;
 
                     mDataChanged = true;
                 }
@@ -285,6 +301,8 @@
                 }
             }
         }
+
+        return matches;
     }
 
     /**
@@ -305,7 +323,7 @@
      *
      * @return the corresponding AppInfo or null
      */
-    private @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName,
+    public @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName,
                                           @NonNull UserHandle user) {
         for (AppInfo info: data) {
             if (componentName.equals(info.componentName) && user.equals(info.user)) {
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
deleted file mode 100644
index 906c2e0..0000000
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ /dev/null
@@ -1,60 +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.model;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import android.content.ComponentName;
-import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/**
- * Callback for receiving various app launch events
- */
-public class AppLaunchTracker implements ResourceBasedOverride {
-
-    /**
-     * Derived from LauncherEvent proto.
-     * TODO: Use proper descriptive constants
-     */
-    public static final String CONTAINER_DEFAULT = Integer.toString(ContainerType.WORKSPACE);
-    public static final String CONTAINER_ALL_APPS = Integer.toString(ContainerType.ALLAPPS);
-    public static final String CONTAINER_PREDICTIONS = Integer.toString(ContainerType.PREDICTION);
-    public static final String CONTAINER_SEARCH = Integer.toString(ContainerType.SEARCHRESULT);
-    public static final String CONTAINER_OVERVIEW = Integer.toString(ContainerType.OVERVIEW);
-    public static final String CONTAINER_HOTSEAT = Integer.toString(ContainerType.HOTSEAT);
-
-
-    public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
-            forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class);
-
-    public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
-            @Nullable String container) { }
-
-    public void onStartApp(ComponentName componentName, UserHandle user,
-            @Nullable String container) { }
-
-    public void onDismissApp(ComponentName componentName, UserHandle user,
-             @Nullable String container){}
-
-    public void onReturnedToHome() { }
-}
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 8b0ef7b..5c85bab 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
-import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
 import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
 
 import android.util.Log;
@@ -27,6 +26,7 @@
 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;
@@ -76,18 +76,20 @@
         ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
         ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
         final IntArray orderedScreenIds = new IntArray();
+        ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
 
         synchronized (mBgDataModel) {
             workspaceItems.addAll(mBgDataModel.workspaceItems);
             appWidgets.addAll(mBgDataModel.appWidgets);
             orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
+            mBgDataModel.extraItems.forEach(extraItems::add);
             mBgDataModel.lastBindId++;
             mMyBindingId = mBgDataModel.lastBindId;
         }
 
         for (Callbacks cb : mCallbacksList) {
             new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
-                    workspaceItems, appWidgets, orderedScreenIds).bind();
+                    workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
         }
     }
 
@@ -135,7 +137,7 @@
         private final ArrayList<ItemInfo> mWorkspaceItems;
         private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
         private final IntArray mOrderedScreenIds;
-
+        private final ArrayList<FixedContainerItems> mExtraItems;
 
         WorkspaceBinder(Callbacks callbacks,
                 Executor uiExecutor,
@@ -144,6 +146,7 @@
                 int myBindingId,
                 ArrayList<ItemInfo> workspaceItems,
                 ArrayList<LauncherAppWidgetInfo> appWidgets,
+                ArrayList<FixedContainerItems> extraItems,
                 IntArray orderedScreenIds) {
             mCallbacks = callbacks;
             mUiExecutor = uiExecutor;
@@ -152,6 +155,7 @@
             mMyBindingId = myBindingId;
             mWorkspaceItems = workspaceItems;
             mAppWidgets = appWidgets;
+            mExtraItems = extraItems;
             mOrderedScreenIds = orderedScreenIds;
         }
 
@@ -198,15 +202,15 @@
             // Load items on the current page.
             bindWorkspaceItems(currentWorkspaceItems, mainExecutor);
             bindAppWidgets(currentAppWidgets, mainExecutor);
+            mExtraItems.forEach(item ->
+                    executeCallbacksTask(c -> c.bindExtraContainerItems(item), mainExecutor));
 
-            // Locate available spots for prediction using currentWorkspaceItems
-            IntArray gaps = getMissingHotseatRanks(currentWorkspaceItems, idp.numHotseatIcons);
-            bindPredictedItems(gaps, mainExecutor);
             // 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.
+
             final Executor deferredExecutor =
                     validFirstPage ? new ViewOnDrawExecutor() : mainExecutor;
 
@@ -253,11 +257,6 @@
             }
         }
 
-        private void bindPredictedItems(IntArray ranks, final Executor executor) {
-            ArrayList<AppInfo> items = new ArrayList<>(mBgDataModel.cachedPredictedItems);
-            executeCallbacksTask(c -> c.bindPredictedItems(items, ranks), executor);
-        }
-
         protected void executeCallbacksTask(CallbackTask task, Executor executor) {
             executor.execute(() -> {
                 if (mMyBindingId != mBgDataModel.lastBindId) {
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 9013cba..ad553d5 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -22,15 +22,20 @@
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
 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.WorkspaceItemInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
 
 /**
  * Extension of {@link ModelUpdateTask} with some utility methods
@@ -88,11 +93,27 @@
         return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */);
     }
 
-
-    public void bindUpdatedWorkspaceItems(final ArrayList<WorkspaceItemInfo> updatedShortcuts) {
-        if (!updatedShortcuts.isEmpty()) {
-            scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(updatedShortcuts));
+    public void bindUpdatedWorkspaceItems(List<WorkspaceItemInfo> allUpdates) {
+        // Bind workspace items
+        List<WorkspaceItemInfo> workspaceUpdates = allUpdates.stream()
+                .filter(info -> info.id != ItemInfo.NO_ID)
+                .collect(Collectors.toList());
+        if (!workspaceUpdates.isEmpty()) {
+            scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(workspaceUpdates));
         }
+
+        // Bind extra items if any
+        allUpdates.stream()
+                .mapToInt(info -> info.container)
+                .distinct()
+                .mapToObj(mDataModel.extraItems::get)
+                .filter(Objects::nonNull)
+                .forEach(this::bindExtraContainerItems);
+    }
+
+    public void bindExtraContainerItems(FixedContainerItems item) {
+        FixedContainerItems copy = item.clone();
+        scheduleCallbackTask(c -> c.bindExtraContainerItems(copy));
     }
 
     public void bindDeepShortcuts(BgDataModel dataModel) {
@@ -102,8 +123,8 @@
     }
 
     public void bindUpdatedWidgets(BgDataModel dataModel) {
-        final ArrayList<WidgetListRowEntry> widgets =
-                dataModel.widgetsModel.getWidgetsList(mApp.getContext());
+        final ArrayList<WidgetsListBaseEntry> widgets =
+                dataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
         scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
     }
 
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 9bef847..1d7d1a2 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -15,48 +15,57 @@
  */
 package com.android.launcher3.model;
 
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY;
+
 import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
 import static com.android.launcher3.shortcuts.ShortcutRequest.PINNED;
 
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
+
 import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
-import android.util.MutableInt;
 
-import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 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.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
 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.IntArray;
 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.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.function.BiConsumer;
+import java.util.Set;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * All the data stored in-memory and managed by the LauncherModel
@@ -88,21 +97,9 @@
     public final IntSparseArrayMap<FolderInfo> folders = new IntSparseArrayMap<>();
 
     /**
-     * Map of ShortcutKey to the number of times it is pinned.
+     * Extra container based items
      */
-    public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
-
-    /**
-     * List of all cached predicted items visible on home screen
-     */
-    public final ArrayList<AppInfo> cachedPredictedItems = new ArrayList<>();
-
-    /**
-     * @see Callbacks#FLAG_HAS_SHORTCUT_PERMISSION
-     * @see Callbacks#FLAG_QUIET_MODE_ENABLED
-     * @see Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION
-     */
-    public int flags;
+    public final IntSparseArrayMap<FixedContainerItems> extraItems = new IntSparseArrayMap<>();
 
     /**
      * Maps all launcher activities to counts of their shortcuts.
@@ -127,8 +124,8 @@
         appWidgets.clear();
         folders.clear();
         itemsIdMap.clear();
-        pinnedShortcutCounts.clear();
         deepShortcutMap.clear();
+        extraItems.clear();
     }
 
     /**
@@ -181,6 +178,7 @@
     }
 
     public synchronized void removeItem(Context context, Iterable<? extends ItemInfo> items) {
+        ArraySet<UserHandle> updatedDeepShortcuts = new ArraySet<>();
         for (ItemInfo item : items) {
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -199,14 +197,7 @@
                     workspaceItems.remove(item);
                     break;
                 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
-                    // Decrement pinned shortcut count
-                    ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
-                    MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
-                    if ((count == null || --count.value == 0)
-                            && !InstallShortcutReceiver.getPendingShortcuts(context)
-                                .contains(pinnedShortcut)) {
-                        unpinShortcut(context, pinnedShortcut);
-                    }
+                    updatedDeepShortcuts.add(item.user);
                     // Fall through.
                 }
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -220,6 +211,7 @@
             }
             itemsIdMap.remove(item.id);
         }
+        updatedDeepShortcuts.forEach(user -> updateShortcutPinnedState(context, user));
     }
 
     public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
@@ -229,23 +221,7 @@
                 folders.put(item.id, (FolderInfo) item);
                 workspaceItems.add(item);
                 break;
-            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
-                // Increment the count for the given shortcut
-                ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
-                MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
-                if (count == null) {
-                    count = new MutableInt(1);
-                    pinnedShortcutCounts.put(pinnedShortcut, count);
-                } else {
-                    count.value++;
-                }
-
-                // Since this is a new item, pin the shortcut in the system server.
-                if (newItem && count.value == 1) {
-                    updatePinnedShortcuts(context, pinnedShortcut, List::add);
-                }
-                // Fall through
-            }
+            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                 if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
@@ -270,36 +246,86 @@
                 appWidgets.add((LauncherAppWidgetInfo) item);
                 break;
         }
+        if (newItem && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+            updateShortcutPinnedState(context, item.user);
+        }
     }
 
     /**
-     * Removes the given shortcut from the current list of pinned shortcuts.
-     * (Runs on background thread)
+     * Updates the deep shortucts state in system to match out internal model, pinning any missing
+     * shortcuts and unpinning any extra shortcuts.
      */
-    public void unpinShortcut(Context context, ShortcutKey key) {
-        updatePinnedShortcuts(context, key, List::remove);
+    public void updateShortcutPinnedState(Context context) {
+        for (UserHandle user : UserCache.INSTANCE.get(context).getUserProfiles()) {
+            updateShortcutPinnedState(context, user);
+        }
     }
 
-    private void updatePinnedShortcuts(Context context, ShortcutKey key,
-            BiConsumer<List<String>, String> idOp) {
+    /**
+     * Updates the deep shortucts state in system to match out internal model, pinning any missing
+     * shortcuts and unpinning any extra shortcuts.
+     */
+    public synchronized void updateShortcutPinnedState(Context context, UserHandle user) {
         if (GO_DISABLE_WIDGETS) {
             return;
         }
-        String packageName = key.componentName.getPackageName();
-        String id = key.getId();
-        UserHandle user = key.user;
-        List<String> pinnedIds = new ShortcutRequest(context, user)
-                .forPackage(packageName)
-                .query(PINNED)
-                .stream()
-                .map(ShortcutInfo::getId)
-                .collect(Collectors.toCollection(ArrayList::new));
-        idOp.accept(pinnedIds, id);
-        try {
-            context.getSystemService(LauncherApps.class).pinShortcuts(packageName, pinnedIds, user);
-        } catch (SecurityException | IllegalStateException e) {
-            Log.w(TAG, "Failed to pin shortcut", e);
+
+        // Collect all system shortcuts
+        QueryResult result = new ShortcutRequest(context, user)
+                .query(PINNED | FLAG_GET_KEY_FIELDS_ONLY);
+        if (!result.wasSuccess()) {
+            return;
         }
+        // Map of packageName to shortcutIds that are currently in the system
+        Map<String, Set<String>> systemMap = result.stream()
+                .collect(groupingBy(ShortcutInfo::getPackage,
+                        mapping(ShortcutInfo::getId, Collectors.toSet())));
+
+        // Collect all model shortcuts
+        Stream.Builder<WorkspaceItemInfo> itemStream = Stream.builder();
+        forAllWorkspaceItemInfos(user, itemStream::accept);
+        // Map of packageName to shortcutIds that are currently in our model
+        Map<String, Set<String>> modelMap = Stream.concat(
+                    // Model shortcuts
+                    itemStream.build()
+                        .filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+                        .map(ShortcutKey::fromItemInfo),
+                    // Pending shortcuts
+                    ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts(user))
+                .collect(groupingBy(ShortcutKey::getPackageName,
+                        mapping(ShortcutKey::getId, Collectors.toSet())));
+
+        // Check for diff
+        for (Map.Entry<String, Set<String>> entry : modelMap.entrySet()) {
+            Set<String> modelShortcuts = entry.getValue();
+            Set<String> systemShortcuts = systemMap.remove(entry.getKey());
+            if (systemShortcuts == null) {
+                systemShortcuts = Collections.emptySet();
+            }
+
+            // Do not use .equals as it can vary based on the type of set
+            if (systemShortcuts.size() != modelShortcuts.size()
+                    || !systemShortcuts.containsAll(modelShortcuts)) {
+                // Update system state for this package
+                try {
+                    context.getSystemService(LauncherApps.class).pinShortcuts(
+                            entry.getKey(), new ArrayList<>(modelShortcuts), user);
+                } catch (SecurityException | IllegalStateException e) {
+                    Log.w(TAG, "Failed to pin shortcut", e);
+                }
+            }
+        }
+
+        // If there are any extra pinned shortcuts, remove them
+        systemMap.keySet().forEach(packageName -> {
+            // Update system state
+            try {
+                context.getSystemService(LauncherApps.class).pinShortcuts(
+                        packageName, Collections.emptyList(), user);
+            } catch (SecurityException | IllegalStateException e) {
+                Log.w(TAG, "Failed to unpin shortcut", e);
+            }
+        });
     }
 
     /**
@@ -348,6 +374,69 @@
         }
     }
 
+    /**
+     * Returns a list containing all workspace items including widgets.
+     */
+    public synchronized ArrayList<ItemInfo> getAllWorkspaceItems() {
+        ArrayList<ItemInfo> items = new ArrayList<>(workspaceItems.size() + appWidgets.size());
+        items.addAll(workspaceItems);
+        items.addAll(appWidgets);
+        return items;
+    }
+
+    /**
+     * Calls the provided {@code op} for all workspaceItems in the in-memory model (both persisted
+     * items and dynamic/predicted items for the provided {@code userHandle}.
+     * Note the call is not synchronized over the model, that should be handled by the called.
+     */
+    public void forAllWorkspaceItemInfos(UserHandle userHandle, Consumer<WorkspaceItemInfo> op) {
+        for (ItemInfo info : itemsIdMap) {
+            if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
+                op.accept((WorkspaceItemInfo) info);
+            }
+        }
+
+        for (int i = extraItems.size() - 1; i >= 0; i--) {
+            for (ItemInfo info : extraItems.valueAt(i).items) {
+                if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
+                    op.accept((WorkspaceItemInfo) info);
+                }
+            }
+        }
+    }
+
+    /**
+     * An object containing items corresponding to a fixed container
+     */
+    public static class FixedContainerItems {
+
+        public final int containerId;
+        public final List<ItemInfo> items;
+
+        public FixedContainerItems(int containerId) {
+            this(containerId, new ArrayList<>());
+        }
+
+        public FixedContainerItems(int containerId, List<ItemInfo> items) {
+            this.containerId = containerId;
+            this.items = items;
+        }
+
+        @Override
+        public FixedContainerItems clone() {
+            return new FixedContainerItems(containerId, new ArrayList<>(items));
+        }
+
+        public void setItems(List<ItemInfo> newItems) {
+            items.clear();
+            newItems.forEach(item -> {
+                item.container = containerId;
+                items.add(item);
+            });
+        }
+    }
+
+
     public interface Callbacks {
         // If the launcher has permission to access deep shortcuts.
         int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0;
@@ -369,21 +458,25 @@
         void preAddApps();
         void bindAppsAdded(IntArray newScreens,
                 ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
-        void bindPromiseAppProgressUpdated(PromiseAppInfo app);
-        void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
+
+        /**
+         * 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(ArrayList<WidgetListRowEntry> widgets);
+        void bindAllWidgets(List<WidgetsListBaseEntry> widgets);
         void onPageBoundSynchronously(int page);
         void executeOnNextDraw(ViewOnDrawExecutor executor);
         void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
 
-        void bindAllApplications(AppInfo[] apps, int flags);
-
         /**
-         * Binds predicted appInfos at at available prediction slots.
+         * Binds extra item provided any external source
          */
-        void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks);
+        default void bindExtraContainerItems(FixedContainerItems item) { }
+
+        void bindAllApplications(AppInfo[] apps, int flags);
     }
 }
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 8e6b064..f644d49 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -21,7 +21,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 import java.util.ArrayList;
@@ -48,23 +47,18 @@
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         IconCache iconCache = app.getIconCache();
-
-
         ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
 
         synchronized (dataModel) {
-            for (ItemInfo info : dataModel.itemsIdMap) {
-                if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
-                    WorkspaceItemInfo si = (WorkspaceItemInfo) info;
-                    ComponentName cn = si.getTargetComponent();
-                    if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                            && isValidShortcut(si) && cn != null
-                            && mPackages.contains(cn.getPackageName())) {
-                        iconCache.getTitleAndIcon(si, si.usingLowResIcon());
-                        updatedShortcuts.add(si);
-                    }
+            dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+                ComponentName cn = si.getTargetComponent();
+                if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                        && isValidShortcut(si) && cn != null
+                        && mPackages.contains(cn.getPackageName())) {
+                    iconCache.getTitleAndIcon(si, si.usingLowResIcon());
+                    updatedShortcuts.add(si);
                 }
-            }
+            });
             apps.updateIconsAndLabels(mPackages, mUser);
         }
         bindUpdatedWorkspaceItems(updatedShortcuts);
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index f9ed2c7..e391d37 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -19,25 +19,30 @@
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.os.Process.myUserHandle;
 
+import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
+
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
+
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInstaller.SessionInfo;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings;
 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.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Helper class to send broadcasts to package installers that have:
@@ -63,26 +68,10 @@
 
     private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
 
-    private final MultiHashMap<String, String> mPackagesForInstaller;
+    private final HashMap<PackageUserKey, SessionInfo> mSessionInfoForPackage;
 
     public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
-        mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
-    }
-
-    /**
-     * @return Map where the key is the package name of the installer, and the value is a list
-     *         of packages with active sessions for that installer.
-     */
-    private MultiHashMap<String, String> getPackagesForInstaller(
-            HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
-        MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
-        for (Map.Entry<PackageUserKey, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
-            if (myUserHandle().equals(entry.getKey().mUser)) {
-                packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
-                        entry.getKey().mPackageName);
-            }
-        }
-        return packagesForInstaller;
+        mSessionInfoForPackage = sessionInfoForPackage;
     }
 
     /**
@@ -90,9 +79,15 @@
      * first screen.
      */
     public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) {
-        for (Map.Entry<String, ArrayList<String>> entry : mPackagesForInstaller.entrySet()) {
-            sendBroadcastToInstaller(context, entry.getKey(), entry.getValue(), firstScreenItems);
-        }
+        UserHandle myUser = myUserHandle();
+        mSessionInfoForPackage
+                .values()
+                .stream()
+                .filter(info -> myUser.equals(getUserHandle(info)))
+                .collect(groupingBy(SessionInfo::getInstallerPackageName,
+                        mapping(SessionInfo::getAppPackageName, Collectors.toSet())))
+                .forEach((installer, packages) ->
+                    sendBroadcastToInstaller(context, installer, packages, firstScreenItems));
     }
 
     /**
@@ -101,7 +96,7 @@
      * @param firstScreenItems List of items on the first screen.
      */
     private void sendBroadcastToInstaller(Context context, String installerPackageName,
-            List<String> packages, List<ItemInfo> firstScreenItems) {
+            Set<String> packages, List<ItemInfo> firstScreenItems) {
         Set<String> folderItems = new HashSet<>();
         Set<String> workspaceItems = new HashSet<>();
         Set<String> hotseatItems = new HashSet<>();
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index e8a52bd..7b3e509 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -25,7 +25,6 @@
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.Settings;
@@ -40,6 +39,7 @@
 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;
@@ -53,7 +53,7 @@
 public class GridSizeMigrationTask {
 
     private static final String TAG = "GridSizeMigrationTask";
-    private static final boolean DEBUG = true;
+    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.
@@ -893,7 +893,7 @@
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
 
         return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
-                || idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
+                || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
     }
 
     /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
@@ -928,7 +928,7 @@
                 .getBinder(Settings.EXTRA_VALUE)) {
 
             int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
-                    idp.numHotseatIcons);
+                    idp.numDatabaseHotseatIcons);
             Point sourceSize = parsePoint(prefs.getString(
                     KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
 
@@ -948,10 +948,10 @@
 
             HashSet<String> validPackages = getValidPackages(context);
             // Hotseat.
-            if (srcHotseatCount != idp.numHotseatIcons
+            if (srcHotseatCount != idp.numDatabaseHotseatIcons
                     && new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
                             migrateForPreview, srcHotseatCount,
-                            idp.numHotseatIcons).migrateHotseat()) {
+                            idp.numDatabaseHotseatIcons).migrateHotseat()) {
                 dbChanged = true;
             }
 
@@ -991,7 +991,7 @@
                 // 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.numHotseatIcons)
+                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
                         .apply();
             }
         }
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 79467d3..8a1d73e 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -39,7 +39,6 @@
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.LauncherPreviewRenderer;
@@ -48,6 +47,7 @@
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
 import java.util.ArrayList;
@@ -67,7 +67,7 @@
 public class GridSizeMigrationTaskV2 {
 
     private static final String TAG = "GridSizeMigrationTaskV2";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private final Context mContext;
     private final SQLiteDatabase mDb;
@@ -110,7 +110,7 @@
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
 
         return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
-                || idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
+                || idp.numDatabaseHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1);
     }
 
     /** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
@@ -148,7 +148,8 @@
         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.numHotseatIcons);
+        int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
+                idp.numDatabaseHotseatIcons);
 
         if (migrateForPreview) {
             if (!LauncherSettings.Settings.call(
@@ -177,11 +178,11 @@
             DbReader destReader = new DbReader(t.getDb(),
                     migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
                             : LauncherSettings.Favorites.TABLE_NAME,
-                    context, validPackages, idp.numHotseatIcons);
+                    context, validPackages, idp.numDatabaseHotseatIcons);
 
             Point targetSize = new Point(idp.numColumns, idp.numRows);
             GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
-                    srcReader, destReader, idp.numHotseatIcons, targetSize);
+                    srcReader, destReader, idp.numDatabaseHotseatIcons, targetSize);
             task.migrate();
 
             if (!migrateForPreview) {
@@ -202,7 +203,7 @@
                 // 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.numHotseatIcons)
+                        .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numDatabaseHotseatIcons)
                         .apply();
             }
         }
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
new file mode 100644
index 0000000..217f523
--- /dev/null
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
+
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.logging.FileLog;
+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 com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.PersistedItemArray;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Class to maintain a queue of pending items to be added to the workspace.
+ */
+public class ItemInstallQueue {
+
+    private static final String LOG = "ItemInstallQueue";
+
+    public static final int FLAG_ACTIVITY_PAUSED = 1;
+    public static final int FLAG_LOADER_RUNNING = 2;
+    public static final int FLAG_DRAG_AND_DROP = 4;
+
+    private static final String TAG = "InstallShortcutReceiver";
+
+    // The set of shortcuts that are pending install
+    private static final String APPS_PENDING_INSTALL = "apps_to_install";
+
+    public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
+    public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
+
+    public static MainThreadInitializedObject<ItemInstallQueue> INSTANCE =
+            new MainThreadInitializedObject<>(ItemInstallQueue::new);
+
+    private final PersistedItemArray<PendingInstallShortcutInfo> mStorage =
+            new PersistedItemArray<>(APPS_PENDING_INSTALL);
+    private final Context mContext;
+
+    // Determines whether to defer installing shortcuts immediately until
+    // processAllPendingInstalls() is called.
+    private int mInstallQueueDisabledFlags = 0;
+
+    // Only accessed on worker thread
+    private List<PendingInstallShortcutInfo> mItems;
+
+    private ItemInstallQueue(Context context) {
+        mContext = context;
+    }
+
+    @WorkerThread
+    private void ensureQueueLoaded() {
+        Preconditions.assertWorkerThread();
+        if (mItems == null) {
+            mItems = mStorage.read(mContext, this::decode);
+        }
+    }
+
+    @WorkerThread
+    private void addToQueue(PendingInstallShortcutInfo info) {
+        ensureQueueLoaded();
+        if (!mItems.contains(info)) {
+            mItems.add(info);
+            mStorage.write(mContext, mItems);
+        }
+    }
+
+    @WorkerThread
+    private void flushQueueInBackground() {
+        Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
+        if (launcher == null) {
+            // Launcher not loaded
+            return;
+        }
+        ensureQueueLoaded();
+        if (mItems.isEmpty()) {
+            return;
+        }
+
+        List<Pair<ItemInfo, Object>> installQueue = mItems.stream()
+                .map(info -> info.getItemInfo(mContext))
+                .collect(Collectors.toList());
+
+        // Add the items and clear queue
+        if (!installQueue.isEmpty()) {
+            // add log
+            launcher.getModel().addAndBindAddedWorkspaceItems(installQueue);
+        }
+        mItems.clear();
+        mStorage.getFile(mContext).delete();
+    }
+
+    /**
+     * Removes previously added items from the queue.
+     */
+    @WorkerThread
+    public void removeFromInstallQueue(HashSet<String> packageNames, UserHandle user) {
+        if (packageNames.isEmpty()) {
+            return;
+        }
+        ensureQueueLoaded();
+        if (mItems.removeIf(item ->
+                item.user.equals(user) && packageNames.contains(getIntentPackage(item.intent)))) {
+            mStorage.write(mContext, mItems);
+        }
+    }
+
+    /**
+     * Adds an item to the install queue
+     */
+    public void queueItem(ShortcutInfo info) {
+        queuePendingShortcutInfo(new PendingInstallShortcutInfo(info));
+    }
+
+    /**
+     * Adds an item to the install queue
+     */
+    public void queueItem(AppWidgetProviderInfo info, int widgetId) {
+        queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId));
+    }
+
+    /**
+     * Adds an item to the install queue
+     */
+    public void queueItem(String packageName, UserHandle userHandle) {
+        queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle));
+    }
+
+    /**
+     * Returns a stream of all pending shortcuts in the queue
+     */
+    @WorkerThread
+    public Stream<ShortcutKey> getPendingShortcuts(UserHandle user) {
+        ensureQueueLoaded();
+        return mItems.stream()
+                .filter(item -> item.itemType == ITEM_TYPE_DEEP_SHORTCUT && user.equals(item.user))
+                .map(item -> ShortcutKey.fromIntent(item.intent, user));
+    }
+
+    private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
+        final Exception stackTrace = new Exception();
+
+        // Queue the item up for adding if launcher has not loaded properly yet
+        MODEL_EXECUTOR.post(() -> {
+            Pair<ItemInfo, Object> itemInfo = info.getItemInfo(mContext);
+            if (itemInfo == null) {
+                FileLog.d(LOG,
+                        "Adding PendingInstallShortcutInfo with no attached info to queue.",
+                        stackTrace);
+            } else {
+                FileLog.d(LOG,
+                        "Adding PendingInstallShortcutInfo to queue. Attached info: "
+                                + itemInfo.first,
+                        stackTrace);
+            }
+
+            addToQueue(info);
+        });
+        flushInstallQueue();
+    }
+
+    /**
+     * Pauses the push-to-model flow until unpaused. All items are held in the queue and
+     * not added to the model.
+     */
+    public void pauseModelPush(int flag) {
+        mInstallQueueDisabledFlags |= flag;
+    }
+
+    /**
+     * Adds all the queue items to the model if the use is completely resumed.
+     */
+    public void resumeModelPush(int flag) {
+        mInstallQueueDisabledFlags &= ~flag;
+        flushInstallQueue();
+    }
+
+    private void flushInstallQueue() {
+        if (mInstallQueueDisabledFlags != 0) {
+            return;
+        }
+        MODEL_EXECUTOR.post(this::flushQueueInBackground);
+    }
+
+    private static class PendingInstallShortcutInfo extends ItemInfo {
+
+        final Intent intent;
+
+        @Nullable ShortcutInfo shortcutInfo;
+        @Nullable AppWidgetProviderInfo providerInfo;
+
+        /**
+         * Initializes a PendingInstallShortcutInfo to represent a pending launcher target.
+         */
+        public PendingInstallShortcutInfo(String packageName, UserHandle userHandle) {
+            itemType = Favorites.ITEM_TYPE_APPLICATION;
+            intent = new Intent().setPackage(packageName);
+            user = userHandle;
+        }
+
+        /**
+         * Initializes a PendingInstallShortcutInfo to represent a deep shortcut.
+         */
+        public PendingInstallShortcutInfo(ShortcutInfo info) {
+            itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+            intent = ShortcutKey.makeIntent(info);
+            user = info.getUserHandle();
+
+            shortcutInfo = info;
+        }
+
+        /**
+         * Initializes a PendingInstallShortcutInfo to represent an app widget.
+         */
+        public PendingInstallShortcutInfo(AppWidgetProviderInfo info, int widgetId) {
+            itemType = Favorites.ITEM_TYPE_APPWIDGET;
+            intent = new Intent()
+                    .setComponent(info.provider)
+                    .putExtra(EXTRA_APPWIDGET_ID, widgetId);
+            user = info.getProfile();
+
+            providerInfo = info;
+        }
+
+        @Override
+        public Intent getIntent() {
+            return intent;
+        }
+
+        public Pair<ItemInfo, Object> getItemInfo(Context context) {
+            switch (itemType) {
+                case ITEM_TYPE_APPLICATION: {
+                    String packageName = intent.getPackage();
+                    List<LauncherActivityInfo> laiList =
+                            context.getSystemService(LauncherApps.class)
+                                    .getActivityList(packageName, user);
+
+                    final WorkspaceItemInfo si = new WorkspaceItemInfo();
+                    si.user = user;
+                    si.itemType = ITEM_TYPE_APPLICATION;
+
+                    LauncherActivityInfo lai;
+                    boolean usePackageIcon = laiList.isEmpty();
+                    if (usePackageIcon) {
+                        lai = null;
+                        si.intent = makeLaunchIntent(new ComponentName(packageName, ""))
+                                .setPackage(packageName);
+                        si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+                    } else {
+                        lai = laiList.get(0);
+                        si.intent = makeLaunchIntent(lai);
+                    }
+                    LauncherAppState.getInstance(context).getIconCache()
+                            .getTitleAndIcon(si, () -> lai, usePackageIcon, false);
+                    return Pair.create(si, null);
+                }
+                case ITEM_TYPE_DEEP_SHORTCUT: {
+                    WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, context);
+                    LauncherAppState.getInstance(context).getIconCache()
+                            .getShortcutIcon(itemInfo, shortcutInfo);
+                    return Pair.create(itemInfo, shortcutInfo);
+                }
+                case ITEM_TYPE_APPWIDGET: {
+                    LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
+                            .fromProviderInfo(context, providerInfo);
+                    LauncherAppWidgetInfo widgetInfo = new LauncherAppWidgetInfo(
+                            intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0),
+                            info.provider);
+                    InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+                    widgetInfo.minSpanX = info.minSpanX;
+                    widgetInfo.minSpanY = info.minSpanY;
+                    widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
+                    widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
+                    widgetInfo.user = user;
+                    return Pair.create(widgetInfo, providerInfo);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof PendingInstallShortcutInfo) {
+                PendingInstallShortcutInfo other = (PendingInstallShortcutInfo) obj;
+
+                boolean userMatches = user.equals(other.user);
+                boolean itemTypeMatches = itemType == other.itemType;
+                boolean intentMatches = intent.toUri(0).equals(other.intent.toUri(0));
+                boolean shortcutInfoMatches = shortcutInfo == null
+                        ? other.shortcutInfo == null
+                        : other.shortcutInfo != null
+                            && shortcutInfo.getId().equals(other.shortcutInfo.getId())
+                            && shortcutInfo.getPackage().equals(other.shortcutInfo.getPackage());
+                boolean providerInfoMatches = providerInfo == null
+                        ? other.providerInfo == null
+                        : other.providerInfo != null
+                            && providerInfo.provider.equals(other.providerInfo.provider);
+
+                return userMatches
+                        && itemTypeMatches
+                        && intentMatches
+                        && shortcutInfoMatches
+                        && providerInfoMatches;
+            }
+            return false;
+        }
+    }
+
+    private static String getIntentPackage(Intent intent) {
+        return intent.getComponent() == null
+                ? intent.getPackage() : intent.getComponent().getPackageName();
+    }
+
+    private PendingInstallShortcutInfo decode(int itemType, UserHandle user, Intent intent) {
+        switch (itemType) {
+            case Favorites.ITEM_TYPE_APPLICATION:
+                return new PendingInstallShortcutInfo(intent.getPackage(), user);
+            case Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
+                List<ShortcutInfo> si = ShortcutKey.fromIntent(intent, user)
+                        .buildRequest(mContext)
+                        .query(ShortcutRequest.ALL);
+                if (si.isEmpty()) {
+                    return null;
+                } else {
+                    return new PendingInstallShortcutInfo(si.get(0));
+                }
+            }
+            case Favorites.ITEM_TYPE_APPWIDGET: {
+                int widgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0);
+                AppWidgetProviderInfo info =
+                        AppWidgetManager.getInstance(mContext).getAppWidgetInfo(widgetId);
+                if (info == null || !info.provider.equals(intent.getComponent())
+                        || !info.getProfile().equals(user)) {
+                    return null;
+                }
+                return new PendingInstallShortcutInfo(info, widgetId);
+            }
+            default:
+                Log.e(TAG, "Unknown item type");
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 165d1ea..7e3bcee 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -35,11 +35,13 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 
+import androidx.annotation.Nullable;
 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.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
@@ -50,6 +52,7 @@
 import com.android.launcher3.model.data.AppInfo;
 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.ContentWriter;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.IntArray;
@@ -65,7 +68,7 @@
 
     private static final String TAG = "LoaderCursor";
 
-    public final LongSparseArray<UserHandle> allUsers;
+    private final LongSparseArray<UserHandle> allUsers;
 
     private final Uri mContentUri;
     private final Context mContext;
@@ -92,6 +95,9 @@
     private final int restoredIndex;
     private final int intentIndex;
 
+    @Nullable
+    private LauncherActivityInfo mActivityInfo;
+
     // Properties loaded per iteration
     public long serialNumber;
     public UserHandle user;
@@ -132,6 +138,8 @@
     public boolean moveToNext() {
         boolean result = super.moveToNext();
         if (result) {
+            mActivityInfo = null;
+
             // Load common properties.
             itemType = getInt(itemTypeIndex);
             container = getInt(containerIndex);
@@ -245,6 +253,10 @@
         return info;
     }
 
+    public LauncherActivityInfo getLauncherActivityInfo() {
+        return mActivityInfo;
+    }
+
     /**
      * Make an WorkspaceItemInfo object for a shortcut that is an application.
      */
@@ -264,25 +276,25 @@
         Intent newIntent = new Intent(Intent.ACTION_MAIN, null);
         newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
         newIntent.setComponent(componentName);
-        LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
+        mActivityInfo = mContext.getSystemService(LauncherApps.class)
                 .resolveActivity(newIntent, user);
-        if ((lai == null) && !allowMissingTarget) {
+        if ((mActivityInfo == null) && !allowMissingTarget) {
             Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
             return null;
         }
 
         final WorkspaceItemInfo info = new WorkspaceItemInfo();
-        info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        info.itemType = Favorites.ITEM_TYPE_APPLICATION;
         info.user = user;
         info.intent = newIntent;
 
-        mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
+        mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
         if (mIconCache.isDefaultIcon(info.bitmap, user)) {
             loadIcon(info);
         }
 
-        if (lai != null) {
-            AppInfo.updateRuntimeFlagsForActivityTarget(info, lai);
+        if (mActivityInfo != null) {
+            AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo);
         }
 
         // from the db
@@ -383,6 +395,11 @@
      * otherwise marks it for deletion.
      */
     public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
+        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);
         } else {
@@ -399,10 +416,10 @@
             final GridOccupancy hotseatOccupancy =
                     occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT);
 
-            if (item.screenId >= mIDP.numHotseatIcons) {
+            if (item.screenId >= mIDP.numDatabaseHotseatIcons) {
                 Log.e(TAG, "Error loading shortcut " + item
                         + " into hotseat position " + item.screenId
-                        + ", position out of bounds: (0 to " + (mIDP.numHotseatIcons - 1)
+                        + ", position out of bounds: (0 to " + (mIDP.numDatabaseHotseatIcons - 1)
                         + ")");
                 return false;
             }
@@ -418,7 +435,7 @@
                     return true;
                 }
             } else {
-                final GridOccupancy occupancy = new GridOccupancy(mIDP.numHotseatIcons, 1);
+                final GridOccupancy occupancy = new GridOccupancy(mIDP.numDatabaseHotseatIcons, 1);
                 occupancy.cells[item.screenId][0] = true;
                 occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
                 return true;
@@ -445,7 +462,8 @@
             if (item.screenId == Workspace.FIRST_SCREEN_ID) {
                 // Mark the first row as occupied (if the feature is enabled)
                 // in order to account for the QSB.
-                screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
+                int spanY = FeatureFlags.EXPANDED_SMARTSPACE.get() ? 2 : 1;
+                screen.markCells(0, 0, countX + 1, spanY, FeatureFlags.QSB_ON_FIRST_SCREEN);
             }
             occupied.put(item.screenId, screen);
         }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index d62f8d5..f6b0b4d 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -28,6 +28,7 @@
 import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
 import static com.android.launcher3.util.PackageManagerHelper.isSystemApp;
 
+import android.annotation.SuppressLint;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -40,18 +41,19 @@
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
+import android.graphics.Point;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.LongSparseArray;
-import android.util.MutableInt;
 import android.util.TimingLogger;
 
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
@@ -71,8 +73,8 @@
 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.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.pm.PackageInstallInfo;
@@ -85,10 +87,10 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IOUtils;
 import com.android.launcher3.util.LooperIdleLock;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
 import java.util.ArrayList;
@@ -97,6 +99,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CancellationException;
 
 /**
@@ -109,9 +112,12 @@
 public class LoaderTask implements Runnable {
     private static final String TAG = "LoaderTask";
 
+    private static final boolean DEBUG = true;
+
     protected final LauncherAppState mApp;
     private final AllAppsList mBgAllAppsList;
     protected final BgDataModel mBgDataModel;
+    private final ModelDelegate mModelDelegate;
 
     private FirstScreenBroadcast mFirstScreenBroadcast;
 
@@ -126,15 +132,20 @@
 
     private final UserManagerState mUserManagerState = new UserManagerState();
 
-    protected Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap;
+    protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>();
 
     private boolean mStopped;
 
+    private final Set<PackageUserKey> mPendingPackages = new HashSet<>();
+    private boolean mItemsDeleted = false;
+    private String mDbName;
+
     public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
-            LoaderResults results) {
+            ModelDelegate modelDelegate, LoaderResults results) {
         mApp = app;
         mBgAllAppsList = bgAllAppsList;
         mBgDataModel = dataModel;
+        mModelDelegate = modelDelegate;
         mResults = results;
 
         mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
@@ -162,12 +173,7 @@
 
     private void sendFirstScreenActiveInstallsBroadcast() {
         ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
-
-        ArrayList<ItemInfo> allItems = new ArrayList<>();
-        synchronized (mBgDataModel) {
-            allItems.addAll(mBgDataModel.workspaceItems);
-            allItems.addAll(mBgDataModel.appWidgets);
-        }
+        ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems();
         // Screen set is never empty
         final int firstScreen = mBgDataModel.collectWorkspaceScreens().get(0);
 
@@ -189,29 +195,39 @@
         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
             List<ShortcutInfo> allShortcuts = new ArrayList<>();
             loadWorkspace(allShortcuts);
-            loadCachedPredictions();
-            logger.addSplit("loadWorkspace");
+            logASplit(logger, "loadWorkspace");
+
+            // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
+            // sanitizeData should not be invoked if the workspace is loaded from a db different
+            // from the main db as defined in the invariant device profile.
+            // (e.g. both grid preview and minimal device mode uses a different db)
+            if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
+                verifyNotStopped();
+                sanitizeData();
+                logASplit(logger, "sanitizeData");
+            }
 
             verifyNotStopped();
             mResults.bindWorkspace();
-            logger.addSplit("bindWorkspace");
+            logASplit(logger, "bindWorkspace");
 
+            mModelDelegate.workspaceLoadComplete();
             // Notify the installer packages of packages with active installs on the first screen.
             sendFirstScreenActiveInstallsBroadcast();
-            logger.addSplit("sendFirstScreenActiveInstallsBroadcast");
+            logASplit(logger, "sendFirstScreenActiveInstallsBroadcast");
 
             // Take a break
             waitForIdle();
-            logger.addSplit("step 1 complete");
+            logASplit(logger, "step 1 complete");
             verifyNotStopped();
 
             // second step
             List<LauncherActivityInfo> allActivityList = loadAllApps();
-            logger.addSplit("loadAllApps");
+            logASplit(logger, "loadAllApps");
 
             verifyNotStopped();
             mResults.bindAllApps();
-            logger.addSplit("bindAllApps");
+            logASplit(logger, "bindAllApps");
 
             verifyNotStopped();
             IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
@@ -219,54 +235,54 @@
             updateHandler.updateIcons(allActivityList,
                     LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                     mApp.getModel()::onPackageIconsUpdated);
-            logger.addSplit("update icon cache");
+            logASplit(logger, "update icon cache");
 
             if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
                 verifyNotStopped();
-                logger.addSplit("save shortcuts in icon cache");
+                logASplit(logger, "save shortcuts in icon cache");
                 updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
                         mApp.getModel()::onPackageIconsUpdated);
             }
 
             // Take a break
             waitForIdle();
-            logger.addSplit("step 2 complete");
+            logASplit(logger, "step 2 complete");
             verifyNotStopped();
 
             // third step
             List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
-            logger.addSplit("loadDeepShortcuts");
+            logASplit(logger, "loadDeepShortcuts");
 
             verifyNotStopped();
             mResults.bindDeepShortcuts();
-            logger.addSplit("bindDeepShortcuts");
+            logASplit(logger, "bindDeepShortcuts");
 
             if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
                 verifyNotStopped();
-                logger.addSplit("save deep shortcuts in icon cache");
+                logASplit(logger, "save deep shortcuts in icon cache");
                 updateHandler.updateIcons(allDeepShortcuts,
                         new ShortcutCachingLogic(), (pkgs, user) -> { });
             }
 
             // Take a break
             waitForIdle();
-            logger.addSplit("step 3 complete");
+            logASplit(logger, "step 3 complete");
             verifyNotStopped();
 
             // fourth step
             List<ComponentWithLabelAndIcon> allWidgetsList =
                     mBgDataModel.widgetsModel.update(mApp, null);
-            logger.addSplit("load widgets");
+            logASplit(logger, "load widgets");
 
             verifyNotStopped();
             mResults.bindWidgets();
-            logger.addSplit("bindWidgets");
+            logASplit(logger, "bindWidgets");
             verifyNotStopped();
 
             updateHandler.updateIcons(allWidgetsList,
                     new ComponentWithIconCachingLogic(mApp.getContext(), true),
                     mApp.getModel()::onWidgetLabelsUpdated);
-            logger.addSplit("save widgets in icon cache");
+            logASplit(logger, "save widgets in icon cache");
 
             // fifth step
             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
@@ -275,12 +291,13 @@
 
             verifyNotStopped();
             updateHandler.finish();
-            logger.addSplit("finish icon update");
+            logASplit(logger, "finish icon update");
 
+            mModelDelegate.modelLoadComplete();
             transaction.commit();
         } catch (CancellationException e) {
             // Loader stopped, ignore
-            logger.addSplit("Cancelled");
+            logASplit(logger, "Cancelled");
         } finally {
             logger.dumpToLog();
         }
@@ -304,7 +321,7 @@
         final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
         final boolean isSafeMode = pmHelper.isSafeMode();
         final boolean isSdCardReady = Utilities.isBootCompleted();
-        final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
+        final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
 
         boolean clearDb = false;
         try {
@@ -333,6 +350,7 @@
 
         synchronized (mBgDataModel) {
             mBgDataModel.clear();
+            mPendingPackages.clear();
 
             final HashMap<PackageUserKey, SessionInfo> installingPkgs =
                     mSessionHelper.getActiveSessions();
@@ -345,7 +363,9 @@
             final LoaderCursor c = new LoaderCursor(
                     contentResolver.query(contentUri, null, selection, null, null), contentUri,
                     mApp, mUserManagerState);
-
+            final Bundle extras = c.getExtras();
+            mDbName = extras == null
+                    ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME);
             try {
                 final int appWidgetIdIndex = c.getColumnIndexOrThrow(
                         LauncherSettings.Favorites.APPWIDGET_ID);
@@ -359,16 +379,15 @@
                         LauncherSettings.Favorites.RANK);
                 final int optionsIndex = c.getColumnIndexOrThrow(
                         LauncherSettings.Favorites.OPTIONS);
+                final int sourceContainerIndex = c.getColumnIndexOrThrow(
+                        LauncherSettings.Favorites.APPWIDGET_SOURCE);
 
-                final LongSparseArray<UserHandle> allUsers = c.allUsers;
                 final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
 
                 mUserManagerState.init(mUserCache, mUserManager);
 
                 for (UserHandle user : mUserCache.getUserProfiles()) {
                     long serialNo = mUserCache.getSerialNumberForUser(user);
-                    allUsers.put(serialNo, user);
-
                     boolean userUnlocked = mUserManager.isUserUnlocked(user);
 
                     // We can only query for shortcuts when the user is unlocked.
@@ -392,6 +411,7 @@
 
                 WorkspaceItemInfo info;
                 LauncherAppWidgetInfo appWidgetInfo;
+                LauncherAppWidgetProviderInfo widgetProviderInfo;
                 Intent intent;
                 String targetPkg;
 
@@ -419,16 +439,6 @@
                             ComponentName cn = intent.getComponent();
                             targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
 
-                            if (allUsers.indexOfValue(c.user) < 0) {
-                                if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                                    c.markDeleted("Legacy shortcuts are only allowed for current users");
-                                    continue;
-                                } else if (c.restoreFlag != 0) {
-                                    // Don't restore items for other profiles.
-                                    c.markDeleted("Restore from other profiles not supported");
-                                    continue;
-                                }
-                            }
                             if (TextUtils.isEmpty(targetPkg) &&
                                     c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
                                 c.markDeleted("Only legacy shortcuts can have null package");
@@ -498,7 +508,7 @@
                                     // SdCard is not ready yet. Package might get available,
                                     // once it is ready.
                                     Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
-                                    pendingPackages.addToList(c.user, targetPkg);
+                                    mPendingPackages.add(new PackageUserKey(targetPkg, c.user));
                                     // Add the icon on the workspace anyway.
                                     allowMissingTarget = true;
                                 } else {
@@ -588,15 +598,27 @@
                                 if (isSafeMode && !isSystemApp(context, intent)) {
                                     info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
                                 }
+                                    LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
+                                    if (activityInfo != null) {
+                                        info.setProgressLevel(
+                                                PackageManagerHelper
+                                                    .getLoadingProgress(activityInfo),
+                                                PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+                                    }
 
                                 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
                                     tempPackageKey.update(targetPkg, c.user);
                                     SessionInfo si = installingPkgs.get(tempPackageKey);
-                                    if (si == null) {
-                                        info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
-                                    } else {
-                                        info.setInstallProgress((int) (si.getProgress() * 100));
-                                    }
+                                        if (si == null) {
+                                            info.runtimeStatusFlags &=
+                                                ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                                        } else if (activityInfo == null) {
+                                            int installProgress = (int) (si.getProgress() * 100);
+
+                                            info.setProgressLevel(
+                                                    installProgress,
+                                                    PackageInstallInfo.STATUS_INSTALLING);
+                                        }
                                 }
 
                                 c.checkAndAddItem(info, mBgDataModel);
@@ -652,12 +674,13 @@
                             final boolean wasProviderReady = !c.hasRestoreFlag(
                                     LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
 
-                            if (mWidgetProvidersMap == null) {
-                                mWidgetProvidersMap = WidgetManagerHelper.getAllProvidersMap(
-                                        context);
+                            ComponentKey providerKey = new ComponentKey(component, c.user);
+                            if (!mWidgetProvidersMap.containsKey(providerKey)) {
+                                mWidgetProvidersMap.put(providerKey,
+                                        widgetHelper.findProvider(component, c.user));
                             }
-                            final AppWidgetProviderInfo provider = mWidgetProvidersMap.get(
-                                    new ComponentKey(component, c.user));
+                            final AppWidgetProviderInfo provider =
+                                    mWidgetProvidersMap.get(providerKey);
 
                             final boolean isProviderReady = isValidProvider(provider);
                             if (!isSafeMode && !customWidget &&
@@ -725,12 +748,26 @@
                                 appWidgetInfo.spanY = c.getInt(spanYIndex);
                                 appWidgetInfo.options = c.getInt(optionsIndex);
                                 appWidgetInfo.user = c.user;
+                                appWidgetInfo.sourceContainer = c.getInt(sourceContainerIndex);
 
                                 if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
                                     c.markDeleted("Widget has invalid size: "
                                             + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
                                     continue;
                                 }
+                                widgetProviderInfo =
+                                        widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
+                                if (widgetProviderInfo != null
+                                        && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
+                                        || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
+                                    FileLog.d(TAG, "Widget " + widgetProviderInfo.getComponent()
+                                            + " minSizes not meet: span=" + appWidgetInfo.spanX
+                                            + "x" + appWidgetInfo.spanY + " minSpan="
+                                            + widgetProviderInfo.minSpanX + "x"
+                                            + widgetProviderInfo.minSpanY);
+                                    logWidgetInfo(mApp.getInvariantDeviceProfile(),
+                                            widgetProviderInfo);
+                                }
                                 if (!c.isOnWorkspaceOrHotseat()) {
                                     c.markDeleted("Widget found where container != " +
                                             "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
@@ -753,8 +790,8 @@
 
                                 if (appWidgetInfo.restoreStatus !=
                                         LauncherAppWidgetInfo.RESTORE_COMPLETED) {
-                                    String pkg = appWidgetInfo.providerName.getPackageName();
-                                    appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg);
+                                    appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo(
+                                            appWidgetInfo.providerName);
                                     appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
                                     mIconCache.getTitleAndIconForApp(
                                             appWidgetInfo.pendingItemInfo, false);
@@ -772,43 +809,17 @@
                 IOUtils.closeSilently(c);
             }
 
+            // Load delegate items
+            mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);
+
             // Break early if we've stopped loading
             if (mStopped) {
                 mBgDataModel.clear();
                 return;
             }
 
-            if (contentUri == LauncherSettings.Favorites.CONTENT_URI) {
-                // Remove dead items
-                if (c.commitDeleted()) {
-                    // Remove any empty folder
-                    int[] deletedFolderIds = LauncherSettings.Settings
-                            .call(contentResolver,
-                                    LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
-                            .getIntArray(LauncherSettings.Settings.EXTRA_VALUE);
-                    for (int folderId : deletedFolderIds) {
-                        mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
-                        mBgDataModel.folders.remove(folderId);
-                        mBgDataModel.itemsIdMap.remove(folderId);
-                    }
-
-                    // Remove any ghost widgets
-                    LauncherSettings.Settings.call(contentResolver,
-                            LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
-                }
-
-                // Unpin shortcuts that don't exist on the workspace.
-                HashSet<ShortcutKey> pendingShortcuts =
-                        InstallShortcutReceiver.getPendingShortcuts(context);
-                for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
-                    MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
-                    if ((numTimesPinned == null || numTimesPinned.value == 0)
-                            && !pendingShortcuts.contains(key)) {
-                        // Shortcut is pinned but doesn't exist on the workspace; unpin it.
-                        mBgDataModel.unpinShortcut(context, key);
-                    }
-                }
-            }
+            // Remove dead items
+            mItemsDeleted = c.commitDeleted();
 
             // Sort the folder items, update ranks, and make sure all preview items are high res.
             FolderGridOrganizer verifier =
@@ -834,13 +845,6 @@
             }
 
             c.commitRestoredItems();
-            if (!isSdCardReady && !pendingPackages.isEmpty()) {
-                context.registerReceiver(
-                        new SdCardAvailableReceiver(mApp, pendingPackages),
-                        new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
-                        null,
-                        MODEL_EXECUTOR.getHandler());
-            }
         }
     }
 
@@ -865,21 +869,37 @@
         }
     }
 
-    @WorkerThread
-    private void loadCachedPredictions() {
-        synchronized (mBgDataModel) {
-            List<ComponentKey> componentKeys =
-                    mApp.getPredictionModel().getPredictionComponentKeys();
-            List<LauncherActivityInfo> l;
-            mBgDataModel.cachedPredictedItems.clear();
-            for (ComponentKey key : componentKeys) {
-                l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user);
-                if (l.size() == 0) continue;
-                AppInfo info = new AppInfo(l.get(0), key.user,
-                        mUserManagerState.isUserQuiet(key.user));
-                mBgDataModel.cachedPredictedItems.add(info);
-                mIconCache.getTitleAndIcon(info, false);
+    private void sanitizeData() {
+        Context context = mApp.getContext();
+        ContentResolver contentResolver = context.getContentResolver();
+        if (mItemsDeleted) {
+            // Remove any empty folder
+            int[] deletedFolderIds = LauncherSettings.Settings
+                    .call(contentResolver,
+                            LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
+                    .getIntArray(LauncherSettings.Settings.EXTRA_VALUE);
+            synchronized (mBgDataModel) {
+                for (int folderId : deletedFolderIds) {
+                    mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
+                    mBgDataModel.folders.remove(folderId);
+                    mBgDataModel.itemsIdMap.remove(folderId);
+                }
             }
+
+        }
+        // Remove any ghost widgets
+        LauncherSettings.Settings.call(contentResolver,
+                LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
+
+        // Update pinned state of model shortcuts
+        mBgDataModel.updateShortcutPinnedState(context);
+
+        if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) {
+            context.registerReceiver(
+                    new SdCardAvailableReceiver(mApp, mPendingPackages),
+                    new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
+                    null,
+                    MODEL_EXECUTOR.getHandler());
         }
     }
 
@@ -914,14 +934,6 @@
                         PackageInstallInfo.fromInstallingState(info));
             }
         }
-        for (AppInfo item : mBgDataModel.cachedPredictedItems) {
-            List<LauncherActivityInfo> l = mLauncherApps.getActivityList(
-                    item.componentName.getPackageName(), item.user);
-            for (LauncherActivityInfo info : l) {
-                boolean quietMode = mUserManagerState.isUserQuiet(item.user);
-                mBgAllAppsList.add(new AppInfo(info, item.user, quietMode), info);
-            }
-        }
 
         mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED,
                 mUserManagerState.isAnyProfileQuietModeEnabled());
@@ -973,4 +985,54 @@
         return (provider != null) && (provider.provider != null)
                 && (provider.provider.getPackageName() != null);
     }
+
+    @SuppressLint("NewApi") // Already added API check.
+    private static void logWidgetInfo(InvariantDeviceProfile idp,
+            LauncherAppWidgetProviderInfo widgetProviderInfo) {
+        Point cellSize = new Point();
+        for (DeviceProfile deviceProfile : idp.supportedProfiles) {
+            deviceProfile.getCellSize(cellSize);
+            FileLog.d(TAG, "DeviceProfile available width: " + deviceProfile.availableWidthPx
+                    + ", available height: " + deviceProfile.availableHeightPx
+                    + ", cellLayoutBorderSpacingPx: " + deviceProfile.cellLayoutBorderSpacingPx
+                    + ", cellSize: " + cellSize);
+        }
+
+        StringBuilder widgetDimension = new StringBuilder();
+        widgetDimension.append("Widget dimensions:\n")
+                .append("minResizeWidth: ")
+                .append(widgetProviderInfo.minResizeWidth)
+                .append("\n")
+                .append("minResizeHeight: ")
+                .append(widgetProviderInfo.minResizeHeight)
+                .append("\n")
+                .append("defaultWidth: ")
+                .append(widgetProviderInfo.minWidth)
+                .append("\n")
+                .append("defaultHeight: ")
+                .append(widgetProviderInfo.minHeight)
+                .append("\n");
+        if (Utilities.ATLEAST_S) {
+            widgetDimension.append("targetCellWidth: ")
+                    .append(widgetProviderInfo.targetCellWidth)
+                    .append("\n")
+                    .append("targetCellHeight: ")
+                    .append(widgetProviderInfo.targetCellHeight)
+                    .append("\n")
+                    .append("maxResizeWidth: ")
+                    .append(widgetProviderInfo.maxResizeWidth)
+                    .append("\n")
+                    .append("maxResizeHeight: ")
+                    .append(widgetProviderInfo.maxResizeHeight)
+                    .append("\n");
+        }
+        FileLog.d(TAG, widgetDimension.toString());
+    }
+
+    private static void logASplit(final TimingLogger logger, final String label) {
+        logger.addSplit(label);
+        if (DEBUG) {
+            Log.d(TAG, label);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
new file mode 100644
index 0000000..13ec1ec
--- /dev/null
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -0,0 +1,101 @@
+/*
+ * 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.PackageManagerHelper.hasShortcutsPermission;
+
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Map;
+
+/**
+ * Class to extend LauncherModel functionality to provide extra data
+ */
+public class ModelDelegate implements ResourceBasedOverride {
+
+    /**
+     * Creates and initializes a new instance of the delegate
+     */
+    public static ModelDelegate newInstance(
+            Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel) {
+        ModelDelegate delegate = Overrides.getObject(
+                ModelDelegate.class, context, R.string.model_delegate_class);
+
+        delegate.mApp = app;
+        delegate.mAppsList = appsList;
+        delegate.mDataModel = dataModel;
+        return delegate;
+    }
+
+    protected LauncherAppState mApp;
+    protected AllAppsList mAppsList;
+    protected BgDataModel mDataModel;
+
+    public ModelDelegate() { }
+
+    /**
+     * Called periodically to validate and update any data
+     */
+    @WorkerThread
+    public void validateData() {
+        if (hasShortcutsPermission(mApp.getContext())
+                != mAppsList.hasShortcutHostPermission()) {
+            mApp.getModel().forceReload();
+        }
+    }
+
+    /**
+     * Load delegate items if any in the data model
+     */
+    @WorkerThread
+    public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) { }
+
+    /**
+     * Called during loader after workspace loading is complete
+     */
+    @WorkerThread
+    public void workspaceLoadComplete() { }
+
+    /**
+     * Called at the end of model load task
+     */
+    @WorkerThread
+    public void modelLoadComplete() { }
+
+    /**
+     * Called when the delegate is no loner needed
+     */
+    @WorkerThread
+    public void destroy() { }
+
+    /**
+     * Add data to a dumpsys request for Launcher (e.g. for bug reports).
+     *
+     * @see com.android.launcher3.Launcher#dump(java.lang.String, java.io.FileDescriptor,
+     *                                          java.io.PrintWriter, java.lang.String[])
+     **/
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { }
+}
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
index 713492b..756b7da 100644
--- a/src/com/android/launcher3/model/ModelPreload.java
+++ b/src/com/android/launcher3/model/ModelPreload.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.model;
 
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
 import android.content.Context;
 import android.util.Log;
 
@@ -52,8 +54,14 @@
     public final void run() {
         mModel.startLoaderForResultsIfNotLoaded(
                 new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
-        Log.d(TAG, "Preload completed : " + mModel.isModelLoaded());
-        onComplete(mModel.isModelLoaded());
+        MODEL_EXECUTOR.post(() -> {
+            Log.d(TAG, "Preload completed : " + mModel.isModelLoaded());
+            onComplete(mModel.isModelLoaded());
+        });
+    }
+
+    public BgDataModel getBgDataModel() {
+        return mBgDataModel;
     }
 
     /**
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 4efeba5..9b5fac8 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -15,17 +15,29 @@
  */
 package com.android.launcher3.model;
 
+import static com.android.launcher3.Utilities.isValidExtraType;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Process;
+import android.util.Log;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.IntStream;
 
 /**
@@ -33,6 +45,8 @@
  */
 public class ModelUtils {
 
+    private static final String TAG = "ModelUtils";
+
     /**
      * Filters the set of items who are directly or indirectly (via another container) on the
      * specified screen.
@@ -42,13 +56,7 @@
             ArrayList<T> currentScreenItems,
             ArrayList<T> otherScreenItems) {
         // Purge any null ItemInfos
-        Iterator<T> iter = allWorkspaceItems.iterator();
-        while (iter.hasNext()) {
-            ItemInfo i = iter.next();
-            if (i == null) {
-                iter.remove();
-            }
-        }
+        allWorkspaceItems.removeIf(Objects::isNull);
         // Order the set of items by their containers first, this allows use to walk through the
         // list sequentially, build up a list of containers that are in the specified screen,
         // as well as all items in those containers.
@@ -125,4 +133,52 @@
         IntStream.range(0, len).filter(i -> !seen.contains(i)).forEach(result::add);
         return result;
     }
+
+
+    /**
+     * Creates a workspace item info for the legacy shortcut intent
+     */
+    @SuppressWarnings("deprecation")
+    public static WorkspaceItemInfo fromLegacyShortcutIntent(Context context, Intent data) {
+        if (!isValidExtraType(data, Intent.EXTRA_SHORTCUT_INTENT, Intent.class)
+                || !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+                        Intent.ShortcutIconResource.class))
+                || !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON, Bitmap.class))) {
+
+            Log.e(TAG, "Invalid install shortcut intent");
+            return null;
+        }
+
+        Intent launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+        String label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+        if (launchIntent == null || label == null) {
+            Log.e(TAG, "Invalid install shortcut intent");
+            return null;
+        }
+
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
+        info.user = Process.myUserHandle();
+
+        BitmapInfo iconInfo = null;
+        try (LauncherIcons li = LauncherIcons.obtain(context)) {
+            Bitmap bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
+            if (bitmap != null) {
+                iconInfo = li.createIconBitmap(bitmap);
+            } else {
+                info.iconResource = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
+                if (info.iconResource != null) {
+                    iconInfo = li.createIconBitmap(info.iconResource);
+                }
+            }
+        }
+
+        if (iconInfo == null) {
+            Log.e(TAG, "Invalid icon by the app");
+            return null;
+        }
+        info.bitmap = iconInfo;
+        info.contentDescription = info.title = Utilities.trim(label);
+        info.intent = launchIntent;
+        return info;
+    }
 }
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 2c99df7..080ce20 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -28,7 +28,6 @@
 import android.util.Log;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetHost;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
@@ -43,6 +42,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -51,6 +51,7 @@
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 /**
  * Class for handling model updates.
@@ -90,7 +91,7 @@
         // in the hotseat
         if (container == Favorites.CONTAINER_HOTSEAT) {
             item.screenId = mHasVerticalHotseat
-                    ? LauncherAppState.getIDP(mContext).numHotseatIcons - cellY - 1 : cellX;
+                    ? LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons - cellY - 1 : cellX;
         } else {
             item.screenId = screenId;
         }
@@ -259,7 +260,9 @@
      * Removes all the items from the database matching {@param matcher}.
      */
     public void deleteItemsFromDatabase(ItemInfoMatcher matcher) {
-        deleteItemsFromDatabase(matcher.filterItemInfos(mBgDataModel.itemsIdMap));
+        deleteItemsFromDatabase(StreamSupport.stream(mBgDataModel.itemsIdMap.spliterator(), false)
+                        .filter(matcher::matchesInfo)
+                        .collect(Collectors.toList()));
     }
 
     /**
diff --git a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
new file mode 100644
index 0000000..c0dc34a
--- /dev/null
+++ b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
@@ -0,0 +1,77 @@
+/*
+ * 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 android.os.UserHandle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.PackageInstallInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles updates due to incremental download progress updates.
+ */
+public class PackageIncrementalDownloadUpdatedTask extends BaseModelUpdateTask {
+
+    private final UserHandle mUser;
+    private final int mProgress;
+    private final String mPackageName;
+
+    public PackageIncrementalDownloadUpdatedTask(
+            String packageName, UserHandle user, float progress) {
+        mUser = user;
+        mProgress = 1 - progress > 0.001 ? (int) (100 * progress) : 100;
+        mPackageName = packageName;
+    }
+
+    @Override
+    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+        PackageInstallInfo downloadInfo = new PackageInstallInfo(
+                mPackageName,
+                PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING,
+                mProgress,
+                mUser);
+
+        synchronized (appsList) {
+            List<AppInfo> updatedAppInfos = appsList.updatePromiseInstallInfo(downloadInfo);
+            if (!updatedAppInfos.isEmpty()) {
+                for (AppInfo appInfo : updatedAppInfos) {
+                    appInfo.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                    scheduleCallbackTask(
+                            c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
+                }
+            }
+            bindApplicationsIfNeeded();
+        }
+
+        final ArrayList<WorkspaceItemInfo> updatedWorkspaceItems = new ArrayList<>();
+        synchronized (dataModel) {
+            dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+                if (mPackageName.equals(si.getTargetPackage())) {
+                    si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+                    si.setProgressLevel(downloadInfo);
+                    updatedWorkspaceItems.add(si);
+                }
+            });
+        }
+        bindUpdatedWorkspaceItems(updatedWorkspaceItems);
+    }
+}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 203f1ca..9889a80 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -15,21 +15,19 @@
  */
 package com.android.launcher3.model;
 
-import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.CallbackTask;
-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.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.InstantAppResolver;
 
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Handles changes due to a sessions updates for a currently installing app.
@@ -61,30 +59,30 @@
         }
 
         synchronized (apps) {
-            PromiseAppInfo updated = apps.updatePromiseInstallInfo(mInstallInfo);
-            if (updated != null) {
-                scheduleCallbackTask(c -> c.bindPromiseAppProgressUpdated(updated));
+            List<AppInfo> updatedAppInfos = apps.updatePromiseInstallInfo(mInstallInfo);
+            if (!updatedAppInfos.isEmpty()) {
+                for (AppInfo appInfo : updatedAppInfos) {
+                    scheduleCallbackTask(c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
+                }
             }
             bindApplicationsIfNeeded();
         }
 
         synchronized (dataModel) {
             final HashSet<ItemInfo> updates = new HashSet<>();
-            for (ItemInfo info : dataModel.itemsIdMap) {
-                if (info instanceof WorkspaceItemInfo) {
-                    WorkspaceItemInfo si = (WorkspaceItemInfo) info;
-                    ComponentName cn = si.getTargetComponent();
-                    if (si.hasPromiseIconUi() && (cn != null)
-                            && mInstallInfo.packageName.equals(cn.getPackageName())) {
-                        si.setInstallProgress(mInstallInfo.progress);
-                        if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
-                            // Mark this info as broken.
-                            si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
-                        }
-                        updates.add(si);
+            dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
+                if (si.hasPromiseIconUi()
+                        && mInstallInfo.packageName.equals(si.getTargetPackage())) {
+                    int installProgress = mInstallInfo.progress;
+
+                    si.setProgressLevel(installProgress, PackageInstallInfo.STATUS_INSTALLING);
+                    if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
+                        // Mark this info as broken.
+                        si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
                     }
+                    updates.add(si);
                 }
-            }
+            });
 
             for (LauncherAppWidgetInfo widget : dataModel.appWidgets) {
                 if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) {
@@ -94,12 +92,7 @@
             }
 
             if (!updates.isEmpty()) {
-                scheduleCallbackTask(new CallbackTask() {
-                    @Override
-                    public void execute(Callbacks callbacks) {
-                        callbacks.bindRestoreItemsChange(updates);
-                    }
-                });
+                scheduleCallbackTask(callbacks -> callbacks.bindRestoreItemsChange(updates));
             }
         }
     }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 7cd467e..82b0f7c 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -22,18 +22,17 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
-import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
 
-import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.SessionCommitReceiver;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconCache;
@@ -42,10 +41,11 @@
 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.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.util.FlagOp;
-import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
@@ -54,6 +54,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 
@@ -92,10 +93,13 @@
 
         final String[] packages = mPackages;
         final int N = packages.length;
-        FlagOp flagOp = FlagOp.NO_OP;
+        final FlagOp flagOp;
         final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
-        ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
+        final ItemInfoMatcher matcher = mOp == OP_USER_AVAILABILITY_CHANGE
+                ? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user
+                : ItemInfoMatcher.ofPackages(packageSet, mUser);
         final HashSet<ComponentName> removedComponents = new HashSet<>();
+        final HashMap<String, List<LauncherActivityInfo>> activitiesLists = new HashMap<>();
 
         switch (mOp) {
             case OP_ADD: {
@@ -105,12 +109,8 @@
                     if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
                         appsList.removePackage(packages[i], mUser);
                     }
-                    appsList.addPackage(context, packages[i], mUser);
-
-                    // Automatically add homescreen icon for work profile apps for below O device.
-                    if (!Utilities.ATLEAST_OREO && !Process.myUserHandle().equals(mUser)) {
-                        SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
-                    }
+                    activitiesLists.put(
+                            packages[i], appsList.addPackage(context, packages[i], mUser));
                 }
                 flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
@@ -121,8 +121,17 @@
                     for (int i = 0; i < N; i++) {
                         if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
                         iconCache.updateIconsForPkg(packages[i], mUser);
-                        appsList.updatePackage(context, 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.
+                        Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
+                        if (launcher != null) {
+                            launcher.refreshAndBindWidgetsForPackageUser(
+                                    new PackageUserKey(packages[i], mUser));
+                        }
                     }
                 }
                 // Since package was just updated, the target must be available now.
@@ -158,19 +167,22 @@
                 flagOp = ums.isUserQuiet(mUser)
                         ? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
                         : FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
-                // We want to update all packages for this user.
-                matcher = ItemInfoMatcher.ofUser(mUser);
                 appsList.updateDisabledFlags(matcher, flagOp);
 
                 // We are not synchronizing here, as int operations are atomic
                 appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled());
                 break;
             }
+            default:
+                flagOp = FlagOp.NO_OP;
+                break;
         }
 
         bindApplicationsIfNeeded();
 
-        final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
+        final IntSet removedShortcuts = new IntSet();
+        // Shortcuts to keep even if the corresponding app was removed
+        final IntSet forceKeepShortcuts = new IntSet();
 
         // Update shortcut infos
         if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
@@ -180,118 +192,129 @@
             // For system apps, package manager send OP_UPDATE when an app is enabled.
             final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
             synchronized (dataModel) {
-                for (ItemInfo info : dataModel.itemsIdMap) {
-                    if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
-                        WorkspaceItemInfo si = (WorkspaceItemInfo) info;
-                        boolean infoUpdated = false;
-                        boolean shortcutUpdated = false;
+                dataModel.forAllWorkspaceItemInfos(mUser, si -> {
 
-                        // Update shortcuts which use iconResource.
-                        if ((si.iconResource != null)
-                                && packageSet.contains(si.iconResource.packageName)) {
-                            LauncherIcons li = LauncherIcons.obtain(context);
-                            BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
-                            li.recycle();
-                            if (iconInfo != null) {
-                                si.bitmap = iconInfo;
+                    boolean infoUpdated = false;
+                    boolean shortcutUpdated = false;
+
+                    // Update shortcuts which use iconResource.
+                    if ((si.iconResource != null)
+                            && packageSet.contains(si.iconResource.packageName)) {
+                        LauncherIcons li = LauncherIcons.obtain(context);
+                        BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
+                        li.recycle();
+                        if (iconInfo != null) {
+                            si.bitmap = iconInfo;
+                            infoUpdated = true;
+                        }
+                    }
+
+                    ComponentName cn = si.getTargetComponent();
+                    if (cn != null && matcher.matches(si, cn)) {
+                        String packageName = cn.getPackageName();
+
+                        if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
+                            forceKeepShortcuts.add(si.id);
+                            if (mOp == OP_REMOVE) {
+                                return;
+                            }
+                        }
+
+                        if (si.isPromise() && isNewApkAvailable) {
+                            boolean isTargetValid = !cn.getClassName().equals(
+                                    IconCache.EMPTY_CLASS_NAME);
+                            if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                                List<ShortcutInfo> shortcut =
+                                        new ShortcutRequest(context, mUser)
+                                                .forPackage(cn.getPackageName(),
+                                                        si.getDeepShortcutId())
+                                                .query(ShortcutRequest.PINNED);
+                                if (shortcut.isEmpty()) {
+                                    isTargetValid = false;
+                                } else {
+                                    si.updateFromDeepShortcutInfo(shortcut.get(0), context);
+                                    infoUpdated = true;
+                                }
+                            } else if (isTargetValid) {
+                                isTargetValid = context.getSystemService(LauncherApps.class)
+                                        .isActivityEnabled(cn, mUser);
+                            }
+                            if (!isTargetValid && si.hasStatusFlag(
+                                    FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
+                                if (updateWorkspaceItemIntent(context, si, packageName)) {
+                                    infoUpdated = true;
+                                } else if (si.hasPromiseIconUi()) {
+                                    removedShortcuts.add(si.id);
+                                    return;
+                                }
+                            } else if (!isTargetValid) {
+                                removedShortcuts.add(si.id);
+                                FileLog.e(TAG, "Restored shortcut no longer valid "
+                                        + si.getIntent());
+                                return;
+                            } else {
+                                si.status = WorkspaceItemInfo.DEFAULT;
+                                infoUpdated = true;
+                            }
+                        } else if (isNewApkAvailable && removedComponents.contains(cn)) {
+                            if (updateWorkspaceItemIntent(context, si, packageName)) {
                                 infoUpdated = true;
                             }
                         }
 
-                        ComponentName cn = si.getTargetComponent();
-                        if (cn != null && matcher.matches(si, cn)) {
-                            String packageName = cn.getPackageName();
-
-                            if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
-                                removedShortcuts.put(si.id, false);
-                                if (mOp == OP_REMOVE) {
-                                    continue;
-                                }
-                            }
-
-                            if (si.isPromise() && isNewApkAvailable) {
-                                boolean isTargetValid = true;
-                                if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                                    List<ShortcutInfo> shortcut =
-                                            new ShortcutRequest(context, mUser)
-                                                    .forPackage(cn.getPackageName(),
-                                                            si.getDeepShortcutId())
-                                                    .query(ShortcutRequest.PINNED);
-                                    if (shortcut.isEmpty()) {
-                                        isTargetValid = false;
-                                    } else {
-                                        si.updateFromDeepShortcutInfo(shortcut.get(0), context);
-                                        infoUpdated = true;
-                                    }
-                                } else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
-                                    isTargetValid = context.getSystemService(LauncherApps.class)
-                                            .isActivityEnabled(cn, mUser);
-                                }
-                                if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
-                                    if (updateWorkspaceItemIntent(context, si, packageName)) {
-                                        infoUpdated = true;
-                                    } else if (si.hasPromiseIconUi()) {
-                                        removedShortcuts.put(si.id, true);
-                                        continue;
-                                    }
-                                } else if (!isTargetValid) {
-                                    removedShortcuts.put(si.id, true);
-                                    FileLog.e(TAG, "Restored shortcut no longer valid "
-                                            + si.getIntent());
-                                    continue;
-                                } else {
-                                    si.status = WorkspaceItemInfo.DEFAULT;
-                                    infoUpdated = true;
-                                }
-                            } else if (isNewApkAvailable && removedComponents.contains(cn)) {
-                                if (updateWorkspaceItemIntent(context, si, packageName)) {
-                                    infoUpdated = true;
-                                }
-                            }
-
-                            if (isNewApkAvailable &&
-                                    si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
+                        if (isNewApkAvailable) {
+                            List<LauncherActivityInfo> activities = activitiesLists.get(
+                                    packageName);
+                            si.setProgressLevel(
+                                    activities == null || activities.isEmpty()
+                                            ? 100
+                                            : PackageManagerHelper.getLoadingProgress(
+                                                    activities.get(0)),
+                                    PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+                            if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
                                 iconCache.getTitleAndIcon(si, si.usingLowResIcon());
                                 infoUpdated = true;
                             }
-
-                            int oldRuntimeFlags = si.runtimeStatusFlags;
-                            si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
-                            if (si.runtimeStatusFlags != oldRuntimeFlags) {
-                                shortcutUpdated = true;
-                            }
                         }
 
-                        if (infoUpdated || shortcutUpdated) {
-                            updatedWorkspaceItems.add(si);
+                        int oldRuntimeFlags = si.runtimeStatusFlags;
+                        si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
+                        if (si.runtimeStatusFlags != oldRuntimeFlags) {
+                            shortcutUpdated = true;
                         }
-                        if (infoUpdated) {
-                            getModelWriter().updateItemInDatabase(si);
-                        }
-                    } else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
-                        LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
-                        if (mUser.equals(widgetInfo.user)
-                                && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
-                                && packageSet.contains(widgetInfo.providerName.getPackageName())) {
-                            widgetInfo.restoreStatus &=
-                                    ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
-                                            ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+                    }
 
-                            // adding this flag ensures that launcher shows 'click to setup'
-                            // if the widget has a config activity. In case there is no config
-                            // activity, it will be marked as 'restored' during bind.
-                            widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+                    if (infoUpdated || shortcutUpdated) {
+                        updatedWorkspaceItems.add(si);
+                    }
+                    if (infoUpdated && si.id != ItemInfo.NO_ID) {
+                        getModelWriter().updateItemInDatabase(si);
+                    }
+                });
 
-                            widgets.add(widgetInfo);
-                            getModelWriter().updateItemInDatabase(widgetInfo);
-                        }
+                for (LauncherAppWidgetInfo widgetInfo : dataModel.appWidgets) {
+                    if (mUser.equals(widgetInfo.user)
+                            && widgetInfo.hasRestoreFlag(
+                                    LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+                            && packageSet.contains(widgetInfo.providerName.getPackageName())) {
+                        widgetInfo.restoreStatus &=
+                                ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY
+                                        & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+
+                        // adding this flag ensures that launcher shows 'click to setup'
+                        // if the widget has a config activity. In case there is no config
+                        // activity, it will be marked as 'restored' during bind.
+                        widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
+                        widgets.add(widgetInfo);
+                        getModelWriter().updateItemInDatabase(widgetInfo);
                     }
                 }
             }
 
             bindUpdatedWorkspaceItems(updatedWorkspaceItems);
             if (!removedShortcuts.isEmpty()) {
-                deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
+                deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts));
             }
 
             if (!widgets.isEmpty()) {
@@ -319,14 +342,15 @@
         if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
             ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
                     .or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
-                    .and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
+                    .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate());
             deleteAndBindComponentsRemoved(removeMatch);
 
             // Remove any queued items from the install queue
-            InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
+            ItemInstallQueue.INSTANCE.get(context)
+                    .removeFromInstallQueue(removedPackages, mUser);
         }
 
-        if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
+        if (mOp == OP_ADD) {
             // Load widgets for the new package. Changes due to app updates are handled through
             // AppWidgetHost events, this is just to initialize the long-press options.
             for (int i = 0; i < N; i++) {
@@ -342,6 +366,11 @@
      */
     private boolean updateWorkspaceItemIntent(Context context,
             WorkspaceItemInfo si, String packageName) {
+        if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+            // Do not update intent for deep shortcuts as they contain additional information
+            // about the shortcut.
+            return false;
+        }
         // Try to find the best match activity.
         Intent intent = new PackageManagerHelper(context).getAppLaunchIntent(packageName, mUser);
         if (intent != null) {
diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java
deleted file mode 100644
index cb3903d..0000000
--- a/src/com/android/launcher3/model/PredictionModel.java
+++ /dev/null
@@ -1,136 +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.MODEL_EXECUTOR;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.UserHandle;
-
-import androidx.annotation.AnyThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Model Helper for app predictions
- */
-public class PredictionModel implements ResourceBasedOverride {
-
-    private static final String CACHED_ITEMS_KEY = "predicted_item_keys";
-    private static final int MAX_CACHE_ITEMS = 5;
-
-    protected Context mContext;
-    private SharedPreferences mDevicePrefs;
-    private UserCache mUserCache;
-
-
-    /**
-     * Retrieve instance of this object that can be overridden in runtime based on the build
-     * variant of the application.
-     */
-    public static PredictionModel newInstance(Context context) {
-        PredictionModel model = Overrides.getObject(PredictionModel.class, context,
-                R.string.prediction_model_class);
-        model.init(context);
-        return model;
-    }
-
-    protected void init(Context context) {
-        mContext = context;
-        mDevicePrefs = Utilities.getDevicePrefs(mContext);
-        mUserCache = UserCache.INSTANCE.get(mContext);
-
-    }
-
-    /**
-     * Formats and stores a list of component key in device preferences.
-     */
-    @AnyThread
-    public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
-        MODEL_EXECUTOR.execute(() -> {
-            LauncherAppState appState = LauncherAppState.getInstance(mContext);
-            StringBuilder builder = new StringBuilder();
-            int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
-            for (int i = 0; i < count; i++) {
-                builder.append(serializeComponentKeyToString(componentKeys.get(i)));
-                builder.append("\n");
-            }
-            if (componentKeys.isEmpty() /* should invalidate loader items */) {
-                appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
-                    @Override
-                    public void execute(LauncherAppState app, BgDataModel model, AllAppsList apps) {
-                        model.cachedPredictedItems.clear();
-                    }
-                });
-            }
-            mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
-        });
-    }
-
-    /**
-     * parses and returns ComponentKeys saved by
-     * {@link PredictionModel#cachePredictionComponentKeys(List)}
-     */
-    @WorkerThread
-    public List<ComponentKey> getPredictionComponentKeys() {
-        Preconditions.assertWorkerThread();
-        ArrayList<ComponentKey> items = new ArrayList<>();
-        String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
-        for (String line : cachedBlob.split("\n")) {
-            ComponentKey key = getComponentKeyFromSerializedString(line);
-            if (key != null) {
-                items.add(key);
-            }
-
-        }
-        return items;
-    }
-
-    private String serializeComponentKeyToString(ComponentKey componentKey) {
-        long userSerialNumber = mUserCache.getSerialNumberForUser(componentKey.user);
-        return componentKey.componentName.flattenToString() + "#" + userSerialNumber;
-    }
-
-    private ComponentKey getComponentKeyFromSerializedString(String str) {
-        int sep = str.indexOf('#');
-        if (sep < 0 || (sep + 1) >= str.length()) {
-            return null;
-        }
-        ComponentName componentName = ComponentName.unflattenFromString(str.substring(0, sep));
-        if (componentName == null) {
-            return null;
-        }
-        try {
-            long serialNumber = Long.parseLong(str.substring(sep + 1));
-            UserHandle userHandle = mUserCache.getUserForSerialNumber(serialNumber);
-            return userHandle != null ? new ComponentKey(componentName, userHandle) : null;
-        } catch (NumberFormatException ex) {
-            return null;
-        }
-    }
-}
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index eb3cb52..3798575 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -24,12 +24,11 @@
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * Helper class to re-query app status when SD-card becomes available.
@@ -42,10 +41,9 @@
 
     private final LauncherModel mModel;
     private final Context mContext;
-    private final MultiHashMap<UserHandle, String> mPackages;
+    private final Set<PackageUserKey> mPackages;
 
-    public SdCardAvailableReceiver(LauncherAppState app,
-            MultiHashMap<UserHandle, String> packages) {
+    public SdCardAvailableReceiver(LauncherAppState app, Set<PackageUserKey> packages) {
         mModel = app.getModel();
         mContext = app.getContext();
         mPackages = packages;
@@ -55,19 +53,17 @@
     public void onReceive(Context context, Intent intent) {
         final LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
         final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
-        for (Entry<UserHandle, ArrayList<String>> entry : mPackages.entrySet()) {
-            UserHandle user = entry.getKey();
+        for (PackageUserKey puk : mPackages) {
+            UserHandle user = puk.mUser;
 
             final ArrayList<String> packagesRemoved = new ArrayList<>();
             final ArrayList<String> packagesUnavailable = new ArrayList<>();
 
-            for (String pkg : new HashSet<>(entry.getValue())) {
-                if (!launcherApps.isPackageEnabled(pkg, user)) {
-                    if (pmHelper.isAppOnSdcard(pkg, user)) {
-                        packagesUnavailable.add(pkg);
-                    } else {
-                        packagesRemoved.add(pkg);
-                    }
+            if (!launcherApps.isPackageEnabled(puk.mPackageName, user)) {
+                if (pmHelper.isAppOnSdcard(puk.mPackageName, user)) {
+                    packagesUnavailable.add(puk.mPackageName);
+                } else {
+                    packagesRemoved.add(puk.mPackageName);
                 }
             }
             if (!packagesRemoved.isEmpty()) {
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 1cbe5c2..4296d32 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -21,16 +21,17 @@
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Handles changes due to shortcut manager updates (deep shortcut changes)
@@ -54,54 +55,61 @@
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         final Context context = app.getContext();
         // Find WorkspaceItemInfo's that have changed on the workspace.
-        HashSet<ShortcutKey> removedKeys = new HashSet<>();
-        MultiHashMap<ShortcutKey, WorkspaceItemInfo> keyToShortcutInfo = new MultiHashMap<>();
-        HashSet<String> allIds = new HashSet<>();
+        ArrayList<WorkspaceItemInfo> matchingWorkspaceItems = new ArrayList<>();
 
-        for (ItemInfo itemInfo : dataModel.itemsIdMap) {
-            if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
-                if (mPackageName.equals(si.getIntent().getPackage()) && si.user.equals(mUser)) {
-                    keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
-                    allIds.add(si.getDeepShortcutId());
+        synchronized (dataModel) {
+            dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+                if ((si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+                        && mPackageName.equals(si.getIntent().getPackage())) {
+                    matchingWorkspaceItems.add(si);
                 }
-            }
+            });
         }
 
-        final ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
-        if (!keyToShortcutInfo.isEmpty()) {
+        if (!matchingWorkspaceItems.isEmpty()) {
+            if (mShortcuts.isEmpty()) {
+                // Verify that the app is indeed installed.
+                if (!new PackageManagerHelper(app.getContext())
+                        .isAppInstalled(mPackageName, mUser)) {
+                    // App is not installed, ignoring package events
+                    return;
+                }
+            }
             // Update the workspace to reflect the changes to updated shortcuts residing on it.
+            List<String> allLauncherKnownIds = matchingWorkspaceItems.stream()
+                    .map(WorkspaceItemInfo::getDeepShortcutId)
+                    .distinct()
+                    .collect(Collectors.toList());
             List<ShortcutInfo> shortcuts = new ShortcutRequest(context, mUser)
-                    .forPackage(mPackageName, new ArrayList<>(allIds))
+                    .forPackage(mPackageName, allLauncherKnownIds)
                     .query(ShortcutRequest.ALL);
+
+            Set<String> nonPinnedIds = new HashSet<>(allLauncherKnownIds);
+            ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
             for (ShortcutInfo fullDetails : shortcuts) {
-                ShortcutKey key = ShortcutKey.fromInfo(fullDetails);
-                List<WorkspaceItemInfo> workspaceItemInfos = keyToShortcutInfo.remove(key);
                 if (!fullDetails.isPinned()) {
-                    // The shortcut was previously pinned but is no longer, so remove it from
-                    // the workspace and our pinned shortcut counts.
-                    // Note that we put this check here, after querying for full details,
-                    // because there's a possible race condition between pinning and
-                    // receiving this callback.
-                    removedKeys.add(key);
                     continue;
                 }
-                for (final WorkspaceItemInfo workspaceItemInfo : workspaceItemInfos) {
-                    workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
-                    app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails);
-                    updatedWorkspaceItemInfos.add(workspaceItemInfo);
-                }
+
+                String sid = fullDetails.getId();
+                nonPinnedIds.remove(sid);
+                matchingWorkspaceItems
+                        .stream()
+                        .filter(itemInfo -> sid.equals(itemInfo.getDeepShortcutId()))
+                        .forEach(workspaceItemInfo -> {
+                            workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
+                            app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails);
+                            updatedWorkspaceItemInfos.add(workspaceItemInfo);
+                        });
             }
-        }
 
-        // If there are still entries in keyToShortcutInfo, that means that
-        // the corresponding shortcuts weren't passed in onShortcutsChanged(). This
-        // means they were cleared, so we remove and unpin them now.
-        removedKeys.addAll(keyToShortcutInfo.keySet());
-
-        bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
-        if (!keyToShortcutInfo.isEmpty()) {
-            deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
+            bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
+            if (!nonPinnedIds.isEmpty()) {
+                deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(
+                        nonPinnedIds.stream()
+                                .map(id -> new ShortcutKey(mPackageName, mUser, id))
+                                .collect(Collectors.toSet())));
+            }
         }
 
         if (mUpdateIdMap) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 7ec884f..5048e13 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -23,7 +23,6 @@
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -73,27 +72,27 @@
         ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
         HashSet<ShortcutKey> removedKeys = new HashSet<>();
 
-        for (ItemInfo itemInfo : dataModel.itemsIdMap) {
-            if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
-                    && mUser.equals(itemInfo.user)) {
-                WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
-                if (mIsUserUnlocked) {
-                    ShortcutKey key = ShortcutKey.fromItemInfo(si);
-                    ShortcutInfo shortcut = pinnedShortcuts.get(key);
-                    // We couldn't verify the shortcut during loader. If its no longer available
-                    // (probably due to clear data), delete the workspace item as well
-                    if (shortcut == null) {
-                        removedKeys.add(key);
-                        continue;
+        synchronized (dataModel) {
+            dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+                if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                    if (mIsUserUnlocked) {
+                        ShortcutKey key = ShortcutKey.fromItemInfo(si);
+                        ShortcutInfo shortcut = pinnedShortcuts.get(key);
+                        // We couldn't verify the shortcut during loader. If its no longer available
+                        // (probably due to clear data), delete the workspace item as well
+                        if (shortcut == null) {
+                            removedKeys.add(key);
+                            return;
+                        }
+                        si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
+                        si.updateFromDeepShortcutInfo(shortcut, context);
+                        app.getIconCache().getShortcutIcon(si, shortcut);
+                    } else {
+                        si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                     }
-                    si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
-                    si.updateFromDeepShortcutInfo(shortcut, context);
-                    app.getIconCache().getShortcutIcon(si, shortcut);
-                } else {
-                    si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
+                    updatedWorkspaceItemInfos.add(si);
                 }
-                updatedWorkspaceItemInfos.add(si);
-            }
+            });
         }
         bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
         if (!removedKeys.isEmpty()) {
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index 37c089e..7198d54 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -1,14 +1,18 @@
 package com.android.launcher3.model;
 
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
+import android.annotation.SuppressLint;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
 /**
  * An wrapper over various items displayed in a widget picker,
@@ -43,4 +47,31 @@
         activityInfo = info;
         spanX = spanY = 1;
     }
+
+    /**
+     * Returns {@code true} if this {@link WidgetItem} has the same type as the given
+     * {@code otherItem}.
+     *
+     * For example, both items are widgets or both items are shortcuts.
+     */
+    public boolean hasSameType(WidgetItem otherItem) {
+        if (widgetInfo != null && otherItem.widgetInfo != null) {
+            return true;
+        }
+        if (activityInfo != null && otherItem.activityInfo != null) {
+            return true;
+        }
+        return false;
+    }
+
+    /** Returns whether this {@link WidgetItem} has a preview layout that can be used. */
+    @SuppressLint("NewApi") // Already added API check.
+    public boolean hasPreviewLayout() {
+        return ATLEAST_S && widgetInfo != null && widgetInfo.previewLayout != Resources.ID_NULL;
+    }
+
+    /** Returns whether this {@link WidgetItem} is for a shortcut rather than an app widget. */
+    public boolean isShortcut() {
+        return activityInfo != null;
+    }
 }
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index b17b062..7f70bad 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -28,10 +28,13 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 
@@ -91,8 +94,6 @@
         componentName = info.componentName;
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
-        user = info.user;
-        runtimeStatusFlags = info.runtimeStatusFlags;
     }
 
     @VisibleForTesting
@@ -104,13 +105,38 @@
         this.intent = intent;
     }
 
+    public AppInfo(@NonNull PackageInstallInfo installInfo) {
+        componentName = installInfo.componentName;
+        intent = new Intent(Intent.ACTION_MAIN)
+            .addCategory(Intent.CATEGORY_LAUNCHER)
+            .setComponent(componentName)
+            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        setProgressLevel(installInfo);
+        user = installInfo.user;
+    }
+
     @Override
     protected String dumpProperties() {
         return super.dumpProperties() + " componentName=" + componentName;
     }
 
     public WorkspaceItemInfo makeWorkspaceItem() {
-        return new WorkspaceItemInfo(this);
+        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(this);
+
+        if ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            // We need to update the component name when the apk is installed
+            workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+            // Since the user is manually placing it on homescreen, it should not be auto-removed
+            // later
+            workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
+            workspaceItemInfo.status |= FLAG_INSTALL_SESSION_ACTIVE;
+        }
+        if ((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+            workspaceItemInfo.runtimeStatusFlags |= FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        }
+
+        return workspaceItemInfo;
     }
 
     public ComponentKey toComponentKey() {
@@ -129,6 +155,12 @@
                         | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
     }
 
+    @Nullable
+    @Override
+    public ComponentName getTargetComponent() {
+        return componentName;
+    }
+
     public static void updateRuntimeFlagsForActivityTarget(
             ItemInfoWithIcon info, LauncherActivityInfo lai) {
         ApplicationInfo appInfo = lai.getApplicationInfo();
@@ -138,12 +170,16 @@
         info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
                 ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
 
-        if (Utilities.ATLEAST_OREO
-                && appInfo.targetSdkVersion >= Build.VERSION_CODES.O
+        if (appInfo.targetSdkVersion >= Build.VERSION_CODES.O
                 && Process.myUserHandle().equals(lai.getUser())) {
             // The icon for a non-primary user is badged, hence it's not exactly an adaptive icon.
             info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON;
         }
+
+        // Sets the progress level, installation and incremental download flags.
+        info.setProgressLevel(
+                PackageManagerHelper.getLoadingProgress(lai),
+                PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
     }
 
     @Override
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 41ccbd7..cd2ef35 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -20,15 +20,9 @@
 
 import static androidx.core.util.Preconditions.checkNotNull;
 
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.logger.LauncherAtom.Attribute.EMPTY_LABEL;
 import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
 import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
 
 import android.os.Process;
 
@@ -40,16 +34,15 @@
 import com.android.launcher3.folder.FolderNameInfos;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logger.LauncherAtom.Attribute;
+import com.android.launcher3.logger.LauncherAtom.FolderIcon;
 import com.android.launcher3.logger.LauncherAtom.FromState;
 import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.model.ModelWriter;
-import com.android.launcher3.userevent.LauncherLogProto;
-import com.android.launcher3.userevent.LauncherLogProto.Target;
-import com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState;
-import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
 import com.android.launcher3.util.ContentWriter;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.OptionalInt;
 import java.util.stream.IntStream;
 
@@ -147,9 +140,16 @@
      * @param item
      */
     public void remove(WorkspaceItemInfo item, boolean animate) {
-        contents.remove(item);
+        removeAll(Collections.singletonList(item), animate);
+    }
+
+    /**
+     * Remove all matching app or shortcut. Does not change the DB.
+     */
+    public void removeAll(List<WorkspaceItemInfo> items, boolean animate) {
+        contents.removeAll(items);
         for (int i = 0; i < mListeners.size(); i++) {
-            mListeners.get(i).onRemove(item);
+            mListeners.get(i).onRemove(items);
         }
         itemsChanged(animate);
     }
@@ -176,9 +176,9 @@
     }
 
     public interface FolderListener {
-        public void onAdd(WorkspaceItemInfo item, int rank);
-        public void onRemove(WorkspaceItemInfo item);
-        public void onItemsChanged(boolean animate);
+        void onAdd(WorkspaceItemInfo item, int rank);
+        void onRemove(List<WorkspaceItemInfo> item);
+        void onItemsChanged(boolean animate);
     }
 
     public boolean hasOption(int optionFlag) {
@@ -209,8 +209,13 @@
 
     @Override
     public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
+        FolderIcon.Builder folderIcon = FolderIcon.newBuilder()
+                .setCardinality(contents.size());
+        if (LabelState.SUGGESTED.equals(getLabelState())) {
+            folderIcon.setLabelInfo(title.toString());
+        }
         return getDefaultItemInfoBuilder()
-                .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
+                .setFolderIcon(folderIcon)
                 .setRank(rank)
                 .setAttribute(getLabelState().mLogAttribute)
                 .setContainerInfo(getContainerInfo())
@@ -359,113 +364,4 @@
         }
         return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
     }
-
-    /**
-     * Returns {@link LauncherLogProto.LauncherEvent} to log current folder label info.
-     *
-     * @deprecated This method is used only for validation purpose and soon will be removed.
-     */
-    @Deprecated
-    public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent(FromState fromState,
-            ToState toState) {
-        return LauncherLogProto.LauncherEvent.newBuilder()
-                .setAction(LauncherLogProto.Action
-                        .newBuilder()
-                        .setType(LauncherLogProto.Action.Type.SOFT_KEYBOARD))
-                .addSrcTarget(Target
-                        .newBuilder()
-                        .setType(Target.Type.ITEM)
-                        .setItemType(LauncherLogProto.ItemType.EDITTEXT)
-                        .setFromFolderLabelState(convertFolderLabelState(fromState))
-                        .setToFolderLabelState(convertFolderLabelState(toState)))
-                .addSrcTarget(Target.newBuilder()
-                        .setType(Target.Type.CONTAINER)
-                        .setContainerType(LauncherLogProto.ContainerType.FOLDER)
-                        .setPageIndex(screenId)
-                        .setGridX(cellX)
-                        .setGridY(cellY)
-                        .setCardinality(contents.size()))
-                .addSrcTarget(newParentContainerTarget())
-                .build();
-    }
-
-    /**
-     * @deprecated This method is used only for validation purpose and soon will be removed.
-     */
-    @Deprecated
-    private Target.Builder newParentContainerTarget() {
-        Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
-        switch (container) {
-            case CONTAINER_HOTSEAT:
-                return builder.setContainerType(LauncherLogProto.ContainerType.HOTSEAT);
-            case CONTAINER_DESKTOP:
-                return builder.setContainerType(LauncherLogProto.ContainerType.WORKSPACE);
-            default:
-                throw new AssertionError(String
-                        .format("Expected container to be either %s or %s but found %s.",
-                                CONTAINER_HOTSEAT,
-                                CONTAINER_DESKTOP,
-                                container));
-        }
-    }
-
-    /**
-     * @deprecated This method is used only for validation purpose and soon will be removed.
-     */
-    @Deprecated
-    private static FromFolderLabelState convertFolderLabelState(FromState fromState) {
-        switch (fromState) {
-            case FROM_EMPTY:
-                return FROM_EMPTY;
-            case FROM_SUGGESTED:
-                return FROM_SUGGESTED;
-            case FROM_CUSTOM:
-                return FROM_CUSTOM;
-            default:
-                return FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
-        }
-    }
-
-    /**
-     * @deprecated This method is used only for validation purpose and soon will be removed.
-     */
-    @Deprecated
-    private static ToFolderLabelState convertFolderLabelState(ToState toState) {
-        switch (toState) {
-            case UNCHANGED:
-                return ToFolderLabelState.UNCHANGED;
-            case TO_SUGGESTION0:
-                return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
-            case TO_SUGGESTION1_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY;
-            case TO_SUGGESTION1_WITH_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
-            case TO_SUGGESTION2_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY;
-            case TO_SUGGESTION2_WITH_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
-            case TO_SUGGESTION3_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY;
-            case TO_SUGGESTION3_WITH_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
-            case TO_EMPTY_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY;
-            case TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
-            case TO_EMPTY_WITH_EMPTY_SUGGESTIONS:
-                return ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
-            case TO_EMPTY_WITH_SUGGESTIONS_DISABLED:
-                return ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
-            case TO_CUSTOM_WITH_VALID_PRIMARY:
-                return ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY;
-            case TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
-                return ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
-            case TO_CUSTOM_WITH_EMPTY_SUGGESTIONS:
-                return ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
-            case TO_CUSTOM_WITH_SUGGESTIONS_DISABLED:
-                return ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
-            default:
-                return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index b9b3d38..7091d2b 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -24,15 +24,16 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS;
 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_TASKFOREGROUND;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
 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;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
+import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
 
 import android.content.ComponentName;
 import android.content.ContentValues;
@@ -51,9 +52,10 @@
 import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
 import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
 import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
+import com.android.launcher3.logger.LauncherAtom.Shortcut;
 import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
-import com.android.launcher3.logger.LauncherAtom.TaskForegroundContainer;
 import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
+import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.util.ContentWriter;
 
@@ -64,8 +66,10 @@
  */
 public class ItemInfo {
 
-    public static final boolean DEBUG = true;
+    public static final boolean DEBUG = false;
     public static final int NO_ID = -1;
+    // An id that doesn't match any item, including predicted apps with have an id=NO_ID
+    public static final int NO_MATCHING_ID = Integer.MIN_VALUE;
 
     /**
      * The id in the settings database for this item
@@ -164,6 +168,8 @@
         cellY = info.cellY;
         spanX = info.spanX;
         spanY = info.spanY;
+        minSpanX = info.minSpanX;
+        minSpanY = info.minSpanY;
         rank = info.rank;
         screenId = info.screenId;
         itemType = info.itemType;
@@ -182,6 +188,24 @@
         return Optional.ofNullable(getIntent()).map(Intent::getComponent).orElse(mComponentName);
     }
 
+    /**
+     * Returns this item's package name.
+     *
+     * Prioritizes the component package name, then uses the intent package name as a fallback.
+     * This ensures deep shortcuts are supported.
+     */
+    @Nullable
+    public String getTargetPackage() {
+        ComponentName component = getTargetComponent();
+        Intent intent = getIntent();
+
+        return component != null
+                ? component.getPackageName()
+                : intent != null
+                        ? intent.getPackage()
+                        : null;
+    }
+
     public void writeToValues(ContentWriter writer) {
         writer.put(LauncherSettings.Favorites.ITEM_TYPE, itemType)
                 .put(LauncherSettings.Favorites.CONTAINER, container)
@@ -225,7 +249,7 @@
     protected String dumpProperties() {
         return "id=" + id
                 + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
-                + " container=" + LauncherSettings.Favorites.containerToString(container)
+                + " container=" + getContainerInfo()
                 + " targetComponent=" + getTargetComponent()
                 + " screen=" + screenId
                 + " cell(" + cellX + "," + cellY + ")"
@@ -259,12 +283,6 @@
     }
 
     /**
-     * Can be overridden by inherited classes to fill in {@link LauncherAtom.ItemInfo}
-     */
-    public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
-    }
-
-    /**
      * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
      */
     public LauncherAtom.ItemInfo buildProto() {
@@ -287,6 +305,18 @@
                                 .orElse(LauncherAtom.Application.newBuilder()));
                 break;
             case ITEM_TYPE_DEEP_SHORTCUT:
+                itemBuilder
+                        .setShortcut(nullableComponent
+                                .map(component -> {
+                                    Shortcut.Builder lsb = Shortcut.newBuilder()
+                                            .setShortcutName(component.flattenToShortString());
+                                    Optional.ofNullable(getIntent())
+                                            .map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID))
+                                            .ifPresent(lsb::setShortcutId);
+                                    return lsb;
+                                })
+                                .orElse(LauncherAtom.Shortcut.newBuilder()));
+                break;
             case ITEM_TYPE_SHORTCUT:
                 itemBuilder
                         .setShortcut(nullableComponent
@@ -340,13 +370,16 @@
         return itemBuilder.build();
     }
 
-    LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
+    protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
         LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
         itemBuilder.setIsWork(user != Process.myUserHandle());
         return itemBuilder;
     }
 
-    protected ContainerInfo getContainerInfo() {
+    /**
+     * Returns {@link ContainerInfo} used when logging this item.
+     */
+    public ContainerInfo getContainerInfo() {
         switch (container) {
             case CONTAINER_HOTSEAT:
                 return ContainerInfo.newBuilder()
@@ -394,17 +427,23 @@
                 return ContainerInfo.newBuilder()
                         .setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance())
                         .build();
-            case CONTAINER_TASKFOREGROUND:
+            case EXTENDED_CONTAINERS:
                 return ContainerInfo.newBuilder()
-                        .setTaskForegroundContainer(TaskForegroundContainer.getDefaultInstance())
+                        .setExtendedContainers(getExtendedContainer())
                         .build();
-
-
         }
         return ContainerInfo.getDefaultInstance();
     }
 
     /**
+     * Returns non-AOSP container wrapped by {@link ExtendedContainers} object. Should be overridden
+     * by build variants.
+     */
+    protected ExtendedContainers getExtendedContainer() {
+        return ExtendedContainers.getDefaultInstance();
+    }
+
+    /**
      * Returns shallow copy of the object.
      */
     public ItemInfo makeShallowCopy() {
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index d95f94f..6813b97 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,7 +16,16 @@
 
 package com.android.launcher3.model.data;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.PackageManagerHelper;
 
 /**
  * Represents an ItemInfo which also holds an icon.
@@ -88,17 +97,42 @@
     public static final int FLAG_ICON_BADGED = 1 << 9;
 
     /**
+     * The icon is being installed. If {@link WorkspaceItemInfo#FLAG_RESTORED_ICON} or
+     * {@link WorkspaceItemInfo#FLAG_AUTOINSTALL_ICON} is set, then the icon is either being
+     * installed or is in a broken state.
+     */
+    public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 10;
+
+    /**
+     * This icon is still being downloaded.
+     */
+    public static final int FLAG_INCREMENTAL_DOWNLOAD_ACTIVE = 1 << 11;
+
+    public static final int FLAG_SHOW_DOWNLOAD_PROGRESS_MASK = FLAG_INSTALL_SESSION_ACTIVE
+            | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+
+    /**
      * Status associated with the system state of the underlying item. This is calculated every
      * time a new info is created and not persisted on the disk.
      */
     public int runtimeStatusFlags = 0;
 
+    /**
+     * The download progress of the package that this shortcut represents. For legacy apps, this
+     * will always be the installation progress. For apps that support incremental downloads, this
+     * will only match be the installation progress until the app is installed, then this will the
+     * total download progress.
+     */
+    private int mProgressLevel = 100;
+
     protected ItemInfoWithIcon() { }
 
     protected ItemInfoWithIcon(ItemInfoWithIcon info) {
         super(info);
         bitmap = info.bitmap;
+        mProgressLevel = info.mProgressLevel;
         runtimeStatusFlags = info.runtimeStatusFlags;
+        user = info.user;
     }
 
     @Override
@@ -114,7 +148,91 @@
     }
 
     /**
+     * Returns whether the app this shortcut represents is able to be started. For legacy apps,
+     * this returns whether it is fully installed. For apps that support incremental downloads,
+     * this returns whether the app is either fully downloaded or has installed and is downloading
+     * incrementally.
+     */
+    public boolean isAppStartable() {
+        return ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) == 0)
+                && (((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0)
+                    || mProgressLevel == 100);
+    }
+
+    /**
+     * Returns the download progress for the app this shortcut represents. If this app is not yet
+     * installed or does not support incremental downloads, this will return the installation
+     * progress.
+     */
+    public int getProgressLevel() {
+        if ((runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+            return mProgressLevel;
+        }
+        return 100;
+    }
+
+    /**
+     * Sets the download progress for the app this shortcut represents. If this app is not yet
+     * installed or does not support incremental downloads, this will set
+     * {@code FLAG_INSTALL_SESSION_ACTIVE}. If this app is downloading incrementally, this will
+     * set {@code FLAG_INCREMENTAL_DOWNLOAD_ACTIVE}. Otherwise, this will remove both flags.
+     */
+    public void setProgressLevel(PackageInstallInfo installInfo) {
+        setProgressLevel(installInfo.progress, installInfo.state);
+    }
+
+    /**
+     * Sets the download progress for the app this shortcut represents.
+     */
+    public void setProgressLevel(int progress, int status) {
+        if (status == PackageInstallInfo.STATUS_INSTALLING) {
+            mProgressLevel = progress;
+            runtimeStatusFlags = progress < 100
+                    ? runtimeStatusFlags | FLAG_INSTALL_SESSION_ACTIVE
+                    : runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+        } else if (status == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING) {
+            mProgressLevel = progress;
+            runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+            runtimeStatusFlags = progress < 100
+                    ? runtimeStatusFlags | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE
+                    : runtimeStatusFlags & ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        } else {
+            mProgressLevel = status == PackageInstallInfo.STATUS_INSTALLED ? 100 : 0;
+            runtimeStatusFlags &= ~FLAG_INSTALL_SESSION_ACTIVE;
+            runtimeStatusFlags &= ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+        }
+    }
+
+    /** Creates an intent to that launches the app store at this app's page. */
+    @Nullable
+    public Intent getMarketIntent(Context context) {
+        ComponentName componentName = getTargetComponent();
+
+        return componentName != null
+                ? new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName())
+                : null;
+    }
+
+    /**
      * @return a copy of this
      */
     public abstract ItemInfoWithIcon clone();
+
+
+    /**
+     * Returns a FastBitmapDrawable with the icon.
+     */
+    public FastBitmapDrawable newIcon(Context context) {
+        return newIcon(context, false);
+    }
+
+    /**
+     * Returns a FastBitmapDrawable with the icon and context theme applied
+     */
+    public FastBitmapDrawable newIcon(Context context, boolean applyTheme) {
+        FastBitmapDrawable drawable = applyTheme
+                ? bitmap.newThemedIcon(context) : bitmap.newIcon(context);
+        drawable.setIsDisabled(isDisabled());
+        return drawable;
+    }
 }
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index b0d19a6..0283d5f 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -16,18 +16,28 @@
 
 package com.android.launcher3.model.data;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PIN_WIDGETS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
 import android.appwidget.AppWidgetHostView;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.os.Process;
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.util.WidgetSizes;
 
 /**
  * Represents a widget (either instantiated or about to be) in the Launcher.
@@ -82,6 +92,18 @@
     public static final int CUSTOM_WIDGET_ID = -100;
 
     /**
+     * Flags for recording all the features that a widget has enabled.
+     * @see widgetFeatures
+     */
+    public static final int FEATURE_RECONFIGURABLE = 1;
+    public static final int FEATURE_OPTIONAL_CONFIGURATION = 1 << 1;
+    public static final int FEATURE_PREVIEW_LAYOUT = 1 << 2;
+    public static final int FEATURE_TARGET_CELL_SIZE = 1 << 3;
+    public static final int FEATURE_MIN_SIZE = 1 << 4;
+    public static final int FEATURE_MAX_SIZE = 1 << 5;
+    public static final int FEATURE_ROUNDED_CORNERS = 1 << 6;
+
+    /**
      * Identifier for this widget when talking with
      * {@link android.appwidget.AppWidgetManager} for updates.
      */
@@ -114,8 +136,19 @@
      */
     public PackageItemInfo pendingItemInfo;
 
+    /**
+     * Contains a binary representation indicating which widget features are enabled. This value is
+     * -1 if widget features could not be identified.
+     */
+    private int widgetFeatures;
+
     private boolean mHasNotifiedInitialWidgetSizeChanged;
 
+    /**
+     * The container from which this widget was added (e.g. widgets tray, pin widget, search)
+     */
+    public int sourceContainer = LauncherSettings.Favorites.CONTAINER_UNKNOWN;
+
     public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
         this.appWidgetId = appWidgetId;
         this.providerName = providerName;
@@ -130,11 +163,18 @@
         // to indicate that they should be calculated based on the layout and minWidth/minHeight
         spanX = -1;
         spanY = -1;
+        widgetFeatures = -1;
         // We only support app widgets on current user.
         user = Process.myUserHandle();
         restoreStatus = RESTORE_COMPLETED;
     }
 
+    public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName,
+            LauncherAppWidgetProviderInfo providerInfo, AppWidgetHostView hostView) {
+        this(appWidgetId, providerName);
+        widgetFeatures = computeWidgetFeatures(providerInfo, hostView);
+    }
+
     /** Used for testing **/
     public LauncherAppWidgetInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
@@ -157,7 +197,8 @@
                 .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString())
                 .put(LauncherSettings.Favorites.RESTORED, restoreStatus)
                 .put(LauncherSettings.Favorites.OPTIONS, options)
-                .put(LauncherSettings.Favorites.INTENT, bindOptions);
+                .put(LauncherSettings.Favorites.INTENT, bindOptions)
+                .put(LauncherSettings.Favorites.APPWIDGET_SOURCE, sourceContainer);
     }
 
     /**
@@ -166,7 +207,7 @@
      */
     public void onBindAppWidget(Launcher launcher, AppWidgetHostView hostView) {
         if (!mHasNotifiedInitialWidgetSizeChanged) {
-            AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
+            WidgetSizes.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
             mHasNotifiedInitialWidgetSizeChanged = true;
         }
     }
@@ -196,12 +237,58 @@
         return (options & option) != 0;
     }
 
+    @SuppressWarnings("NewApi")
+    private static int computeWidgetFeatures(
+            LauncherAppWidgetProviderInfo providerInfo, AppWidgetHostView hostView) {
+        int widgetFeatures = 0;
+        if (providerInfo.isReconfigurable()) {
+            widgetFeatures |= FEATURE_RECONFIGURABLE;
+        }
+        if (providerInfo.isConfigurationOptional()) {
+            widgetFeatures |= FEATURE_OPTIONAL_CONFIGURATION;
+        }
+        if (ATLEAST_S && providerInfo.previewLayout != Resources.ID_NULL) {
+            widgetFeatures |= FEATURE_PREVIEW_LAYOUT;
+        }
+        if (ATLEAST_S && providerInfo.targetCellWidth > 0 || providerInfo.targetCellHeight > 0) {
+            widgetFeatures |= FEATURE_TARGET_CELL_SIZE;
+        }
+        if (providerInfo.minResizeWidth > 0 || providerInfo.minResizeHeight > 0) {
+            widgetFeatures |= FEATURE_MIN_SIZE;
+        }
+        if (ATLEAST_S && providerInfo.maxResizeWidth > 0 || providerInfo.maxResizeHeight > 0) {
+            widgetFeatures |= FEATURE_MAX_SIZE;
+        }
+        if (hostView instanceof LauncherAppWidgetHostView &&
+                ((LauncherAppWidgetHostView) hostView).hasEnforcedCornerRadius()) {
+            widgetFeatures |= FEATURE_ROUNDED_CORNERS;
+        }
+        return widgetFeatures;
+    }
+
+    public static LauncherAtom.Attribute getAttribute(int container) {
+        switch (container) {
+            case CONTAINER_WIDGETS_TRAY:
+                return LauncherAtom.Attribute.WIDGETS;
+            case CONTAINER_BOTTOM_WIDGETS_TRAY:
+                return LauncherAtom.Attribute.WIDGETS_BOTTOM_TRAY;
+            case CONTAINER_PIN_WIDGETS:
+                return LauncherAtom.Attribute.PINITEM;
+            case CONTAINER_WIDGETS_PREDICTION:
+                return LauncherAtom.Attribute.WIDGETS_TRAY_PREDICTION;
+            case CONTAINER_ALL_APPS:
+                return LauncherAtom.Attribute.ALL_APPS_SEARCH_RESULT_WIDGETS;
+            default:
+                return LauncherAtom.Attribute.UNKNOWN;
+        }
+    }
+
     @Override
-    public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
-        builder.setWidget(LauncherAtom.Widget.newBuilder()
-                .setSpanX(spanX)
-                .setSpanY(spanY)
-                .setComponentName(providerName.toString())
-                .setPackageName(providerName.getPackageName()));
+    public LauncherAtom.ItemInfo buildProto(FolderInfo folderInfo) {
+        LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
+        return info.toBuilder()
+                .setWidget(info.getWidget().toBuilder().setWidgetFeatures(widgetFeatures))
+                .setAttribute(getAttribute(sourceContainer))
+                .build();
     }
 }
diff --git a/src/com/android/launcher3/model/data/PackageItemInfo.java b/src/com/android/launcher3/model/data/PackageItemInfo.java
index b70d0d4..a81fe6a 100644
--- a/src/com/android/launcher3/model/data/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/data/PackageItemInfo.java
@@ -16,27 +16,47 @@
 
 package com.android.launcher3.model.data;
 
+import androidx.annotation.IntDef;
+
 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 String packageName;
+    public final String packageName;
+
+    /** Represents a widget category shown in the widget tray section. */
+    @Category public final int category;
 
     public PackageItemInfo(String packageName) {
+        this(packageName, NO_CATEGORY);
+    }
+
+    public PackageItemInfo(String packageName, @Category int category) {
         this.packageName = packageName;
+        this.category = category;
         this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
     }
 
     public PackageItemInfo(PackageItemInfo copy) {
         this.packageName = copy.packageName;
+        this.category = copy.category;
         this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
     }
 
@@ -60,6 +80,6 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(packageName);
+        return Objects.hash(packageName, user);
     }
 }
diff --git a/src/com/android/launcher3/model/data/PromiseAppInfo.java b/src/com/android/launcher3/model/data/PromiseAppInfo.java
deleted file mode 100644
index b6231ed..0000000
--- a/src/com/android/launcher3/model/data/PromiseAppInfo.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.model.data;
-
-import android.content.Context;
-import android.content.Intent;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.pm.PackageInstallInfo;
-import com.android.launcher3.util.PackageManagerHelper;
-
-public class PromiseAppInfo extends AppInfo {
-
-    public int level = 0;
-
-    public PromiseAppInfo(@NonNull PackageInstallInfo installInfo) {
-        componentName = installInfo.componentName;
-        intent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_LAUNCHER)
-                .setComponent(componentName)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-    }
-
-    @Override
-    public WorkspaceItemInfo makeWorkspaceItem() {
-        WorkspaceItemInfo shortcut = new WorkspaceItemInfo(this);
-        shortcut.setInstallProgress(level);
-        // We need to update the component name when the apk is installed
-        shortcut.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
-        // Since the user is manually placing it on homescreen, it should not be auto-removed later
-        shortcut.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
-        return shortcut;
-    }
-
-    public Intent getMarketIntent(Context context) {
-        return new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName());
-    }
-}
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
new file mode 100644
index 0000000..b3057d5
--- /dev/null
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -0,0 +1,137 @@
+/*
+ * 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 com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Process;
+import android.os.UserHandle;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.logger.LauncherAtom.ItemInfo;
+import com.android.launcher3.logger.LauncherAtom.SearchActionItem;
+
+/**
+ * Represents a SearchAction with in launcher
+ */
+public class SearchActionItemInfo extends ItemInfoWithIcon {
+
+    public static final int FLAG_SHOULD_START = 1 << 1;
+    public static final int FLAG_SHOULD_START_FOR_RESULT = FLAG_SHOULD_START | 1 << 2;
+    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;
+
+    private final String mFallbackPackageName;
+    private int mFlags = 0;
+    private final Icon mIcon;
+
+    // If true title does not contain any personal info and eligible for logging.
+    private final boolean mIsPersonalTitle;
+    private Intent mIntent;
+
+    private PendingIntent mPendingIntent;
+
+    public SearchActionItemInfo(Icon icon, String packageName, UserHandle user,
+            CharSequence title, boolean isPersonalTitle) {
+        mIsPersonalTitle = isPersonalTitle;
+        this.user = user == null ? Process.myUserHandle() : user;
+        this.title = title;
+        this.container = EXTENDED_CONTAINERS;
+        mFallbackPackageName = packageName;
+        mIcon = icon;
+    }
+
+    public 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;
+    }
+
+    /**
+     * Returns if multiple flags are all available.
+     */
+    public boolean hasFlags(int flags) {
+        return (mFlags & flags) != 0;
+    }
+
+    public void setFlags(int flags) {
+        mFlags |= flags ;
+    }
+
+    @Override
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    /**
+     * Setter for mIntent with assertion for null value mPendingIntent
+     */
+    public void setIntent(Intent intent) {
+        if (mPendingIntent != null && intent != null) {
+            throw new RuntimeException(
+                    "SearchActionItemInfo can only have either an Intent or a PendingIntent");
+        }
+        mIntent = intent;
+    }
+
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
+    /**
+     * Setter of mPendingIntent with assertion for null value mIntent
+     */
+    public void setPendingIntent(PendingIntent pendingIntent) {
+        if (mIntent != null && pendingIntent != null) {
+            throw new RuntimeException(
+                    "SearchActionItemInfo can only have either an Intent or a PendingIntent");
+        }
+        mPendingIntent = pendingIntent;
+    }
+
+    @Nullable
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    @Override
+    public ItemInfoWithIcon clone() {
+        return new SearchActionItemInfo(this);
+    }
+
+    @Override
+    public ItemInfo buildProto(FolderInfo fInfo) {
+        SearchActionItem.Builder itemBuilder = SearchActionItem.newBuilder()
+                .setPackageName(mFallbackPackageName);
+
+        if (!mIsPersonalTitle) {
+            itemBuilder.setTitle(title.toString());
+        }
+        return getDefaultItemInfoBuilder()
+                .setSearchActionItem(itemBuilder)
+                .setContainerInfo(getContainerInfo())
+                .build();
+    }
+}
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index a7bf1f3..690e904 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -58,20 +58,14 @@
     public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
 
     /**
-     * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
-     * is set, then the icon is either being installed or is in a broken state.
-     */
-    public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
-
-    /**
      * Indicates that the widget restore has started.
      */
-    public static final int FLAG_RESTORE_STARTED = 1 << 3;
+    public static final int FLAG_RESTORE_STARTED = 1 << 2;
 
     /**
      * Web UI supported.
      */
-    public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
+    public static final int FLAG_SUPPORTS_WEB_UI = 1 << 3;
 
     /**
      * The intent used to start the application.
@@ -98,11 +92,6 @@
      */
     @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
 
-    /**
-     * The installation progress [0-100] of the package that this shortcut represents.
-     */
-    private int mInstallProgress;
-
 
     public WorkspaceItemInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -114,7 +103,6 @@
         intent = new Intent(info.intent);
         iconResource = info.iconResource;
         status = info.status;
-        mInstallProgress = info.mInstallProgress;
         personKeys = info.personKeys.clone();
     }
 
@@ -168,15 +156,6 @@
         return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
     }
 
-    public int getInstallProgress() {
-        return mInstallProgress;
-    }
-
-    public void setInstallProgress(int progress) {
-        mInstallProgress = progress;
-        status |= FLAG_INSTALL_SESSION_ACTIVE;
-    }
-
     public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
         // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
         intent = ShortcutKey.makeIntent(shortcutInfo);
@@ -216,7 +195,7 @@
         if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT || hasStatusFlag(
                 FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON))) {
             // Legacy shortcuts and promise icons with web UI may not have a componentName but just
-            // a packageName. In that case create a dummy componentName instead of adding additional
+            // a packageName. In that case create a empty componentName instead of adding additional
             // check everywhere.
             String pkg = intent.getPackage();
             return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
deleted file mode 100644
index fd3d41a..0000000
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.notification;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.anim.PropertyResetListener;
-import com.android.launcher3.util.Themes;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * A {@link FrameLayout} that contains only icons of notifications.
- * If there are more than {@link #MAX_FOOTER_NOTIFICATIONS} icons, we add a "..." overflow.
- */
-public class NotificationFooterLayout extends FrameLayout {
-
-    public interface IconAnimationEndListener {
-        void onIconAnimationEnd(NotificationInfo animatedNotification);
-    }
-
-    private static final int MAX_FOOTER_NOTIFICATIONS = 5;
-
-    private static final Rect sTempRect = new Rect();
-
-    private final List<NotificationInfo> mNotifications = new ArrayList<>();
-    private final List<NotificationInfo> mOverflowNotifications = new ArrayList<>();
-    private final boolean mRtl;
-    private final int mBackgroundColor;
-
-    FrameLayout.LayoutParams mIconLayoutParams;
-    private View mOverflowEllipsis;
-    private LinearLayout mIconRow;
-    private NotificationItemView mContainer;
-
-    public NotificationFooterLayout(Context context) {
-        this(context, null, 0);
-    }
-
-    public NotificationFooterLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public NotificationFooterLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        Resources res = getResources();
-        mRtl = Utilities.isRtl(res);
-
-        int iconSize = res.getDimensionPixelSize(R.dimen.notification_footer_icon_size);
-        mIconLayoutParams = new LayoutParams(iconSize, iconSize);
-        mIconLayoutParams.gravity = Gravity.CENTER_VERTICAL;
-        setWidth((int) res.getDimension(R.dimen.bg_popup_item_width));
-        mBackgroundColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
-    }
-
-
-    /**
-     * Compute margin start for each icon such that the icons between the first one and the ellipsis
-     * are evenly spaced out.
-     */
-    public void setWidth(int width) {
-        if (getLayoutParams() != null) {
-            getLayoutParams().width = width;
-        }
-        Resources res = getResources();
-        int iconSize = res.getDimensionPixelSize(R.dimen.notification_footer_icon_size);
-
-        int paddingEnd = res.getDimensionPixelSize(R.dimen.notification_footer_icon_row_padding);
-        int ellipsisSpace = res.getDimensionPixelSize(R.dimen.horizontal_ellipsis_offset)
-                + res.getDimensionPixelSize(R.dimen.horizontal_ellipsis_size);
-        int availableIconRowSpace = width - paddingEnd - ellipsisSpace
-                - iconSize * MAX_FOOTER_NOTIFICATIONS;
-        mIconLayoutParams.setMarginStart(availableIconRowSpace / MAX_FOOTER_NOTIFICATIONS);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mOverflowEllipsis = findViewById(R.id.overflow);
-        mIconRow = findViewById(R.id.icon_row);
-    }
-
-    void setContainer(NotificationItemView container) {
-        mContainer = container;
-    }
-
-    /**
-     * Keep track of the NotificationInfo, and then update the UI when
-     * {@link #commitNotificationInfos()} is called.
-     */
-    public void addNotificationInfo(final NotificationInfo notificationInfo) {
-        if (mNotifications.size() < MAX_FOOTER_NOTIFICATIONS) {
-            mNotifications.add(notificationInfo);
-        } else {
-            mOverflowNotifications.add(notificationInfo);
-        }
-    }
-
-    /**
-     * Adds icons and potentially overflow text for all of the NotificationInfo's
-     * added using {@link #addNotificationInfo(NotificationInfo)}.
-     */
-    public void commitNotificationInfos() {
-        mIconRow.removeAllViews();
-
-        for (int i = 0; i < mNotifications.size(); i++) {
-            NotificationInfo info = mNotifications.get(i);
-            addNotificationIconForInfo(info);
-        }
-        updateOverflowEllipsisVisibility();
-    }
-
-    private void updateOverflowEllipsisVisibility() {
-        mOverflowEllipsis.setVisibility(mOverflowNotifications.isEmpty() ? GONE : VISIBLE);
-    }
-
-    /**
-     * Creates an icon for the given NotificationInfo, and adds it to the icon row.
-     * @return the icon view that was added
-     */
-    private View addNotificationIconForInfo(NotificationInfo info) {
-        View icon = new View(getContext());
-        icon.setBackground(info.getIconForBackground(getContext(), mBackgroundColor));
-        icon.setOnClickListener(info);
-        icon.setTag(info);
-        icon.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-        mIconRow.addView(icon, 0, mIconLayoutParams);
-        return icon;
-    }
-
-    public void animateFirstNotificationTo(Rect toBounds,
-            final IconAnimationEndListener callback) {
-        AnimatorSet animation = new AnimatorSet();
-        final View firstNotification = mIconRow.getChildAt(mIconRow.getChildCount() - 1);
-
-        Rect fromBounds = sTempRect;
-        firstNotification.getGlobalVisibleRect(fromBounds);
-        float scale = (float) toBounds.height() / fromBounds.height();
-        Animator moveAndScaleIcon = new PropertyListBuilder().scale(scale)
-                .translationY(toBounds.top - fromBounds.top
-                        + (fromBounds.height() * scale - fromBounds.height()) / 2)
-                .build(firstNotification);
-        moveAndScaleIcon.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                callback.onIconAnimationEnd((NotificationInfo) firstNotification.getTag());
-                removeViewFromIconRow(firstNotification);
-            }
-        });
-        animation.play(moveAndScaleIcon);
-
-        // Shift all notifications (not the overflow) over to fill the gap.
-        int gapWidth = mIconLayoutParams.width + mIconLayoutParams.getMarginStart();
-        if (mRtl) {
-            gapWidth = -gapWidth;
-        }
-        if (!mOverflowNotifications.isEmpty()) {
-            NotificationInfo notification = mOverflowNotifications.remove(0);
-            mNotifications.add(notification);
-            View iconFromOverflow = addNotificationIconForInfo(notification);
-            animation.play(ObjectAnimator.ofFloat(iconFromOverflow, ALPHA, 0, 1));
-        }
-        int numIcons = mIconRow.getChildCount() - 1; // All children besides the one leaving.
-        // We have to reset the translation X to 0 when the new main notification
-        // is removed from the footer.
-        PropertyResetListener<View, Float> propertyResetListener
-                = new PropertyResetListener<>(TRANSLATION_X, 0f);
-        for (int i = 0; i < numIcons; i++) {
-            final View child = mIconRow.getChildAt(i);
-            Animator shiftChild = ObjectAnimator.ofFloat(child, TRANSLATION_X, gapWidth);
-            shiftChild.addListener(propertyResetListener);
-            animation.play(shiftChild);
-        }
-        animation.start();
-    }
-
-    private void removeViewFromIconRow(View child) {
-        mIconRow.removeView(child);
-        mNotifications.remove(child.getTag());
-        updateOverflowEllipsisVisibility();
-        if (mIconRow.getChildCount() == 0) {
-            // There are no more icons in the footer, so hide it.
-            if (mContainer != null) {
-                mContainer.removeFooter();
-            }
-        }
-    }
-
-    public void trimNotifications(List<String> notifications) {
-        if (!isAttachedToWindow() || mIconRow.getChildCount() == 0) {
-            return;
-        }
-        Iterator<NotificationInfo> overflowIterator = mOverflowNotifications.iterator();
-        while (overflowIterator.hasNext()) {
-            if (!notifications.contains(overflowIterator.next().notificationKey)) {
-                overflowIterator.remove();
-            }
-        }
-        for (int i = mIconRow.getChildCount() - 1; i >= 0; i--) {
-            View child = mIconRow.getChildAt(i);
-            NotificationInfo childInfo = (NotificationInfo) child.getTag();
-            if (!notifications.contains(childInfo.notificationKey)) {
-                removeViewFromIconRow(child);
-            }
-        }
-    }
-}
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 835f72d..d27d8c7 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -22,7 +22,6 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -86,9 +85,8 @@
             mIsIconLarge = true;
         }
         if (mIconDrawable == null) {
-            mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
-                    .getInstance(context).getIconCache()
-                    .getDefaultIcon(statusBarNotification.getUser()).icon);
+            mIconDrawable = LauncherAppState.getInstance(context).getIconCache()
+                    .getDefaultIcon(statusBarNotification.getUser()).newIcon(context);
         }
         intent = notification.contentIntent;
         autoCancel = (notification.flags & Notification.FLAG_AUTO_CANCEL) != 0;
@@ -106,7 +104,6 @@
                 view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
         try {
             intent.send(null, 0, null, null, null, null, activityOptions);
-            launcher.getUserEventDispatcher().logNotificationLaunch(view, intent);
             launcher.getStatsLogManager().logger().withItemInfo(mItemInfo)
                     .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP);
         } catch (PendingIntent.CanceledException e) {
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 0320aa3..af943a6 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -16,23 +16,22 @@
 
 package com.android.launcher3.notification;
 
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
-
-import android.app.Notification;
+import android.animation.AnimatorSet;
 import android.content.Context;
-import android.graphics.Color;
+import android.graphics.Outline;
 import android.graphics.Rect;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewOutlineProvider;
 import android.widget.TextView;
 
 import com.android.launcher3.R;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
 import com.android.launcher3.util.Themes;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -43,60 +42,51 @@
     private static final Rect sTempRect = new Rect();
 
     private final Context mContext;
-    private final PopupContainerWithArrow mContainer;
+    private final PopupContainerWithArrow mPopupContainer;
+    private final ViewGroup mRootView;
 
-    private final TextView mHeaderText;
     private final TextView mHeaderCount;
     private final NotificationMainView mMainView;
-    private final NotificationFooterLayout mFooter;
-    private final SingleAxisSwipeDetector mSwipeDetector;
-    private final View mIconView;
 
     private final View mHeader;
-    private final View mDivider;
 
     private View mGutter;
 
     private boolean mIgnoreTouch = false;
-    private boolean mAnimatingNextIcon;
-    private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT;
+    private List<NotificationInfo> mNotificationInfos = new ArrayList<>();
 
-    public NotificationItemView(PopupContainerWithArrow container) {
-        mContainer = container;
+    public NotificationItemView(PopupContainerWithArrow container, ViewGroup rootView) {
+        mPopupContainer = container;
+        mRootView = rootView;
         mContext = container.getContext();
 
-        mHeaderText = container.findViewById(R.id.notification_text);
         mHeaderCount = container.findViewById(R.id.notification_count);
         mMainView = container.findViewById(R.id.main_view);
-        mFooter = container.findViewById(R.id.footer);
-        mIconView = container.findViewById(R.id.popup_item_icon);
 
         mHeader = container.findViewById(R.id.header);
-        mDivider = container.findViewById(R.id.divider);
 
-        mSwipeDetector = new SingleAxisSwipeDetector(mContext, mMainView, HORIZONTAL);
-        mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_BOTH, false);
-        mMainView.setSwipeDetector(mSwipeDetector);
-        mFooter.setContainer(this);
+        float radius = Themes.getDialogCornerRadius(mContext);
+        rootView.setClipToOutline(true);
+        rootView.setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), radius);
+            }
+        });
+    }
+
+    /**
+     * Animates the background color to a new color.
+     * @param color The color to change to.
+     * @param animatorSetOut The AnimatorSet where we add the color animator to.
+     */
+    public void updateBackgroundColor(int color, AnimatorSet animatorSetOut) {
+        mMainView.updateBackgroundColor(color, animatorSetOut);
     }
 
     public void addGutter() {
         if (mGutter == null) {
-            mGutter = mContainer.inflateAndAdd(R.layout.notification_gutter, mContainer);
-        }
-    }
-
-    /**
-     * Sets width for notification footer and spaces out items evenly
-     */
-    public void setFooterWidth(int footerWidth) {
-        mFooter.setWidth(footerWidth);
-    }
-
-    public void removeFooter() {
-        if (mContainer.indexOfChild(mFooter) >= 0) {
-            mContainer.removeView(mFooter);
-            mContainer.removeView(mDivider);
+            mGutter = mPopupContainer.inflateAndAdd(R.layout.notification_gutter, mRootView);
         }
     }
 
@@ -108,39 +98,39 @@
     }
 
     public void removeAllViews() {
-        mContainer.removeView(mMainView);
-        mContainer.removeView(mHeader);
-
-        if (mContainer.indexOfChild(mFooter) >= 0) {
-            mContainer.removeView(mFooter);
-            mContainer.removeView(mDivider);
-        }
-
+        mRootView.removeView(mMainView);
+        mRootView.removeView(mHeader);
         if (mGutter != null) {
-            mContainer.removeView(mGutter);
+            mRootView.removeView(mGutter);
         }
     }
 
-    public void updateHeader(int notificationCount, int iconColor) {
-        mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount));
-        if (Color.alpha(iconColor) > 0) {
-            if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
-                mNotificationHeaderTextColor =
-                        IconPalette.resolveContrastColor(mContext, iconColor,
-                                Themes.getAttrColor(mContext, R.attr.popupColorPrimary));
-            }
-            mHeaderText.setTextColor(mNotificationHeaderTextColor);
-            mHeaderCount.setTextColor(mNotificationHeaderTextColor);
+    /**
+     * Updates the header text.
+     * @param notificationCount The number of notifications.
+     */
+    public void updateHeader(int notificationCount) {
+        final String text;
+        final int visibility;
+        if (notificationCount <= 1) {
+            text = "";
+            visibility = View.INVISIBLE;
+        } else {
+            text = String.valueOf(notificationCount);
+            visibility = View.VISIBLE;
+
         }
+        mHeaderCount.setText(text);
+        mHeaderCount.setVisibility(visibility);
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            sTempRect.set(mMainView.getLeft(), mMainView.getTop(),
-                    mMainView.getRight(), mMainView.getBottom());
+            sTempRect.set(mRootView.getLeft(), mRootView.getTop(),
+                    mRootView.getRight(), mRootView.getBottom());
             mIgnoreTouch = !sTempRect.contains((int) ev.getX(), (int) ev.getY());
             if (!mIgnoreTouch) {
-                mContainer.getParent().requestDisallowInterceptTouchEvent(true);
+                mPopupContainer.getParent().requestDisallowInterceptTouchEvent(true);
             }
         }
         if (mIgnoreTouch) {
@@ -151,53 +141,39 @@
             return false;
         }
 
-        mSwipeDetector.onTouchEvent(ev);
-        return mSwipeDetector.isDraggingOrSettling();
-    }
-
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mIgnoreTouch) {
-            return false;
-        }
-        if (mMainView.getNotificationInfo() == null) {
-            // The notification hasn't been populated yet.
-            return false;
-        }
-        return mSwipeDetector.onTouchEvent(ev);
+        return false;
     }
 
     public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) {
+        mNotificationInfos.clear();
         if (notificationInfos.isEmpty()) {
             return;
         }
+        mNotificationInfos.addAll(notificationInfos);
 
         NotificationInfo mainNotification = notificationInfos.get(0);
         mMainView.applyNotificationInfo(mainNotification, false);
-
-        for (int i = 1; i < notificationInfos.size(); i++) {
-            mFooter.addNotificationInfo(notificationInfos.get(i));
-        }
-        mFooter.commitNotificationInfos();
     }
 
     public void trimNotifications(final List<String> notificationKeys) {
-        boolean dismissedMainNotification = !notificationKeys.contains(
-                mMainView.getNotificationInfo().notificationKey);
-        if (dismissedMainNotification && !mAnimatingNextIcon) {
-            // Animate the next icon into place as the new main notification.
-            mAnimatingNextIcon = true;
-            mMainView.setContentVisibility(View.INVISIBLE);
-            mMainView.setContentTranslation(0);
-            mIconView.getGlobalVisibleRect(sTempRect);
-            mFooter.animateFirstNotificationTo(sTempRect, (newMainNotification) -> {
-                if (newMainNotification != null) {
-                    mMainView.applyNotificationInfo(newMainNotification, true);
-                    mMainView.setContentVisibility(View.VISIBLE);
+        NotificationInfo currentMainNotificationInfo = mMainView.getNotificationInfo();
+        boolean shouldUpdateMainNotification = !notificationKeys.contains(
+                currentMainNotificationInfo.notificationKey);
+
+        if (shouldUpdateMainNotification) {
+            int size = notificationKeys.size();
+            NotificationInfo nextNotification = null;
+            // We get the latest notification by finding the notification after the one that was
+            // just dismissed.
+            for (int i = 0; i < size; ++i) {
+                if (currentMainNotificationInfo == mNotificationInfos.get(i) && i + 1 < size) {
+                    nextNotification = mNotificationInfos.get(i + 1);
+                    break;
                 }
-                mAnimatingNextIcon = false;
-            });
-        } else {
-            mFooter.trimNotifications(notificationKeys);
+            }
+            if (nextNotification != null) {
+                mMainView.applyNotificationInfo(nextNotification, true);
+            }
         }
     }
 }
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index a1917ec..1dda3df 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -30,9 +30,9 @@
 
 /**
  * The key data associated with the notification, used to determine what to include
- * in dots and dummy popup views before they are populated.
+ * in dots and stub popup views before they are populated.
  *
- * @see NotificationInfo for the full data used when populating the dummy views.
+ * @see NotificationInfo for the full data used when populating the stub views.
  */
 public class NotificationKeyData {
     public final String notificationKey;
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 059ad18..e58f5fa 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -18,7 +18,7 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
 
 import android.annotation.TargetApi;
 import android.app.Notification;
@@ -38,7 +38,7 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.SecureSettingsObserver;
+import com.android.launcher3.util.SettingsCache;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -81,7 +81,8 @@
     /** The last notification key that was dismissed from launcher UI */
     private String mLastKeyDismissedByLauncher;
 
-    private SecureSettingsObserver mNotificationDotsObserver;
+    private SettingsCache mSettingsCache;
+    private SettingsCache.OnChangeListener mNotificationSettingsChangedListener;
 
     public NotificationListener() {
         mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage);
@@ -207,10 +208,12 @@
         super.onListenerConnected();
         sIsConnected = true;
 
-        mNotificationDotsObserver =
-                newNotificationSettingsObserver(this, this::onNotificationSettingsChanged);
-        mNotificationDotsObserver.register();
-        mNotificationDotsObserver.dispatchOnChange();
+        // Register an observer to rebind the notification listener when dots are re-enabled.
+        mSettingsCache = SettingsCache.INSTANCE.get(this);
+        mNotificationSettingsChangedListener = this::onNotificationSettingsChanged;
+        mSettingsCache.register(NOTIFICATION_BADGING_URI,
+                mNotificationSettingsChangedListener);
+        onNotificationSettingsChanged(mSettingsCache.getValue(NOTIFICATION_BADGING_URI));
 
         onNotificationFullRefresh();
     }
@@ -229,7 +232,7 @@
     public void onListenerDisconnected() {
         super.onListenerDisconnected();
         sIsConnected = false;
-        mNotificationDotsObserver.unregister();
+        mSettingsCache.unregister(NOTIFICATION_BADGING_URI, mNotificationSettingsChangedListener);
         onNotificationFullRefresh();
     }
 
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index b03aa9c..b8aa824 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -16,15 +16,15 @@
 
 package com.android.launcher3.notification;
 
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DISMISSED;
 
-import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.content.res.ColorStateList;
+import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.RippleDrawable;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -36,20 +36,15 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.touch.BaseSwipeDetector;
-import com.android.launcher3.touch.OverScroll;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.Themes;
 
 /**
  * A {@link android.widget.FrameLayout} that contains a single notification,
  * e.g. icon + title + text.
  */
 @TargetApi(Build.VERSION_CODES.N)
-public class NotificationMainView extends FrameLayout implements SingleAxisSwipeDetector.Listener {
+public class NotificationMainView extends FrameLayout {
 
     private static final FloatProperty<NotificationMainView> CONTENT_TRANSLATION =
             new FloatProperty<NotificationMainView>("contentTranslation") {
@@ -67,8 +62,6 @@
     // This is used only to track the notification view, so that it can be properly logged.
     public static final ItemInfo NOTIFICATION_ITEM_INFO = new ItemInfo();
 
-    private final ObjectAnimator mContentTranslateAnimator;
-
     private NotificationInfo mNotificationInfo;
     private ViewGroup mTextAndBackground;
     private int mBackgroundColor;
@@ -78,6 +71,8 @@
 
     private SingleAxisSwipeDetector mSwipeDetector;
 
+    private final ColorDrawable mColorDrawable;
+
     public NotificationMainView(Context context) {
         this(context, null, 0);
     }
@@ -89,7 +84,7 @@
     public NotificationMainView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mContentTranslateAnimator = ObjectAnimator.ofFloat(this, CONTENT_TRANSLATION, 0);
+        mColorDrawable = new ColorDrawable(Color.TRANSPARENT);
     }
 
     @Override
@@ -97,19 +92,37 @@
         super.onFinishInflate();
 
         mTextAndBackground = findViewById(R.id.text_and_background);
-        ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
-        mBackgroundColor = colorBackground.getColor();
-        RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
-                Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
-                colorBackground, null);
-        mTextAndBackground.setBackground(rippleBackground);
         mTitleView = mTextAndBackground.findViewById(R.id.title);
         mTextView = mTextAndBackground.findViewById(R.id.text);
         mIconView = findViewById(R.id.popup_item_icon);
+
+        ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
+        updateBackgroundColor(colorBackground.getColor());
     }
 
-    public void setSwipeDetector(SingleAxisSwipeDetector swipeDetector) {
-        mSwipeDetector = swipeDetector;
+    private void updateBackgroundColor(int color) {
+        mBackgroundColor = color;
+        mColorDrawable.setColor(color);
+        mTextAndBackground.setBackground(mColorDrawable);
+        if (mNotificationInfo != null) {
+            mIconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
+                    mBackgroundColor));
+        }
+    }
+
+    /**
+     * Animates the background color to a new color.
+     * @param color The color to change to.
+     * @param animatorSetOut The AnimatorSet where we add the color animator to.
+     */
+    public void updateBackgroundColor(int color, AnimatorSet animatorSetOut) {
+        int oldColor = mBackgroundColor;
+        ValueAnimator colors = ValueAnimator.ofArgb(oldColor, color);
+        colors.addUpdateListener(valueAnimator -> {
+            int newColor = (int) valueAnimator.getAnimatedValue();
+            updateBackgroundColor(newColor);
+        });
+        animatorSetOut.play(colors);
     }
 
     /**
@@ -137,7 +150,7 @@
             setOnClickListener(mNotificationInfo);
         }
         setContentTranslation(0);
-        // Add a dummy ItemInfo so that logging populates the correct container and item types
+        // Add a stub ItemInfo so that logging populates the correct container and item types
         // instead of DEFAULT_CONTAINERTYPE and DEFAULT_ITEMTYPE, respectively.
         setTag(NOTIFICATION_ITEM_INFO);
         if (animate) {
@@ -150,11 +163,6 @@
         mIconView.setTranslationX(translation);
     }
 
-    public void setContentVisibility(int visibility) {
-        mTextAndBackground.setVisibility(visibility);
-        mIconView.setVisibility(visibility);
-    }
-
     public NotificationInfo getNotificationInfo() {
         return mNotificationInfo;
     }
@@ -168,61 +176,6 @@
         Launcher launcher = Launcher.getLauncher(getContext());
         launcher.getPopupDataProvider().cancelNotification(
                 mNotificationInfo.notificationKey);
-        launcher.getUserEventDispatcher().logActionOnItem(
-                LauncherLogProto.Action.Touch.SWIPE,
-                LauncherLogProto.Action.Direction.RIGHT, // Assume all swipes are right for logging.
-                LauncherLogProto.ItemType.NOTIFICATION);
-    }
-
-    // SingleAxisSwipeDetector.Listener's
-    @Override
-    public void onDragStart(boolean start, float startDisplacement) { }
-
-
-    @Override
-    public boolean onDrag(float displacement) {
-        setContentTranslation(canChildBeDismissed()
-                ? displacement : OverScroll.dampedScroll(displacement, getWidth()));
-        mContentTranslateAnimator.cancel();
-        return true;
-    }
-
-    @Override
-    public void onDragEnd(float velocity) {
-        final boolean willExit;
-        final float endTranslation;
-        final float startTranslation = mTextAndBackground.getTranslationX();
-
-        if (!canChildBeDismissed()) {
-            willExit = false;
-            endTranslation = 0;
-        } else if (mSwipeDetector.isFling(velocity)) {
-            willExit = true;
-            endTranslation = velocity < 0 ? - getWidth() : getWidth();
-        } else if (Math.abs(startTranslation) > getWidth() / 2) {
-            willExit = true;
-            endTranslation = (startTranslation < 0 ? -getWidth() : getWidth());
-        } else {
-            willExit = false;
-            endTranslation = 0;
-        }
-
-        long duration = BaseSwipeDetector.calculateDuration(velocity,
-                (endTranslation - startTranslation) / getWidth());
-
-        mContentTranslateAnimator.removeAllListeners();
-        mContentTranslateAnimator.setDuration(duration)
-                .setInterpolator(scrollInterpolatorForVelocity(velocity));
-        mContentTranslateAnimator.setFloatValues(startTranslation, endTranslation);
-        mContentTranslateAnimator.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                mSwipeDetector.finishedScrolling();
-                if (willExit) {
-                    onChildDismissed();
-                }
-            }
-        });
-        mContentTranslateAnimator.start();
+        launcher.getStatsLogManager().logger().log(LAUNCHER_NOTIFICATION_DISMISSED);
     }
 }
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 408796f..f73d782 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -24,7 +24,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.util.Themes;
 
 /**
  * A PageIndicator that briefly shows a fraction of a line when moving between pages
@@ -123,7 +123,7 @@
         mLauncher = Launcher.getLauncher(context);
         mLineHeight = res.getDimensionPixelSize(R.dimen.workspace_page_indicator_line_height);
 
-        boolean darkText = WallpaperColorInfo.INSTANCE.get(context).supportsDarkText();
+        boolean darkText = Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText);
         mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
         mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
     }
@@ -268,7 +268,9 @@
         } else {
             lp.leftMargin = lp.rightMargin = 0;
             lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-            lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
+            lp.bottomMargin = grid.isTaskbarPresent
+                    ? grid.workspacePadding.bottom + grid.taskbarSize
+                    : 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 901d27f..ab35bd6 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -17,6 +17,7 @@
 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;
@@ -31,17 +32,20 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.ItemInstallQueue;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -53,6 +57,8 @@
  */
 public class InstallSessionHelper {
 
+    private static final String LOG = "InstallSessionHelper";
+
     // Set<String> of session ids of promise icons that have been added to the home screen
     // as FLAG_PROMISE_NEW_INSTALLS.
     protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
@@ -64,27 +70,27 @@
 
     private final LauncherApps mLauncherApps;
     private final Context mAppContext;
-    private final IntSet mPromiseIconIds;
 
     private final PackageInstaller mInstaller;
     private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
 
+    private IntSet mPromiseIconIds;
+
     public InstallSessionHelper(Context context) {
         mInstaller = context.getPackageManager().getPackageInstaller();
         mAppContext = context.getApplicationContext();
         mLauncherApps = context.getSystemService(LauncherApps.class);
+    }
 
+    @WorkerThread
+    private IntSet getPromiseIconIds() {
+        Preconditions.assertWorkerThread();
+        if (mPromiseIconIds != null) {
+            return mPromiseIconIds;
+        }
         mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
-                getPrefs(context).getString(PROMISE_ICON_IDS, "")));
+                getPrefs(mAppContext).getString(PROMISE_ICON_IDS, "")));
 
-        cleanUpPromiseIconIds();
-    }
-
-    public static UserHandle getUserHandle(SessionInfo info) {
-        return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
-    }
-
-    protected void cleanUpPromiseIconIds() {
         IntArray existingIds = new IntArray();
         for (SessionInfo info : getActiveSessions().values()) {
             existingIds.add(info.getSessionId());
@@ -99,6 +105,7 @@
         for (int i = idsToRemove.size() - 1; i >= 0; --i) {
             mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
         }
+        return mPromiseIconIds;
     }
 
     public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
@@ -125,7 +132,7 @@
 
     private void updatePromiseIconPrefs() {
         getPrefs(mAppContext).edit()
-                .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
+                .putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString())
                 .apply();
     }
 
@@ -167,7 +174,7 @@
      * Attempt to restore workspace layout if the session is triggered due to device restore.
      */
     public boolean restoreDbIfApplicable(@NonNull final SessionInfo info) {
-        if (!Utilities.ATLEAST_OREO || !FeatureFlags.ENABLE_DATABASE_RESTORE.get()) {
+        if (!FeatureFlags.ENABLE_DATABASE_RESTORE.get()) {
             return false;
         }
         if (isRestore(info)) {
@@ -183,13 +190,15 @@
         return info.getInstallReason() == PackageManager.INSTALL_REASON_DEVICE_RESTORE;
     }
 
+    @WorkerThread
     public boolean promiseIconAddedForId(int sessionId) {
-        return mPromiseIconIds.contains(sessionId);
+        return getPromiseIconIds().contains(sessionId);
     }
 
+    @WorkerThread
     public void removePromiseIconId(int sessionId) {
-        if (mPromiseIconIds.contains(sessionId)) {
-            mPromiseIconIds.getArray().removeValue(sessionId);
+        if (promiseIconAddedForId(sessionId)) {
+            getPromiseIconIds().getArray().removeValue(sessionId);
             updatePromiseIconPrefs();
         }
     }
@@ -202,30 +211,39 @@
      * - The app is not already installed
      * - A promise icon for the session has not already been created
      */
+    @WorkerThread
     void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
-        if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+        if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
                 && SessionCommitReceiver.isEnabled(mAppContext)
-                && verify(sessionInfo) != null
-                && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
-                && sessionInfo.getAppIcon() != null
-                && !TextUtils.isEmpty(sessionInfo.getAppLabel())
-                && !mPromiseIconIds.contains(sessionInfo.getSessionId())
-                && new PackageManagerHelper(mAppContext).getApplicationInfo(
-                        sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), 0) == null) {
-            SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
-            mPromiseIconIds.add(sessionInfo.getSessionId());
+                && verifySessionInfo(sessionInfo)
+                && !promiseIconAddedForId(sessionInfo.getSessionId())) {
+            FileLog.d(LOG, "Adding package name to install queue: "
+                    + sessionInfo.getAppPackageName());
+
+            ItemInstallQueue.INSTANCE.get(mAppContext)
+                    .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
+
+            getPromiseIconIds().add(sessionInfo.getSessionId());
             updatePromiseIconPrefs();
         }
     }
 
-    public InstallSessionTracker registerInstallTracker(
-            InstallSessionTracker.Callback callback, LooperExecutor executor) {
+    public boolean verifySessionInfo(PackageInstaller.SessionInfo sessionInfo) {
+        return verify(sessionInfo) != null
+                && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
+                && sessionInfo.getAppIcon() != null
+                && !TextUtils.isEmpty(sessionInfo.getAppLabel())
+                && !new PackageManagerHelper(mAppContext).isAppInstalled(
+                        sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
+    }
+
+    public InstallSessionTracker registerInstallTracker(InstallSessionTracker.Callback callback) {
         InstallSessionTracker tracker = new InstallSessionTracker(this, callback);
 
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
-            mInstaller.registerSessionCallback(tracker, executor.getHandler());
+            mInstaller.registerSessionCallback(tracker, MODEL_EXECUTOR.getHandler());
         } else {
-            mLauncherApps.registerPackageInstallerSessionCallback(executor, tracker);
+            mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, tracker);
         }
         return tracker;
     }
@@ -237,4 +255,8 @@
             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 eb3ca73..b0b907a 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -24,8 +24,11 @@
 import android.os.UserHandle;
 import android.util.SparseArray;
 
+import androidx.annotation.WorkerThread;
+
 import com.android.launcher3.util.PackageUserKey;
 
+@WorkerThread
 public class InstallSessionTracker extends PackageInstaller.SessionCallback {
 
     // Lazily initialized
diff --git a/src/com/android/launcher3/pm/PackageInstallInfo.java b/src/com/android/launcher3/pm/PackageInstallInfo.java
index 7997d16..fad904f 100644
--- a/src/com/android/launcher3/pm/PackageInstallInfo.java
+++ b/src/com/android/launcher3/pm/PackageInstallInfo.java
@@ -25,7 +25,8 @@
 
     public static final int STATUS_INSTALLED = 0;
     public static final int STATUS_INSTALLING = 1;
-    public static final int STATUS_FAILED = 2;
+    public static final int STATUS_INSTALLED_DOWNLOADING = 2;
+    public static final int STATUS_FAILED = 3;
 
     public final ComponentName componentName;
     public final String packageName;
@@ -56,5 +57,4 @@
     public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
         return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
     }
-
 }
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index 40d7031..7af14c6 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -27,7 +27,6 @@
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Process;
@@ -39,13 +38,13 @@
 
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -175,37 +174,23 @@
         List<ShortcutConfigActivityInfo> result = new ArrayList<>();
         UserHandle myUser = Process.myUserHandle();
 
-        if (Utilities.ATLEAST_OREO) {
-            final List<UserHandle> users;
-            final String packageName;
-            if (packageUser == null) {
-                users = UserCache.INSTANCE.get(context).getUserProfiles();
-                packageName = null;
-            } else {
-                users = new ArrayList<>(1);
-                users.add(packageUser.mUser);
-                packageName = packageUser.mPackageName;
-            }
-            LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
-            for (UserHandle user : users) {
-                boolean ignoreTargetSdk = myUser.equals(user);
-                for (LauncherActivityInfo activityInfo :
-                        launcherApps.getShortcutConfigActivityList(packageName, user)) {
-                    if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion
-                            >= Build.VERSION_CODES.O) {
-                        result.add(new ShortcutConfigActivityInfoVO(activityInfo));
-                    }
-                }
-            }
+        final List<UserHandle> users;
+        final String packageName;
+        if (packageUser == null) {
+            users = UserCache.INSTANCE.get(context).getUserProfiles();
+            packageName = null;
         } else {
-            if (packageUser == null || packageUser.mUser.equals(myUser)) {
-                Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-                if (packageUser != null) {
-                    intent.setPackage(packageUser.mPackageName);
-                }
-                for (ResolveInfo info :
-                        context.getPackageManager().queryIntentActivities(intent, 0)) {
-                    result.add(new ShortcutConfigActivityInfoVL(info.activityInfo));
+            users = Collections.singletonList(packageUser.mUser);
+            packageName = packageUser.mPackageName;
+        }
+        LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+        for (UserHandle user : users) {
+            boolean ignoreTargetSdk = myUser.equals(user);
+            for (LauncherActivityInfo activityInfo :
+                    launcherApps.getShortcutConfigActivityList(packageName, user)) {
+                if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion
+                        >= Build.VERSION_CODES.O) {
+                    result.add(new ShortcutConfigActivityInfoVO(activityInfo));
                 }
             }
         }
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 2d7d6b0..5ade22b 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -60,6 +60,9 @@
     private void onUsersChanged(Intent intent) {
         enableAndResetCache();
         mUserChangeListeners.forEach(Runnable::run);
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.WORK_PROFILE_REMOVED, "profile changed", new Exception());
+        }
     }
 
     /**
@@ -104,9 +107,6 @@
                 mUsers = null;
                 mUserToSerialMap = null;
             }
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work profile removed", new Exception());
-            }
         }
     }
 
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index d5b32fc..a534ee3 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -16,52 +16,86 @@
 
 package com.android.launcher3.popup;
 
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static androidx.core.content.ContextCompat.getColorStateList;
+
+import static com.android.launcher3.anim.Interpolators.ACCELERATED_EASE;
+import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_LOCAL_COLOR_POPUPS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.CornerPathEffect;
-import android.graphics.Outline;
-import android.graphics.Paint;
+import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.drawable.ShapeDrawable;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.util.Pair;
+import android.util.SparseIntArray;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
+import android.view.animation.Interpolator;
 import android.widget.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.LauncherAnimUtils;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
 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.Workspace;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.TriangleShape;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.LocalColorExtractor;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
 
 /**
  * A container for shortcuts to deep links and notifications associated with an app.
  *
  * @param <T> The activity on with the popup shows
  */
-public abstract class ArrowPopup<T extends BaseDraggingActivity> extends AbstractFloatingView {
+public abstract class ArrowPopup<T extends StatefulActivity<LauncherState>>
+        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;
+
+    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;
+
+    // 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();
 
@@ -70,17 +104,40 @@
     protected final T mLauncher;
     protected final boolean mIsRtl;
 
-    private final int mArrowOffset;
+    private final int mArrowOffsetVertical;
+    private final int mArrowOffsetHorizontal;
+    private final int mArrowWidth;
+    private final int mArrowHeight;
+    private final int mArrowPointRadius;
     private final View mArrow;
 
+    private final int mMargin;
+
     protected boolean mIsLeftAligned;
     protected boolean mIsAboveIcon;
     private int mGravity;
 
-    protected Animator mOpenCloseAnimator;
+    protected AnimatorSet mOpenCloseAnimator;
     protected boolean mDeferContainerRemoval;
-    private final Rect mStartRect = new Rect();
-    private final Rect mEndRect = new Rect();
+
+    private final GradientDrawable mRoundedTop;
+    private final GradientDrawable mRoundedBottom;
+
+    private Runnable mOnCloseCallback = () -> { };
+
+    // The rect string of the view that the arrow is attached to, in screen reference frame.
+    protected String mArrowColorRectString;
+    private int mArrowColor;
+    protected final HashMap<String, View> mViewForRect = new HashMap<>();
+
+    @Nullable protected LocalColorExtractor mColorExtractor;
+
+    private final float mElevation;
+    private final int mBackgroundColor;
+
+    private final String mIterateChildrenTag;
+
+    private final int[] mColors;
 
     public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
@@ -89,21 +146,50 @@
         mLauncher = BaseDraggingActivity.fromContext(context);
         mIsRtl = Utilities.isRtl(getResources());
 
-        setClipToOutline(true);
-        setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
-            }
-        });
+        mBackgroundColor = Themes.getAttrColor(context, R.attr.popupColorPrimary);
+        mArrowColor = mBackgroundColor;
+        mElevation = getResources().getDimension(R.dimen.deep_shortcuts_elevation);
 
         // Initialize arrow view
         final Resources resources = getResources();
-        final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
-        final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
+        mMargin = resources.getDimensionPixelSize(R.dimen.popup_margin);
+        mArrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
+        mArrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
         mArrow = new View(context);
-        mArrow.setLayoutParams(new DragLayer.LayoutParams(arrowWidth, arrowHeight));
-        mArrowOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
+        mArrow.setLayoutParams(new DragLayer.LayoutParams(mArrowWidth, mArrowHeight));
+        mArrowOffsetVertical = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
+        mArrowOffsetHorizontal = resources.getDimensionPixelSize(
+                R.dimen.popup_arrow_horizontal_center_offset) - (mArrowWidth / 2);
+        mArrowPointRadius = resources.getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
+
+        int smallerRadius = resources.getDimensionPixelSize(R.dimen.popup_smaller_radius);
+        mRoundedTop = new GradientDrawable();
+        mRoundedTop.setColor(mBackgroundColor);
+        mRoundedTop.setCornerRadii(new float[] { mOutlineRadius, mOutlineRadius, mOutlineRadius,
+                mOutlineRadius, smallerRadius, smallerRadius, smallerRadius, smallerRadius});
+
+        mRoundedBottom = new GradientDrawable();
+        mRoundedBottom.setColor(mBackgroundColor);
+        mRoundedBottom.setCornerRadii(new float[] { smallerRadius, smallerRadius, smallerRadius,
+                smallerRadius, mOutlineRadius, mOutlineRadius, mOutlineRadius, mOutlineRadius});
+
+        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();
+        }
+
+        if (isAboveAnotherSurface) {
+            mColors = new int[] {
+                    getColorStateList(context, R.color.popup_shade_first).getDefaultColor()};
+        } 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()};
+        }
     }
 
     public ArrowPopup(Context context, AttributeSet attrs) {
@@ -147,79 +233,294 @@
     protected void onInflationComplete(boolean isReversed) { }
 
     /**
+     * Set the margins and radius of backgrounds after views are properly ordered.
+     */
+    public void assignMarginsAndBackgrounds(ViewGroup viewGroup) {
+        assignMarginsAndBackgrounds(viewGroup, Color.TRANSPARENT);
+    }
+
+    /**
+     * @param backgroundColor When Color.TRANSPARENT, we get color from {@link #mColors}.
+     *                        Otherwise, we will use this color for all child views.
+     */
+    private void assignMarginsAndBackgrounds(ViewGroup viewGroup, int backgroundColor) {
+        final boolean getColorFromColorArray = backgroundColor == Color.TRANSPARENT;
+
+        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) {
+                totalVisibleShortcuts++;
+            }
+        }
+
+        int numVisibleChild = 0;
+        int numVisibleShortcut = 0;
+        View lastView = null;
+        AnimatorSet colorAnimator = new AnimatorSet();
+        for (int i = 0; i < count; i++) {
+            View view = viewGroup.getChildAt(i);
+            if (view.getVisibility() == VISIBLE) {
+                if (lastView != null) {
+                    MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
+                    mlp.bottomMargin = mMargin;
+                }
+                lastView = view;
+                MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams();
+                mlp.bottomMargin = 0;
+
+
+                if (getColorFromColorArray) {
+                    backgroundColor = mColors[numVisibleChild % mColors.length];
+                }
+
+                if (view instanceof ViewGroup && mIterateChildrenTag.equals(view.getTag())) {
+                    assignMarginsAndBackgrounds((ViewGroup) view, backgroundColor);
+                    numVisibleChild++;
+                    continue;
+                }
+
+                if (view instanceof DeepShortcutView) {
+                    if (totalVisibleShortcuts == 1) {
+                        view.setBackgroundResource(R.drawable.single_item_primary);
+                    } else if (totalVisibleShortcuts > 1) {
+                        if (numVisibleShortcut == 0) {
+                            view.setBackground(mRoundedTop.getConstantState().newDrawable());
+                        } else if (numVisibleShortcut == (totalVisibleShortcuts - 1)) {
+                            view.setBackground(mRoundedBottom.getConstantState().newDrawable());
+                        } else {
+                            view.setBackgroundResource(R.drawable.middle_item_primary);
+                        }
+                        numVisibleShortcut++;
+                    }
+                }
+
+                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++;
+            }
+        }
+
+        colorAnimator.setDuration(0).start();
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+    }
+
+
+    @TargetApi(Build.VERSION_CODES.S)
+    private int getExtractedColor(SparseIntArray colors) {
+        int index = Utilities.isDarkTheme(getContext())
+                ? DARK_COLOR_EXTRACTION_INDEX
+                : LIGHT_COLOR_EXTRACTION_INDEX;
+        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
+            public boolean onPreDraw() {
+                getViewTreeObserver().removeOnPreDrawListener(this);
+                initColorExtractionLocations(launcher);
+                return true;
+            }
+        });
+    }
+
+    /**
+     * Returns list of child views that will receive local color extraction treatment.
+     * Note: Order should match the view hierarchy.
+     */
+    protected List<View> getChildrenForColorExtraction() {
+        return Collections.emptyList();
+    }
+
+    private void initColorExtractionLocations(Launcher launcher) {
+        if (mColorExtractor == null) {
+            return;
+        }
+        ArrayList<RectF> locations = new ArrayList<>();
+
+        boolean firstVisibleChild = true;
+        // Order matters here, since we need the arrow to match the color of its adjacent view.
+        for (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;
+                        }
+                    }
+
+                    if (firstVisibleChild) {
+                        firstVisibleChild = false;
+                    }
+
+                }
+            }
+        }
+        if (!locations.isEmpty()) {
+            mColorExtractor.addLocation(locations);
+        }
+    }
+
+    /**
+     * Sets the background color of the child.
+     */
+    protected void setChildColor(View view, int color, AnimatorSet animatorSetOut) {
+        Drawable bg = view.getBackground();
+        if (bg instanceof GradientDrawable) {
+            GradientDrawable gd = (GradientDrawable) bg.mutate();
+            int oldColor = ((GradientDrawable) bg).getColor().getDefaultColor();
+            animatorSetOut.play(ObjectAnimator.ofArgb(gd, "color", oldColor, color));
+        } else if (bg instanceof ColorDrawable) {
+            ColorDrawable cd = (ColorDrawable) bg.mutate();
+            int oldColor = ((ColorDrawable) bg).getColor();
+            animatorSetOut.play(ObjectAnimator.ofArgb(cd, "color", oldColor, color));
+        }
+    }
+
+    /**
      * Shows the popup at the desired location, optionally reversing the children.
      * @param viewsToFlip number of views from the top to to flip in case of reverse order
      */
     protected void reorderAndShow(int viewsToFlip) {
+        setupForDisplay();
+        boolean reverseOrder = mIsAboveIcon;
+        if (reverseOrder) {
+            reverseOrder(viewsToFlip);
+        }
+        onInflationComplete(reverseOrder);
+        assignMarginsAndBackgrounds(this);
+        if (shouldAddArrow()) {
+            addArrow();
+        }
+        animateOpen();
+    }
+
+    /**
+     * Shows the popup at the desired location.
+     */
+    protected void show() {
+        setupForDisplay();
+        onInflationComplete(false);
+        assignMarginsAndBackgrounds(this);
+        if (shouldAddArrow()) {
+            addArrow();
+        }
+        animateOpen();
+    }
+
+    private void setupForDisplay() {
         setVisibility(View.INVISIBLE);
         mIsOpen = true;
         getPopupContainer().addView(this);
         orientAboutObject();
+    }
 
-        boolean reverseOrder = mIsAboveIcon;
-        if (reverseOrder) {
-            int count = getChildCount();
-            ArrayList<View> allViews = new ArrayList<>(count);
-            for (int i = 0; i < count; i++) {
-                if (i == viewsToFlip) {
-                    Collections.reverse(allViews);
-                }
-                allViews.add(getChildAt(i));
+    private void reverseOrder(int viewsToFlip) {
+        int count = getChildCount();
+        ArrayList<View> allViews = new ArrayList<>(count);
+        for (int i = 0; i < count; i++) {
+            if (i == viewsToFlip) {
+                Collections.reverse(allViews);
             }
-            Collections.reverse(allViews);
-            removeAllViews();
-            for (int i = 0; i < count; i++) {
-                addView(allViews.get(i));
-            }
-
-            orientAboutObject();
+            allViews.add(getChildAt(i));
         }
-        onInflationComplete(reverseOrder);
+        Collections.reverse(allViews);
+        removeAllViews();
+        for (int i = 0; i < count; i++) {
+            addView(allViews.get(i));
+        }
+    }
 
-        // Add the arrow.
-        final Resources res = getResources();
-        final int arrowCenterOffset = res.getDimensionPixelSize(isAlignedWithStart()
-                ? R.dimen.popup_arrow_horizontal_center_start
-                : R.dimen.popup_arrow_horizontal_center_end);
-        final int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
-        getPopupContainer().addView(mArrow);
-        DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
+    private int getArrowLeft() {
         if (mIsLeftAligned) {
-            mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
-        } else {
-            mArrow.setX(getX() + getMeasuredWidth() - arrowCenterOffset - halfArrowWidth);
+            return mArrowOffsetHorizontal;
         }
+        return getMeasuredWidth() - mArrowOffsetHorizontal - mArrowWidth;
+    }
+
+    private void addArrow() {
+        getPopupContainer().addView(mArrow);
+        mArrow.setX(getX() + getArrowLeft());
 
         if (Gravity.isVertical(mGravity)) {
             // This is only true if there wasn't room for the container next to the icon,
             // so we centered it instead. In that case we don't want to showDefaultOptions the arrow.
             mArrow.setVisibility(INVISIBLE);
         } else {
-            ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
-                    arrowLp.width, arrowLp.height, !mIsAboveIcon));
-            Paint arrowPaint = arrowDrawable.getPaint();
-            arrowPaint.setColor(Themes.getAttrColor(getContext(), R.attr.popupColorPrimary));
-            // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
-            int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
-            arrowPaint.setPathEffect(new CornerPathEffect(radius));
-            mArrow.setBackground(arrowDrawable);
-            // Clip off the part of the arrow that is underneath the popup.
-            if (mIsAboveIcon) {
-                mArrow.setClipBounds(new Rect(0, -mArrowOffset, arrowLp.width, arrowLp.height));
-            } else {
-                mArrow.setClipBounds(new Rect(0, 0, arrowLp.width, arrowLp.height + mArrowOffset));
-            }
-            mArrow.setElevation(getElevation());
+            updateArrowColor();
         }
 
-        mArrow.setPivotX(arrowLp.width / 2);
-        mArrow.setPivotY(mIsAboveIcon ? arrowLp.height : 0);
-
-        animateOpen();
+        mArrow.setPivotX(mArrowWidth / 2.0f);
+        mArrow.setPivotY(mIsAboveIcon ? mArrowHeight : 0);
     }
 
-    protected boolean isAlignedWithStart() {
-        return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
+    private void updateArrowColor() {
+        if (!Gravity.isVertical(mGravity)) {
+            mArrow.setBackground(new RoundedArrowDrawable(
+                    mArrowWidth, mArrowHeight, mArrowPointRadius,
+                    mOutlineRadius, getMeasuredWidth(), getMeasuredHeight(),
+                    mArrowOffsetHorizontal, -mArrowOffsetVertical,
+                    !mIsAboveIcon, mIsLeftAligned,
+                    mArrowColor));
+            setElevation(mElevation);
+            mArrow.setElevation(mElevation);
+        }
+    }
+
+    /**
+     * Returns whether or not we should add the arrow.
+     */
+    protected boolean shouldAddArrow() {
+        return true;
     }
 
     /**
@@ -252,10 +553,19 @@
      */
     private void orientAboutObject(boolean allowAlignLeft, boolean allowAlignRight) {
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-        int width = getMeasuredWidth();
-        int extraVerticalSpace = mArrow.getLayoutParams().height + mArrowOffset
+
+        int extraVerticalSpace = mArrowHeight + mArrowOffsetVertical
                 + getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
-        int height = getMeasuredHeight() + extraVerticalSpace;
+        // The margins are added after we call this method, so we need to account for them here.
+        int numVisibleChildren = 0;
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            if (getChildAt(i).getVisibility() == VISIBLE) {
+                numVisibleChildren++;
+            }
+        }
+        int childMargins = (numVisibleChildren - 1) * mMargin;
+        int height = getMeasuredHeight() + extraVerticalSpace + childMargins;
+        int width = getMeasuredWidth() + getPaddingLeft() + getPaddingRight();
 
         getTargetObjectLocation(mTempRect);
         InsettableFrameLayout dragLayer = getPopupContainer();
@@ -269,22 +579,7 @@
 
         // Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
         int iconWidth = mTempRect.width();
-        Resources resources = getResources();
-        int xOffset;
-        if (isAlignedWithStart()) {
-            // Aligning with the shortcut icon.
-            int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
-            int shortcutPaddingStart = resources.getDimensionPixelSize(
-                    R.dimen.popup_padding_start);
-            xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
-        } else {
-            // Aligning with the drag handle.
-            int shortcutDragHandleWidth = resources.getDimensionPixelSize(
-                    R.dimen.deep_shortcut_drag_handle_size);
-            int shortcutPaddingEnd = resources.getDimensionPixelSize(
-                    R.dimen.popup_padding_end);
-            xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
-        }
+        int xOffset = iconWidth / 2 - mArrowOffsetHorizontal - mArrowWidth / 2;
         x += mIsLeftAligned ? xOffset : -xOffset;
 
         // Check whether we can still align as we originally wanted, now that we've calculated x.
@@ -353,12 +648,14 @@
         FrameLayout.LayoutParams arrowLp = (FrameLayout.LayoutParams) mArrow.getLayoutParams();
         if (mIsAboveIcon) {
             arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
-            lp.bottomMargin = getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top;
-            arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrowOffset - insets.bottom;
+            lp.bottomMargin =
+                    getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top;
+            arrowLp.bottomMargin =
+                    lp.bottomMargin - arrowLp.height - mArrowOffsetVertical - insets.bottom;
         } else {
             arrowLp.gravity = lp.gravity = Gravity.TOP;
             lp.topMargin = y + insets.top;
-            arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrowOffset;
+            arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrowOffsetVertical;
         }
     }
 
@@ -397,39 +694,10 @@
     private void animateOpen() {
         setVisibility(View.VISIBLE);
 
-        final AnimatorSet openAnim = new AnimatorSet();
-        final Resources res = getResources();
-        final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
-        final long arrowDuration = res.getInteger(R.integer.config_popupArrowOpenCloseDuration);
-        final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;
-
-        // Rectangular reveal.
-        mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
-        final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
-                .createRevealAnimator(this, false);
-        revealAnim.setDuration(revealDuration);
-        revealAnim.setInterpolator(revealInterpolator);
-        // Clip the popup to the initial outline while the notification dot and arrow animate.
-        revealAnim.start();
-        revealAnim.pause();
-
-        ValueAnimator fadeIn = ValueAnimator.ofFloat(0, 1);
-        fadeIn.setDuration(revealDuration + arrowDuration);
-        fadeIn.setInterpolator(revealInterpolator);
-        fadeIn.addUpdateListener(anim -> {
-            float alpha = (float) anim.getAnimatedValue();
-            mArrow.setAlpha(alpha);
-            setAlpha(revealAnim.isStarted() ? alpha : 0);
-        });
-        openAnim.play(fadeIn);
-
-        // Animate the arrow.
-        mArrow.setScaleX(0);
-        mArrow.setScaleY(0);
-        Animator arrowScale = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 1)
-                .setDuration(arrowDuration);
-
-        openAnim.addListener(new AnimatorListenerAdapter() {
+        mOpenCloseAnimator = getOpenCloseAnimator(true, OPEN_DURATION, OPEN_FADE_START_DELAY,
+                OPEN_FADE_DURATION, OPEN_CHILD_FADE_START_DELAY, OPEN_CHILD_FADE_DURATION,
+                DECELERATED_EASE);
+        mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 setAlpha(1f);
@@ -437,56 +705,77 @@
                 mOpenCloseAnimator = null;
             }
         });
-
-        mOpenCloseAnimator = openAnim;
-        openAnim.playSequentially(arrowScale, revealAnim);
-        openAnim.start();
+        mOpenCloseAnimator.start();
     }
 
+    private AnimatorSet getOpenCloseAnimator(boolean isOpening, int totalDuration,
+            int fadeStartDelay, int fadeDuration, int childFadeStartDelay,
+            int childFadeDuration, Interpolator interpolator) {
+        final AnimatorSet animatorSet = new AnimatorSet();
+        float[] alphaValues = isOpening ? new float[] {0, 1} : new float[] {1, 0};
+        float[] scaleValues = isOpening ? new float[] {0.5f, 1} : new float[] {1, 0.5f};
+
+        ValueAnimator fade = ValueAnimator.ofFloat(alphaValues);
+        fade.setStartDelay(fadeStartDelay);
+        fade.setDuration(fadeDuration);
+        fade.setInterpolator(LINEAR);
+        fade.addUpdateListener(anim -> {
+            float alpha = (float) anim.getAnimatedValue();
+            mArrow.setAlpha(alpha);
+            setAlpha(alpha);
+        });
+        animatorSet.play(fade);
+
+        setPivotX(mIsLeftAligned ? 0 : getMeasuredWidth());
+        setPivotY(mIsAboveIcon ? getMeasuredHeight() : 0);
+        Animator scale = ObjectAnimator.ofFloat(this, View.SCALE_Y, scaleValues);
+        scale.setDuration(totalDuration);
+        scale.setInterpolator(interpolator);
+        animatorSet.play(scale);
+
+        fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet);
+
+        return animatorSet;
+    }
+
+    private void fadeInChildViews(ViewGroup group, float[] alphaValues, long startDelay,
+            long duration, AnimatorSet out) {
+        for (int i = group.getChildCount() - 1; i >= 0; --i) {
+            View view = group.getChildAt(i);
+            if (view.getVisibility() == VISIBLE && view instanceof ViewGroup) {
+                if (mIterateChildrenTag.equals(view.getTag())) {
+                    fadeInChildViews((ViewGroup) view, alphaValues, startDelay, duration, out);
+                    continue;
+                }
+                for (int j = ((ViewGroup) view).getChildCount() - 1; j >= 0; --j) {
+                    View childView = ((ViewGroup) view).getChildAt(j);
+                    childView.setAlpha(alphaValues[0]);
+                    ValueAnimator childFade = ObjectAnimator.ofFloat(childView, ALPHA, alphaValues);
+                    childFade.setStartDelay(startDelay);
+                    childFade.setDuration(duration);
+                    childFade.setInterpolator(LINEAR);
+
+                    out.play(childFade);
+                }
+            }
+        }
+    }
+
+
     protected void animateClose() {
         if (!mIsOpen) {
             return;
         }
-        if (getOutlineProvider() instanceof RevealOutlineAnimation) {
-            ((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
-        } else {
-            mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
-        }
         if (mOpenCloseAnimator != null) {
             mOpenCloseAnimator.cancel();
         }
         mIsOpen = false;
 
-
-        final AnimatorSet closeAnim = new AnimatorSet();
-        final Resources res = getResources();
-        final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;
-        final long revealDuration = res.getInteger(R.integer.config_popupOpenCloseDuration);
-        final long arrowDuration = res.getInteger(R.integer.config_popupArrowOpenCloseDuration);
-
-        // Hide the arrow
-        Animator scaleArrow = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0)
-                .setDuration(arrowDuration);
-
-        // Rectangular reveal (reversed).
-        final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
-                .createRevealAnimator(this, true);
-        revealAnim.setDuration(revealDuration);
-        revealAnim.setInterpolator(revealInterpolator);
-        closeAnim.playSequentially(revealAnim, scaleArrow);
-
-        ValueAnimator fadeOut = ValueAnimator.ofFloat(getAlpha(), 0);
-        fadeOut.setDuration(revealDuration + arrowDuration);
-        fadeOut.setInterpolator(revealInterpolator);
-        fadeOut.addUpdateListener(anim -> {
-            float alpha = (float) anim.getAnimatedValue();
-            mArrow.setAlpha(alpha);
-            setAlpha(scaleArrow.isStarted() ? 0 : alpha);
-        });
-        closeAnim.play(fadeOut);
-
-        onCreateCloseAnimation(closeAnim);
-        closeAnim.addListener(new AnimatorListenerAdapter() {
+        mOpenCloseAnimator = getOpenCloseAnimator(false, CLOSE_DURATION, CLOSE_FADE_START_DELAY,
+                CLOSE_FADE_DURATION, CLOSE_CHILD_FADE_START_DELAY, CLOSE_CHILD_FADE_DURATION,
+                ACCELERATED_EASE);
+        onCreateCloseAnimation(mOpenCloseAnimator);
+        mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 mOpenCloseAnimator = null;
@@ -497,8 +786,7 @@
                 }
             }
         });
-        mOpenCloseAnimator = closeAnim;
-        closeAnim.start();
+        mOpenCloseAnimator.start();
     }
 
     /**
@@ -506,25 +794,6 @@
      */
     protected void onCreateCloseAnimation(AnimatorSet anim) { }
 
-    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
-        Resources res = getResources();
-        int arrowCenterX = res.getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
-                R.dimen.popup_arrow_horizontal_center_start:
-                R.dimen.popup_arrow_horizontal_center_end);
-        int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
-        float arrowCornerRadius = res.getDimension(R.dimen.popup_arrow_corner_radius);
-        if (!mIsLeftAligned) {
-            arrowCenterX = getMeasuredWidth() - arrowCenterX;
-        }
-        int arrowCenterY = mIsAboveIcon ? getMeasuredHeight() : 0;
-
-        mStartRect.set(arrowCenterX - halfArrowWidth, arrowCenterY, arrowCenterX + halfArrowWidth,
-                arrowCenterY);
-
-        return new RoundedRectRevealOutlineProvider
-                (arrowCornerRadius, mOutlineRadius, mStartRect, mEndRect);
-    }
-
     /**
      * Closes the popup without animation.
      */
@@ -537,6 +806,20 @@
         mDeferContainerRemoval = false;
         getPopupContainer().removeView(this);
         getPopupContainer().removeView(mArrow);
+        mOnCloseCallback.run();
+        mArrowColorRectString = null;
+        mViewForRect.clear();
+        if (mColorExtractor != null) {
+            mColorExtractor.removeLocations();
+            mColorExtractor.setListener(null);
+        }
+    }
+
+    /**
+     * Callback to be called when the popup is closed
+     */
+    public void setOnCloseCallback(@NonNull Runnable callback) {
+        mOnCloseCallback = callback;
     }
 
     protected BaseDragLayer getPopupContainer() {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 614cf14..18f263a 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -19,13 +19,8 @@
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
 import static com.android.launcher3.Utilities.squaredHypot;
 import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.notification.NotificationMainView.NOTIFICATION_ITEM_INFO;
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
 import android.animation.AnimatorSet;
@@ -51,8 +46,8 @@
 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.Utilities;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
 import com.android.launcher3.dot.DotInfo;
@@ -69,12 +64,14 @@
 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.BaseDragLayer;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -86,8 +83,8 @@
  *
  * @param <T> The activity on with the popup shows
  */
-public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends ArrowPopup<T>
-        implements DragSource, DragController.DragListener {
+public class PopupContainerWithArrow<T extends StatefulActivity<LauncherState>>
+        extends ArrowPopup<T> implements DragSource, DragController.DragListener {
 
     private final List<DeepShortcutView> mShortcuts = new ArrayList<>();
     private final PointF mInterceptTouchDown = new PointF();
@@ -97,6 +94,11 @@
     private BubbleTextView mOriginalIcon;
     private NotificationItemView mNotificationItemView;
     private int mNumNotifications;
+    private ViewGroup mNotificationContainer;
+
+    private ViewGroup mWidgetContainer;
+
+    private ViewGroup mDeepShortcutContainer;
 
     private ViewGroup mSystemShortcutContainer;
 
@@ -136,29 +138,10 @@
     }
 
     @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (mNotificationItemView != null) {
-            return mNotificationItemView.onTouchEvent(ev) || super.onTouchEvent(ev);
-        }
-        return super.onTouchEvent(ev);
-    }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_ACTION_POPUP) != 0;
     }
 
-    @Override
-    public void logActionCommand(int command) {
-        mLauncher.getUserEventDispatcher().logActionCommand(
-                command, mOriginalIcon, getLogContainerType());
-    }
-
-    @Override
-    public int getLogContainerType() {
-        return ContainerType.DEEPSHORTCUTS;
-    }
-
     public OnClickListener getItemClickListener() {
         return (view) -> {
             mLauncher.getItemOnClickListener().onClick(view);
@@ -175,8 +158,7 @@
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             BaseDragLayer dl = getPopupContainer();
             if (!dl.isEventOverView(this, ev)) {
-                mLauncher.getUserEventDispatcher().logActionTapOutside(
-                        newContainerTarget(ContainerType.DEEPSHORTCUTS));
+                // TODO: add WW log if want to log if tap closed deep shortcut container.
                 close(true);
 
                 // We let touches on the original icon go through so that users can launch
@@ -187,6 +169,14 @@
         return false;
     }
 
+    @Override
+    protected void setChildColor(View view, int color, AnimatorSet animatorSetOut) {
+        super.setChildColor(view, color, animatorSetOut);
+        if (view.getId() == R.id.notification_container && mNotificationItemView != null) {
+            mNotificationItemView.updateBackgroundColor(color, animatorSetOut);
+        }
+    }
+
     /**
      * Returns true if we can show the container.
      */
@@ -224,6 +214,7 @@
                         .filter(Objects::nonNull)
                         .collect(Collectors.toList()));
         launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
+        container.requestFocus();
         return container;
     }
 
@@ -232,6 +223,13 @@
         mPopupItemDragHandler = new LauncherPopupItemDragHandler(launcher, this);
         mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(launcher);
         launcher.getDragController().addDragListener(this);
+        addPreDrawForColorExtraction(launcher);
+    }
+
+    @Override
+    protected List<View> getChildrenForColorExtraction() {
+        return Arrays.asList(mSystemShortcutContainer, mWidgetContainer, mDeepShortcutContainer,
+                mNotificationContainer);
     }
 
     @Override
@@ -239,20 +237,6 @@
         if (isReversed && mNotificationItemView != null) {
             mNotificationItemView.inverseGutterMargin();
         }
-
-        // Update dividers
-        int count = getChildCount();
-        DeepShortcutView lastView = null;
-        for (int i = 0; i < count; i++) {
-            View view = getChildAt(i);
-            if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
-                if (lastView != null) {
-                    lastView.setDividerVisibility(VISIBLE);
-                }
-                lastView = (DeepShortcutView) view;
-                lastView.setDividerVisibility(INVISIBLE);
-            }
-        }
     }
 
     @TargetApi(Build.VERSION_CODES.P)
@@ -274,44 +258,64 @@
         // Add views
         if (mNumNotifications > 0) {
             // Add notification entries
-            View.inflate(getContext(), R.layout.notification_content, this);
-            mNotificationItemView = new NotificationItemView(this);
-            if (mNumNotifications == 1) {
-                mNotificationItemView.removeFooter();
+            if (mNotificationContainer == null) {
+                mNotificationContainer = findViewById(R.id.notification_container);
+                mNotificationContainer.setVisibility(VISIBLE);
             }
-            else {
-                mNotificationItemView.setFooterWidth(containerWidth);
-            }
+            View.inflate(getContext(), R.layout.notification_content, mNotificationContainer);
+            mNotificationItemView = new NotificationItemView(this, mNotificationContainer);
             updateNotificationHeader();
         }
         int viewsToFlip = getChildCount();
         mSystemShortcutContainer = this;
+        if (mDeepShortcutContainer == null) {
+            mDeepShortcutContainer = findViewById(R.id.deep_shortcuts_container);
+        }
         if (hasDeepShortcuts) {
+            mDeepShortcutContainer.setVisibility(View.VISIBLE);
+
             if (mNotificationItemView != null) {
                 mNotificationItemView.addGutter();
             }
 
             for (int i = shortcutCount; i > 0; i--) {
-                DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut, this);
+                DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut, mDeepShortcutContainer);
                 v.getLayoutParams().width = containerWidth;
                 mShortcuts.add(v);
             }
             updateHiddenShortcuts();
 
             if (!systemShortcuts.isEmpty()) {
-                mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
                 for (SystemShortcut shortcut : systemShortcuts) {
-                    initializeSystemShortcut(
-                            R.layout.system_shortcut_icon_only, mSystemShortcutContainer, shortcut);
+                    if (shortcut instanceof SystemShortcut.Widgets) {
+                        if (mWidgetContainer == null) {
+                            mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
+                                    this);
+                        }
+                        initializeSystemShortcut(R.layout.system_shortcut, mWidgetContainer,
+                                shortcut);
+                    }
+                }
+                mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
+
+                for (SystemShortcut shortcut : systemShortcuts) {
+                    if (!(shortcut instanceof SystemShortcut.Widgets)) {
+                        initializeSystemShortcut(
+                                R.layout.system_shortcut_icon_only, mSystemShortcutContainer,
+                                shortcut);
+                    }
                 }
             }
-        } else if (!systemShortcuts.isEmpty()) {
-            if (mNotificationItemView != null) {
-                mNotificationItemView.addGutter();
-            }
+        } else {
+            mDeepShortcutContainer.setVisibility(View.GONE);
+            if (!systemShortcuts.isEmpty()) {
+                if (mNotificationItemView != null) {
+                    mNotificationItemView.addGutter();
+                }
 
-            for (SystemShortcut shortcut : systemShortcuts) {
-                initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
+                for (SystemShortcut shortcut : systemShortcuts) {
+                    initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
+                }
             }
         }
 
@@ -359,34 +363,11 @@
     private void updateHiddenShortcuts() {
         int allowedCount = mNotificationItemView != null
                 ? MAX_SHORTCUTS_IF_NOTIFICATIONS : MAX_SHORTCUTS;
-        int originalHeight = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height);
-        int itemHeight = mNotificationItemView != null ?
-                getResources().getDimensionPixelSize(R.dimen.bg_popup_item_condensed_height)
-                : originalHeight;
-        float iconScale = ((float) itemHeight) / originalHeight;
 
         int total = mShortcuts.size();
         for (int i = 0; i < total; i++) {
             DeepShortcutView view = mShortcuts.get(i);
             view.setVisibility(i >= allowedCount ? GONE : VISIBLE);
-            view.getLayoutParams().height = itemHeight;
-            view.getIconView().setScaleX(iconScale);
-            view.getIconView().setScaleY(iconScale);
-        }
-    }
-
-    private void updateDividers() {
-        int count = getChildCount();
-        DeepShortcutView lastView = null;
-        for (int i = 0; i < count; i++) {
-            View view = getChildAt(i);
-            if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
-                if (lastView != null) {
-                    lastView.setDividerVisibility(VISIBLE);
-                }
-                lastView = (DeepShortcutView) view;
-                lastView.setDividerVisibility(INVISIBLE);
-            }
         }
     }
 
@@ -400,9 +381,7 @@
         } else if (view instanceof ImageView) {
             // Only the system shortcut icon shows on a gray background header.
             info.setIconAndContentDescriptionFor((ImageView) view);
-            if (Utilities.ATLEAST_OREO) {
-                view.setTooltipText(view.getContentDescription());
-            }
+            view.setTooltipText(view.getContentDescription());
         }
         view.setTag(info);
         view.setOnClickListener(info);
@@ -452,7 +431,9 @@
                     // Make sure we keep the original icon hidden while it is being dragged.
                     mOriginalIcon.setVisibility(INVISIBLE);
                 } else {
-                    mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon);
+                    // TODO: add WW logging if want to add logging for long press on popup
+                    //  container.
+                    //  mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon);
                     if (!mIsAboveIcon) {
                         // Show the icon but keep the text hidden.
                         mOriginalIcon.setVisibility(VISIBLE);
@@ -467,8 +448,7 @@
         ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
         DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo);
         if (mNotificationItemView != null && dotInfo != null) {
-            mNotificationItemView.updateHeader(
-                    dotInfo.getNotificationCount(), itemInfo.bitmap.color);
+            mNotificationItemView.updateHeader(dotInfo.getNotificationCount());
         }
     }
 
@@ -499,18 +479,6 @@
     }
 
     @Override
-    public void fillInLogContainerData(ItemInfo childInfo, Target child,
-            ArrayList<Target> parents) {
-        if (childInfo == NOTIFICATION_ITEM_INFO) {
-            child.itemType = ItemType.NOTIFICATION;
-        } else {
-            child.itemType = ItemType.DEEPSHORTCUT;
-            child.rank = childInfo.rank;
-        }
-        parents.add(newContainerTarget(ContainerType.DEEPSHORTCUTS));
-    }
-
-    @Override
     protected void onCreateCloseAnimation(AnimatorSet anim) {
         // Animate original icon's text back in.
         anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
@@ -557,25 +525,34 @@
             mLauncher.getPopupDataProvider().setChangeListener(null);
         }
 
+        private View getWidgetsView(ViewGroup container) {
+            for (int i = container.getChildCount() - 1; i >= 0; --i) {
+                View systemShortcutView = container.getChildAt(i);
+                if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
+                    return systemShortcutView;
+                }
+            }
+            return null;
+        }
+
         @Override
         public void onWidgetsBound() {
             ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
             SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
-            View widgetsView = null;
-            int count = mSystemShortcutContainer.getChildCount();
-            for (int i = 0; i < count; i++) {
-                View systemShortcutView = mSystemShortcutContainer.getChildAt(i);
-                if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
-                    widgetsView = systemShortcutView;
-                    break;
-                }
+            View widgetsView = getWidgetsView(PopupContainerWithArrow.this);
+            if (widgetsView == null && mWidgetContainer != null) {
+                widgetsView = getWidgetsView(mWidgetContainer);
             }
 
             if (widgetInfo != null && widgetsView == null) {
                 // We didn't have any widgets cached but now there are some, so enable the shortcut.
                 if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
-                    initializeSystemShortcut(R.layout.system_shortcut_icon_only,
-                            mSystemShortcutContainer, widgetInfo);
+                    if (mWidgetContainer == null) {
+                        mWidgetContainer = inflateAndAdd(R.layout.widget_shortcut_container,
+                                PopupContainerWithArrow.this);
+                    }
+                    initializeSystemShortcut(R.layout.system_shortcut, mWidgetContainer,
+                            widgetInfo);
                 } else {
                     // If using the expanded system shortcut (as opposed to just the icon), we need
                     // to reopen the container to ensure measurements etc. all work out. While this
@@ -587,8 +564,10 @@
                 }
             } else if (widgetInfo == null && widgetsView != null) {
                 // No widgets exist, but we previously added the shortcut so remove it.
-                if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
-                    mSystemShortcutContainer.removeView(widgetsView);
+                if (mSystemShortcutContainer
+                        != PopupContainerWithArrow.this
+                        && mWidgetContainer != null) {
+                    mWidgetContainer.removeView(widgetsView);
                 } else {
                     close(false);
                     PopupContainerWithArrow.showForIcon(mOriginalIcon);
@@ -620,8 +599,9 @@
                 // No more notifications, remove the notification views and expand all shortcuts.
                 mNotificationItemView.removeAllViews();
                 mNotificationItemView = null;
+                mNotificationContainer.setVisibility(GONE);
                 updateHiddenShortcuts();
-                updateDividers();
+                assignMarginsAndBackgrounds(PopupContainerWithArrow.this);
             } else {
                 mNotificationItemView.trimNotifications(
                         NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
@@ -630,6 +610,17 @@
     }
 
     /**
+     * Dismisses the popup if it is no longer valid
+     */
+    public static void dismissInvalidPopup(BaseDraggingActivity activity) {
+        PopupContainerWithArrow popup = getOpen(activity);
+        if (popup != null && (!popup.mOriginalIcon.isAttachedToWindow()
+                || !canShow(popup.mOriginalIcon, (ItemInfo) popup.mOriginalIcon.getTag()))) {
+            popup.animateClose();
+        }
+    }
+
+    /**
      * Handler to control drag-and-drop for popup items
      */
     public interface PopupItemDragHandler extends OnLongClickListener, OnTouchListener { }
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 5a5f668..6f9f0d7 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -31,16 +31,16 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.ShortcutUtil;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -59,8 +59,11 @@
     private HashMap<ComponentKey, Integer> mDeepShortcutMap = new HashMap<>();
     /** Maps packages to their DotInfo's . */
     private Map<PackageUserKey, DotInfo> mPackageUserToDotInfos = new HashMap<>();
-    /** Maps packages to their Widgets */
-    private ArrayList<WidgetListRowEntry> mAllWidgets = new ArrayList<>();
+
+    /** All installed widgets. */
+    private List<WidgetsListBaseEntry> mAllWidgets = List.of();
+    /** Widgets that can be recommended to the users. */
+    private List<ItemInfo> mRecommendedWidgets = List.of();
 
     private PopupDataChangeListener mChangeListener = PopupDataChangeListener.INSTANCE;
 
@@ -188,7 +191,16 @@
         notificationListener.cancelNotificationFromLauncher(notificationKey);
     }
 
-    public void setAllWidgets(ArrayList<WidgetListRowEntry> allWidgets) {
+    /**
+     * Sets a list of recommended widgets ordered by their order of appearance in the widgets
+     * recommendation UI.
+     */
+    public void setRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
+        mRecommendedWidgets = recommendedWidgets;
+        mChangeListener.onRecommendedWidgetsBound();
+    }
+
+    public void setAllWidgets(List<WidgetsListBaseEntry> allWidgets) {
         mAllWidgets = allWidgets;
         mChangeListener.onWidgetsBound();
     }
@@ -197,25 +209,33 @@
         mChangeListener = listener == null ? PopupDataChangeListener.INSTANCE : listener;
     }
 
-    public ArrayList<WidgetListRowEntry> getAllWidgets() {
+    public List<WidgetsListBaseEntry> getAllWidgets() {
         return mAllWidgets;
     }
 
+    /** Returns a list of recommended widgets. */
+    public List<WidgetItem> getRecommendedWidgets() {
+        HashMap<ComponentKey, WidgetItem> allWidgetItems = new HashMap<>();
+        mAllWidgets.stream()
+                .filter(entry -> entry instanceof WidgetsListContentEntry)
+                .forEach(entry -> ((WidgetsListContentEntry) entry).mWidgets
+                        .forEach(widget -> allWidgetItems.put(
+                                new ComponentKey(widget.componentName, widget.user), widget)));
+        return mRecommendedWidgets.stream()
+                .map(recommendedWidget -> allWidgetItems.get(
+                        new ComponentKey(recommendedWidget.getTargetComponent(),
+                                recommendedWidget.user)))
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
     public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
-        for (WidgetListRowEntry entry : mAllWidgets) {
-            if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) {
-                ArrayList<WidgetItem> widgets = new ArrayList<>(entry.widgets);
-                // Remove widgets not associated with the correct user.
-                Iterator<WidgetItem> iterator = widgets.iterator();
-                while (iterator.hasNext()) {
-                    if (!iterator.next().user.equals(packageUserKey.mUser)) {
-                        iterator.remove();
-                    }
-                }
-                return widgets.isEmpty() ? null : widgets;
-            }
-        }
-        return null;
+        return mAllWidgets.stream()
+                .filter(row -> row instanceof WidgetsListContentEntry
+                        && row.mPkgItem.packageName.equals(packageUserKey.mPackageName))
+                .flatMap(row -> ((WidgetsListContentEntry) row).mWidgets.stream())
+                .filter(widget -> packageUserKey.mUser.equals(widget.user))
+                .collect(Collectors.toList());
     }
 
     /**
@@ -253,5 +273,8 @@
         default void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) { }
 
         default void onWidgetsBound() { }
+
+        /** A callback to get notified when recommended widgets are bound. */
+        default void onRecommendedWidgetsBound() { }
     }
 }
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 61829c0..7c393ad 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -37,7 +37,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
 
 @TargetApi(Build.VERSION_CODES.Q)
 public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
@@ -107,9 +106,6 @@
                     Toast.LENGTH_SHORT)
                     .show();
         }
-
-        mTarget.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
-                LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
     }
 
     @Override
diff --git a/src/com/android/launcher3/popup/RoundedArrowDrawable.java b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
new file mode 100644
index 0000000..e662d5c
--- /dev/null
+++ b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
@@ -0,0 +1,161 @@
+/*
+ * 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.popup;
+
+import static java.lang.Math.atan;
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+import static java.lang.Math.toDegrees;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A drawable for a very specific purpose. Used for the caret arrow on a rounded rectangle popup
+ * bubble.
+ * Draws a triangle with one rounded tip, the opposite edge is clipped by the body of the popup
+ * so there is no overlap when drawing them together.
+ */
+public class RoundedArrowDrawable extends Drawable {
+
+    private final Path mPath;
+    private final Paint mPaint;
+
+    /**
+     * Default constructor.
+     *
+     * @param width of the arrow.
+     * @param height of the arrow.
+     * @param radius of the tip of the arrow.
+     * @param popupRadius of the rect to clip this by.
+     * @param popupWidth of the rect to clip this by.
+     * @param popupHeight of the rect to clip this by.
+     * @param arrowOffsetX from the edge of the popup to the arrow.
+     * @param arrowOffsetY how much the arrow will overlap the popup.
+     * @param isPointingUp or not.
+     * @param leftAligned or false for right aligned.
+     * @param color to draw the triangle.
+     */
+    public RoundedArrowDrawable(float width, float height, float radius, float popupRadius,
+            float popupWidth, float popupHeight,
+            float arrowOffsetX, float arrowOffsetY, boolean isPointingUp, boolean leftAligned,
+            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 and positioned on the left..
+        addDownPointingRoundedTriangleToPath(width, height, radius, mPath);
+        clipPopupBodyFromPath(popupRadius, popupWidth, popupHeight, arrowOffsetX, arrowOffsetY,
+                mPath);
+
+        // ... then flip it horizontal or vertical based on where it will be used.
+        Matrix pathTransform = new Matrix();
+        pathTransform.setScale(
+                leftAligned ? 1 : -1, isPointingUp ? -1 : 1, width * 0.5f, height * 0.5f);
+        mPath.transform(pathTransform);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.drawPath(mPath, mPaint);
+    }
+
+    @Override
+    public void getOutline(Outline outline) {
+        outline.setPath(mPath);
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAlpha(int i) {
+        mPaint.setAlpha(i);
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mPaint.setColorFilter(colorFilter);
+    }
+
+    private static void addDownPointingRoundedTriangleToPath(float width, float height,
+            float radius, Path path) {
+        // Calculated for the arrow pointing down, will be flipped later if needed.
+
+        // Theta is half of the angle inside the triangle tip
+        float tanTheta = width / (2.0f * height);
+        float theta = (float) atan(tanTheta);
+
+        // Some trigonometry to find the center of the circle for the rounded tip
+        float roundedPointCenterY = (float) (height - (radius / sin(theta)));
+
+        // p is the distance along the triangle side to the intersection with the point circle
+        float p = radius / tanTheta;
+        float lineRoundPointIntersectFromCenter = (float) (p * sin(theta));
+        float lineRoundPointIntersectFromTop = (float) (height - (p * cos(theta)));
+
+        float centerX = width / 2.0f;
+        float thetaDeg = (float) toDegrees(theta);
+
+        path.reset();
+        path.moveTo(0, 0);
+        // Draw the top
+        path.lineTo(width, 0);
+        // Draw the right side up to the circle intersection
+        path.lineTo(
+                centerX + lineRoundPointIntersectFromCenter,
+                lineRoundPointIntersectFromTop);
+        // Draw the rounded point
+        path.arcTo(
+                centerX - radius,
+                roundedPointCenterY - radius,
+                centerX + radius,
+                roundedPointCenterY + radius,
+                thetaDeg,
+                180 - (2 * thetaDeg),
+                false);
+        // Draw the left edge to close
+        path.lineTo(0, 0);
+        path.close();
+    }
+
+    private static void clipPopupBodyFromPath(float popupRadius, float popupWidth,
+            float popupHeight, float arrowOffsetX, float arrowOffsetY, Path path) {
+        // Make a path that is used to clip the triangle, this represents the body of the popup
+        Path clipPiece = new Path();
+        clipPiece.addRoundRect(
+                0, 0, popupWidth, popupHeight,
+                popupRadius, popupRadius, Path.Direction.CW);
+        // clipping is performed as if the arrow is pointing down and positioned on the left, the
+        // resulting path will be flipped as needed later.
+        // The extra 0.5 in the vertical offset is to close the gap between this anti-aliased object
+        // and the anti-aliased body of the popup.
+        clipPiece.offset(-arrowOffsetX, -popupHeight + arrowOffsetY - 0.5f);
+        path.op(clipPiece, Path.Op.DIFFERENCE);
+    }
+}
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index fd292a3..d3f4909 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -21,8 +21,6 @@
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
@@ -47,6 +45,13 @@
     protected final T mTarget;
     protected final ItemInfo mItemInfo;
 
+    /**
+     * Indicates if it's invokable or not through some disabled UI
+     */
+    private boolean isEnabled = true;
+
+    private boolean mHasFinishRecentsInAction = false;
+
     public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) {
         mIconResId = iconResId;
         mLabelResId = labelResId;
@@ -85,10 +90,26 @@
                 mAccessibilityActionId, context.getText(mLabelResId));
     }
 
+    public void setEnabled(boolean enabled) {
+        isEnabled = enabled;
+    }
+
+    public boolean isEnabled() {
+        return isEnabled;
+    }
+
     public boolean hasHandlerForAction(int action) {
         return mAccessibilityActionId == action;
     }
 
+    public void setHasFinishRecentsInAction(boolean hasFinishRecentsInAction) {
+        mHasFinishRecentsInAction = hasFinishRecentsInAction;
+    }
+
+    public boolean hasFinishRecentsInAction() {
+        return mHasFinishRecentsInAction;
+    }
+
     public interface Factory<T extends BaseDraggingActivity> {
 
         @Nullable SystemShortcut<T> getShortcut(T activity, ItemInfo itemInfo);
@@ -99,7 +120,7 @@
         final List<WidgetItem> widgets =
                 launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
                         itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
-        if (widgets == null) {
+        if (widgets.isEmpty()) {
             return null;
         }
         return new Widgets(launcher, itemInfo);
@@ -117,8 +138,6 @@
                     (WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
                             R.layout.widgets_bottom_sheet, mTarget.getDragLayer(), false);
             widgetsBottomSheet.populateAndShow(mItemInfo);
-            mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
-                    ControlType.WIDGETS_BUTTON, view);
             mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
                     .log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP);
         }
@@ -139,8 +158,6 @@
             Rect sourceBounds = mTarget.getViewBounds(view);
             new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
                     mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
-            mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
-                    ControlType.APPINFO_TARGET, view);
             mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
                     .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
         }
@@ -174,7 +191,7 @@
         public void onClick(View view) {
             Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
                     mItemInfo.getTargetComponent().getPackageName());
-            mTarget.startActivitySafely(view, intent, mItemInfo, null);
+            mTarget.startActivitySafely(view, intent, mItemInfo);
             AbstractFloatingView.closeAllOpenViews(mTarget);
         }
     }
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index a5462a6..c9af2fe 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -284,8 +284,9 @@
             insertOperations.clear();
         }
 
-        IntSparseArrayMap<Object> hotseatItems = GridSizeMigrationTask.removeBrokenHotseatItems(mContext);
-        int myHotseatCount = LauncherAppState.getIDP(mContext).numHotseatIcons;
+        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(
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 53183bf..223f4f1 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -103,7 +103,7 @@
      */
     private void backupWorkspace(Context context, SQLiteDatabase db) throws Exception {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        new GridBackupTable(context, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
+        new GridBackupTable(context, db, idp.numDatabaseHotseatIcons, idp.numColumns, idp.numRows)
                 .doBackup(getDefaultProfileId(db), GridBackupTable.OPTION_REQUIRES_SANITIZATION);
     }
 
@@ -111,7 +111,7 @@
             @NonNull DatabaseHelper helper, @NonNull BackupManager backupManager)
             throws Exception {
         final InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        GridBackupTable backupTable = new GridBackupTable(context, db, idp.numHotseatIcons,
+        GridBackupTable backupTable = new GridBackupTable(context, db, idp.numDatabaseHotseatIcons,
                 idp.numColumns, idp.numRows);
         if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
             int itemsDeleted = sanitizeDB(helper, db, backupManager);
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 289e0d8..23ee251 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -30,7 +30,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -42,13 +41,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.InvariantDeviceProfile;
 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.graphics.FragmentWithPreview;
+import com.android.launcher3.widget.util.WidgetSizes;
 
 /**
  * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
@@ -158,7 +157,7 @@
 
         protected String mKeyWidgetId = "qsb_widget_id";
         private QsbWidgetHost mQsbWidgetHost;
-        private AppWidgetProviderInfo mWidgetInfo;
+        protected AppWidgetProviderInfo mWidgetInfo;
         private QsbWidgetHostView mQsb;
 
         // We need to store the orientation here, due to a bug (b/64916689) that results in widgets
@@ -292,15 +291,8 @@
 
         protected Bundle createBindOptions() {
             InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
-
-            Bundle opts = new Bundle();
-            Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(getContext(),
-                    idp.numColumns, 1, null);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
-            return opts;
+            return WidgetSizes.getWidgetSizeOptions(getContext(), mWidgetInfo.provider,
+                    idp.numColumns, 1);
         }
 
         protected View getDefaultView(ViewGroup container, boolean showSetupIcon) {
diff --git a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
new file mode 100644
index 0000000..5b8d5bc
--- /dev/null
+++ b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.recyclerview;
+
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Creates and populates views with data
+ *
+ * @param <T> A data model which is used to populate the {@link ViewHolder}.
+ * @param <V> A subclass of {@link ViewHolder} which holds references to views.
+ */
+public interface ViewHolderBinder<T, V extends ViewHolder> {
+    /**
+     * Creates a new view, and attach it to the parent {@link ViewGroup}. Then, populates UI
+     * references in a {@link ViewHolder}.
+     */
+    V newViewHolder(ViewGroup parent);
+
+    /** Populate UI references in {@link ViewHolder} with data. */
+    void bindViewHolder(V viewHolder, T data, int position);
+
+    /**
+     * Called when the view is recycled. Views are recycled in batches once they are sufficiently
+     * far off screen that it is unlikely the user will scroll back to them soon. Optionally
+     * override this to free expensive resources.
+     */
+    default void unbindViewHolder(V viewHolder) {}
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchAlgorithm.java b/src/com/android/launcher3/search/SearchAlgorithm.java
similarity index 69%
rename from src/com/android/launcher3/allapps/search/SearchAlgorithm.java
rename to src/com/android/launcher3/search/SearchAlgorithm.java
index c409b1c..a1720c7 100644
--- a/src/com/android/launcher3/allapps/search/SearchAlgorithm.java
+++ b/src/com/android/launcher3/search/SearchAlgorithm.java
@@ -13,20 +13,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.allapps.search;
+package com.android.launcher3.search;
 
 /**
  * An interface for handling search.
+ *
+ * @param <T> Search Result type
  */
-public interface SearchAlgorithm {
+public interface SearchAlgorithm<T> {
 
     /**
-     * Performs search and sends the result to the callback.
+     * Performs search and sends the result to {@link SearchCallback}.
      */
-    void doSearch(String query, AllAppsSearchBarController.Callbacks callback);
+    void doSearch(String query, SearchCallback<T> callback);
 
     /**
      * Cancels any active request.
      */
     void cancel(boolean interruptActiveRequests);
+
+    /**
+     * Cleans up after search is no longer needed.
+     */
+    default void destroy() {};
 }
diff --git a/src/com/android/launcher3/search/SearchCallback.java b/src/com/android/launcher3/search/SearchCallback.java
new file mode 100644
index 0000000..5796116
--- /dev/null
+++ b/src/com/android/launcher3/search/SearchCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.search;
+
+import java.util.ArrayList;
+
+/**
+ * An interface for receiving search results.
+ *
+ * @param <T> Search Result type
+ */
+public interface SearchCallback<T> {
+
+    /**
+     * Called when the search from primary source is complete.
+     *
+     * @param items list of search results
+     */
+    void onSearchResult(String query, ArrayList<T> items);
+
+    /**
+     * Called when the search from secondary source is complete.
+     *
+     * @param items list of search results
+     */
+    void onAppendSearchResult(String query, ArrayList<T> items);
+
+    /**
+     * Called when the search results should be cleared.
+     */
+    void clearSearchResult();
+}
+
diff --git a/src/com/android/launcher3/search/StringMatcherUtility.java b/src/com/android/launcher3/search/StringMatcherUtility.java
new file mode 100644
index 0000000..acab52b
--- /dev/null
+++ b/src/com/android/launcher3/search/StringMatcherUtility.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.search;
+
+import java.text.Collator;
+
+/**
+ * Utilities for matching query string to target string.
+ */
+public class StringMatcherUtility {
+
+    /**
+     * Returns {@code true} is {@code query} is a prefix substring of a complete word/phrase in
+     * {@code target}.
+     */
+    public static boolean matches(String query, String target, StringMatcher matcher) {
+        int queryLength = query.length();
+
+        int targetLength = target.length();
+
+        if (targetLength < queryLength || queryLength <= 0) {
+            return false;
+        }
+
+        if (requestSimpleFuzzySearch(query)) {
+            return target.toLowerCase().contains(query);
+        }
+
+        int lastType;
+        int thisType = Character.UNASSIGNED;
+        int nextType = Character.getType(target.codePointAt(0));
+
+        int end = targetLength - queryLength;
+        for (int i = 0; i <= end; i++) {
+            lastType = thisType;
+            thisType = nextType;
+            nextType = i < (targetLength - 1)
+                    ? Character.getType(target.codePointAt(i + 1)) : Character.UNASSIGNED;
+            if (isBreak(thisType, lastType, nextType)
+                    && matcher.matches(query, target.substring(i, i + queryLength))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the current point should be a break point. Following cases
+     * are considered as break points:
+     *      1) Any non space character after a space character
+     *      2) Any digit after a non-digit character
+     *      3) Any capital character after a digit or small character
+     *      4) Any capital character before a small character
+     */
+    private static boolean isBreak(int thisType, int prevType, int nextType) {
+        switch (prevType) {
+            case Character.UNASSIGNED:
+            case Character.SPACE_SEPARATOR:
+            case Character.LINE_SEPARATOR:
+            case Character.PARAGRAPH_SEPARATOR:
+                return true;
+        }
+        switch (thisType) {
+            case Character.UPPERCASE_LETTER:
+                if (nextType == Character.UPPERCASE_LETTER) {
+                    return true;
+                }
+                // Follow through
+            case Character.TITLECASE_LETTER:
+                // Break point if previous was not a upper case
+                return prevType != Character.UPPERCASE_LETTER;
+            case Character.LOWERCASE_LETTER:
+                // Break point if previous was not a letter.
+                return prevType > Character.OTHER_LETTER || prevType <= Character.UNASSIGNED;
+            case Character.DECIMAL_DIGIT_NUMBER:
+            case Character.LETTER_NUMBER:
+            case Character.OTHER_NUMBER:
+                // Break point if previous was not a number
+                return !(prevType == Character.DECIMAL_DIGIT_NUMBER
+                        || prevType == Character.LETTER_NUMBER
+                        || prevType == Character.OTHER_NUMBER);
+            case Character.MATH_SYMBOL:
+            case Character.CURRENCY_SYMBOL:
+            case Character.OTHER_PUNCTUATION:
+            case Character.DASH_PUNCTUATION:
+                // Always a break point for a symbol
+                return true;
+            default:
+                return  false;
+        }
+    }
+
+    /**
+     * Performs locale sensitive string comparison using {@link Collator}.
+     */
+    public static class StringMatcher {
+
+        private static final char MAX_UNICODE = '\uFFFF';
+
+        private final Collator mCollator;
+
+        StringMatcher() {
+            // On android N and above, Collator uses ICU implementation which has a much better
+            // support for non-latin locales.
+            mCollator = Collator.getInstance();
+            mCollator.setStrength(Collator.PRIMARY);
+            mCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+        }
+
+        /**
+         * Returns true if {@param query} is a prefix of {@param target}
+         */
+        public boolean matches(String query, String target) {
+            switch (mCollator.compare(query, target)) {
+                case 0:
+                    return true;
+                case -1:
+                    // The target string can contain a modifier which would make it larger than
+                    // the query string (even though the length is same). If the query becomes
+                    // larger after appending a unicode character, it was originally a prefix of
+                    // the target string and hence should match.
+                    return mCollator.compare(query + MAX_UNICODE, target) > -1;
+                default:
+                    return false;
+            }
+        }
+
+        public static StringMatcher getInstance() {
+            return new StringMatcher();
+        }
+    }
+
+    /**
+     * Matching optimization to search in Chinese.
+     */
+    private static boolean requestSimpleFuzzySearch(String s) {
+        for (int i = 0; i < s.length(); ) {
+            int codepoint = s.codePointAt(i);
+            i += Character.charCount(codepoint);
+            switch (Character.UnicodeScript.of(codepoint)) {
+                case HAN:
+                    //Character.UnicodeScript.HAN: use String.contains to match
+                    return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 21ad275..5999091 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -15,11 +15,8 @@
  */
 package com.android.launcher3.secondarydisplay;
 
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityOptions;
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
@@ -37,9 +34,10 @@
 import com.android.launcher3.model.BgDataModel;
 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.PromiseAppInfo;
 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;
@@ -47,7 +45,7 @@
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -89,8 +87,8 @@
         if (mDragLayer != null) {
             return;
         }
-        InvariantDeviceProfile currentDisplayIdp =
-                new InvariantDeviceProfile(this, getWindow().getDecorView().getDisplay());
+        InvariantDeviceProfile currentDisplayIdp = new InvariantDeviceProfile(
+                this, getWindow().getDecorView().getDisplay());
 
         // Disable transpose layout and use multi-window mode so that the icons are scaled properly
         mDeviceProfile = currentDisplayIdp.getDeviceProfile(this)
@@ -169,11 +167,6 @@
     }
 
     @Override
-    public ActivityOptions getActivityLaunchOptions(View v) {
-        return null;
-    }
-
-    @Override
     protected void reapplyUi() { }
 
     @Override
@@ -196,9 +189,6 @@
     public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
 
     @Override
-    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
-
-    @Override
     public void bindScreens(IntArray orderedScreenIds) { }
 
     @Override
@@ -219,12 +209,12 @@
             ArrayList<ItemInfo> addAnimated) { }
 
     @Override
-    public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
-        mAppsView.getAppsStore().updatePromiseAppProgress(app);
+    public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
+        mAppsView.getAppsStore().updateProgressBar(app);
     }
 
     @Override
-    public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) { }
+    public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
 
     @Override
     public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
@@ -236,7 +226,7 @@
     public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
 
     @Override
-    public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets) { }
+    public void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
 
     @Override
     public void onPageBoundSynchronously(int page) { }
@@ -298,6 +288,7 @@
     @Override
     public void bindAllApplications(AppInfo[] apps, int flags) {
         mAppsView.getAppsStore().setApps(apps, flags);
+        PopupContainerWithArrow.dismissInvalidPopup(this);
     }
 
     public PopupDataProvider getPopupDataProvider() {
@@ -318,16 +309,18 @@
         if (tag instanceof ItemInfo) {
             ItemInfo item = (ItemInfo) tag;
             Intent intent;
-            if (item instanceof PromiseAppInfo) {
-                PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
-                intent = promiseAppInfo.getMarketIntent(this);
+            if (item instanceof ItemInfoWithIcon
+                    && (((ItemInfoWithIcon) item).runtimeStatusFlags
+                        & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
+                intent = appInfo.getMarketIntent(this);
             } else {
                 intent = item.getIntent();
             }
             if (intent == null) {
                 throw new IllegalArgumentException("Input must have a valid intent");
             }
-            startActivitySafely(v, intent, item, CONTAINER_ALL_APPS);
+            startActivitySafely(v, intent, item);
         }
     }
 }
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 40630d3..f78f6dd 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -29,7 +29,6 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.model.data.ItemInfo;
@@ -109,8 +108,6 @@
         setMeasuredDimension(width, height);
 
         DeviceProfile grid = mActivity.getDeviceProfile();
-        InvariantDeviceProfile idp = grid.inv;
-
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
@@ -118,10 +115,10 @@
                 int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx
                         + grid.cellLayoutPaddingLeftRightPx);
 
-                int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding;
+                int maxWidth = grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + padding;
                 int appsWidth = Math.min(width, maxWidth);
 
-                int maxHeight = grid.allAppsCellHeightPx * idp.numAllAppsColumns + padding;
+                int maxHeight = grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + padding;
                 int appsHeight = Math.min(height, maxHeight);
 
                 mAppsView.measure(
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index b12d04f..4d63218 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -15,7 +15,10 @@
  */
 package com.android.launcher3.settings;
 
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
 
 import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
 import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
@@ -26,25 +29,29 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
+import android.text.Editable;
+import android.text.TextWatcher;
 import android.util.ArrayMap;
 import android.util.Pair;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.widget.EditText;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceCategory;
 import androidx.preference.PreferenceDataStore;
 import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceScreen;
 import androidx.preference.PreferenceViewHolder;
 import androidx.preference.SwitchPreference;
@@ -97,6 +104,65 @@
         initFlags();
         loadPluginPrefs();
         maybeAddSandboxCategory();
+
+        if (getActivity() != null) {
+            getActivity().setTitle("Developer Options");
+        }
+    }
+
+    private void filterPreferences(String query, PreferenceGroup pg) {
+        int count = pg.getPreferenceCount();
+        int hidden = 0;
+        for (int i = 0; i < count; i++) {
+            Preference preference = pg.getPreference(i);
+            if (preference instanceof PreferenceGroup) {
+                filterPreferences(query, (PreferenceGroup) preference);
+            } else {
+                String title = preference.getTitle().toString().toLowerCase().replace("_", " ");
+                if (query.isEmpty() || title.contains(query)) {
+                    preference.setVisible(true);
+                } else {
+                    preference.setVisible(false);
+                    hidden++;
+                }
+            }
+        }
+        pg.setVisible(hidden != count);
+    }
+
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        EditText filterBox = view.findViewById(R.id.filter_box);
+        filterBox.setVisibility(VISIBLE);
+        filterBox.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+
+            }
+
+            @Override
+            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+
+            }
+
+            @Override
+            public void afterTextChanged(Editable editable) {
+                String query = editable.toString().toLowerCase().replace("_", " ");
+                filterPreferences(query, mPreferenceScreen);
+            }
+        });
+
+        View listView = getListView();
+        final int bottomPadding = listView.getPaddingBottom();
+        listView.setOnApplyWindowInsetsListener((v, insets) -> {
+            v.setPadding(
+                    v.getPaddingLeft(),
+                    v.getPaddingTop(),
+                    v.getPaddingRight(),
+                    bottomPadding + insets.getSystemWindowInsetBottom());
+            return insets.consumeSystemWindowInsets();
+        });
     }
 
     @Override
@@ -161,7 +227,7 @@
 
         Set<String> pluginActions = manager.getPluginActions();
 
-        ArrayMap<Pair<String, String>, ArrayList<Pair<String, ServiceInfo>>> plugins =
+        ArrayMap<Pair<String, String>, ArrayList<Pair<String, ResolveInfo>>> plugins =
                 new ArrayMap<>();
 
         Set<String> pluginPermissionApps = pm.getPackagesHoldingPermissions(
@@ -173,7 +239,7 @@
         for (String action : pluginActions) {
             String name = toName(action);
             List<ResolveInfo> result = pm.queryIntentServices(
-                    new Intent(action), MATCH_DISABLED_COMPONENTS);
+                    new Intent(action), MATCH_DISABLED_COMPONENTS | GET_RESOLVED_FILTER);
             for (ResolveInfo info : result) {
                 String packageName = info.serviceInfo.packageName;
                 if (!pluginPermissionApps.contains(packageName)) {
@@ -184,7 +250,7 @@
                 if (!plugins.containsKey(key)) {
                     plugins.put(key, new ArrayList<>());
                 }
-                plugins.get(key).add(Pair.create(name, info.serviceInfo));
+                plugins.get(key).add(Pair.create(name, info));
             }
         }
 
@@ -192,11 +258,11 @@
         plugins.forEach((key, si) -> {
             String packageName = key.first;
             List<ComponentName> componentNames = si.stream()
-                    .map(p -> new ComponentName(packageName, p.second.name))
+                    .map(p -> new ComponentName(packageName, p.second.serviceInfo.name))
                     .collect(Collectors.toList());
             if (!componentNames.isEmpty()) {
                 SwitchPreference pref = new PluginPreference(
-                        prefContext, si.get(0).second.applicationInfo, enabler, componentNames);
+                        prefContext, si.get(0).second, enabler, componentNames);
                 pref.setSummary("Plugins: "
                         + si.stream().map(p -> p.first).collect(Collectors.joining(", ")));
                 mPluginsCategory.addPreference(pref);
@@ -218,13 +284,28 @@
         }
         PreferenceCategory sandboxCategory = newCategory("Gesture Navigation Sandbox");
         sandboxCategory.setSummary("Learn and practice navigation gestures");
+        Preference launchOnboardingTutorialPreference = new Preference(context);
+        launchOnboardingTutorialPreference.setKey("launchOnboardingTutorial");
+        launchOnboardingTutorialPreference.setTitle("Launch Onboarding Tutorial");
+        launchOnboardingTutorialPreference.setSummary("Learn the basic navigation gestures.");
+        launchOnboardingTutorialPreference.setOnPreferenceClickListener(preference -> {
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {
+                            "HOME_NAVIGATION",
+                            "BACK_NAVIGATION",
+                            "OVERVIEW_NAVIGATION"}));
+            return true;
+        });
+        sandboxCategory.addPreference(launchOnboardingTutorialPreference);
         Preference launchBackTutorialPreference = new Preference(context);
         launchBackTutorialPreference.setKey("launchBackTutorial");
         launchBackTutorialPreference.setTitle("Launch Back Tutorial");
         launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
         launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
             startActivity(launchSandboxIntent.putExtra(
-                    "tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION"));
+                    "tutorial_steps",
+                    new String[] {"BACK_NAVIGATION"}));
             return true;
         });
         sandboxCategory.addPreference(launchBackTutorialPreference);
@@ -233,7 +314,9 @@
         launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
         launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
         launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
-            startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION"));
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {"HOME_NAVIGATION"}));
             return true;
         });
         sandboxCategory.addPreference(launchHomeTutorialPreference);
@@ -242,7 +325,9 @@
         launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
         launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
         launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
-            startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION"));
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {"OVERVIEW_NAVIGATION"}));
             return true;
         });
         sandboxCategory.addPreference(launchOverviewTutorialPreference);
@@ -251,10 +336,23 @@
         launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
         launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
         launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
-            startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT"));
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {"ASSISTANT"}));
             return true;
         });
         sandboxCategory.addPreference(launchAssistantTutorialPreference);
+        Preference launchSandboxModeTutorialPreference = new Preference(context);
+        launchSandboxModeTutorialPreference.setKey("launchSandboxMode");
+        launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
+        launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
+        launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
+            startActivity(launchSandboxIntent.putExtra(
+                    "tutorial_steps",
+                    new String[] {"SANDBOX_MODE"}));
+            return true;
+        });
+        sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
     }
 
     private String toName(String action) {
@@ -272,21 +370,33 @@
     }
 
     private static class PluginPreference extends SwitchPreference {
-        private final boolean mHasSettings;
-        private final PreferenceDataStore mPluginEnabler;
         private final String mPackageName;
+        private final ResolveInfo mSettingsInfo;
+        private final PreferenceDataStore mPluginEnabler;
         private final List<ComponentName> mComponentNames;
 
-        PluginPreference(Context prefContext, ApplicationInfo info,
+        PluginPreference(Context prefContext, ResolveInfo pluginInfo,
                 PreferenceDataStore pluginEnabler, List<ComponentName> componentNames) {
             super(prefContext);
             PackageManager pm = prefContext.getPackageManager();
-            mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
-                    .setPackage(info.packageName), 0) != null;
-            mPackageName = info.packageName;
-            mComponentNames = componentNames;
+            mPackageName = pluginInfo.serviceInfo.applicationInfo.packageName;
+            Intent settingsIntent = new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName);
+            // If any Settings activity in app has category filters, set plugin action as category.
+            List<ResolveInfo> settingsInfos =
+                    pm.queryIntentActivities(settingsIntent, GET_RESOLVED_FILTER);
+            if (pluginInfo.filter != null) {
+                for (ResolveInfo settingsInfo : settingsInfos) {
+                    if (settingsInfo.filter != null && settingsInfo.filter.countCategories() > 0) {
+                        settingsIntent.addCategory(pluginInfo.filter.getAction(0));
+                        break;
+                    }
+                }
+            }
+
+            mSettingsInfo = pm.resolveActivity(settingsIntent, 0);
             mPluginEnabler = pluginEnabler;
-            setTitle(info.loadLabel(pm));
+            mComponentNames = componentNames;
+            setTitle(pluginInfo.loadLabel(pm));
             setChecked(isPluginEnabled());
             setWidgetLayoutResource(R.layout.switch_preference_with_settings);
         }
@@ -327,17 +437,14 @@
         @Override
         public void onBindViewHolder(PreferenceViewHolder holder) {
             super.onBindViewHolder(holder);
-            holder.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE
-                    : View.GONE);
-            holder.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE
-                    : View.GONE);
+            boolean hasSettings = mSettingsInfo != null;
+            holder.findViewById(R.id.settings).setVisibility(hasSettings ? VISIBLE : GONE);
+            holder.findViewById(R.id.divider).setVisibility(hasSettings ? VISIBLE : GONE);
             holder.findViewById(R.id.settings).setOnClickListener(v -> {
-                ResolveInfo result = v.getContext().getPackageManager().resolveActivity(
-                        new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName), 0);
-                if (result != null) {
+                if (hasSettings) {
                     v.getContext().startActivity(new Intent().setComponent(
-                            new ComponentName(result.activityInfo.packageName,
-                                    result.activityInfo.name)));
+                            new ComponentName(mSettingsInfo.activityInfo.packageName,
+                                    mSettingsInfo.activityInfo.name)));
                 }
             });
             holder.itemView.setOnLongClickListener(v -> {
diff --git a/src/com/android/launcher3/settings/NotificationDotsPreference.java b/src/com/android/launcher3/settings/NotificationDotsPreference.java
index a91303a..0ee2744 100644
--- a/src/com/android/launcher3/settings/NotificationDotsPreference.java
+++ b/src/com/android/launcher3/settings/NotificationDotsPreference.java
@@ -17,6 +17,8 @@
 
 import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
 import static com.android.launcher3.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -24,6 +26,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.database.ContentObserver;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -35,20 +38,28 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.util.SecureSettingsObserver;
+import com.android.launcher3.util.SettingsCache;
 
 /**
  * A {@link Preference} for indicating notification dots status.
  * Also has utility methods for updating UI based on dots status changes.
  */
 public class NotificationDotsPreference extends Preference
-        implements SecureSettingsObserver.OnChangeListener {
+        implements SettingsCache.OnChangeListener {
 
     private boolean mWidgetFrameVisible = false;
 
     /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
     private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
 
+    private final ContentObserver mListenerListObserver =
+            new ContentObserver(MAIN_EXECUTOR.getHandler()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            updateUI();
+        }
+    };
+
     public NotificationDotsPreference(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -66,6 +77,35 @@
         super(context);
     }
 
+    @Override
+    public void onAttached() {
+        super.onAttached();
+        SettingsCache.INSTANCE.get(getContext()).register(NOTIFICATION_BADGING_URI, this);
+        getContext().getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS),
+                false, mListenerListObserver);
+        updateUI();
+
+        // Update intent
+        Bundle extras = new Bundle();
+        extras.putString(EXTRA_FRAGMENT_ARG_KEY, "notification_badging");
+        setIntent(new Intent("android.settings.NOTIFICATION_SETTINGS")
+                .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, extras));
+    }
+
+    private void updateUI() {
+        onSettingsChanged(SettingsCache.INSTANCE.get(getContext())
+                .getValue(NOTIFICATION_BADGING_URI));
+    }
+
+    @Override
+    public void onDetached() {
+        super.onDetached();
+        SettingsCache.INSTANCE.get(getContext()).unregister(NOTIFICATION_BADGING_URI, this);
+        getContext().getContentResolver().unregisterContentObserver(mListenerListObserver);
+
+    }
+
     private void setWidgetFrameVisible(boolean isVisible) {
         if (mWidgetFrameVisible != isVisible) {
             mWidgetFrameVisible = isVisible;
diff --git a/src/com/android/launcher3/settings/PreferenceHighlighter.java b/src/com/android/launcher3/settings/PreferenceHighlighter.java
index 8ba8146..96ee216 100644
--- a/src/com/android/launcher3/settings/PreferenceHighlighter.java
+++ b/src/com/android/launcher3/settings/PreferenceHighlighter.java
@@ -24,16 +24,18 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.RectF;
 import android.util.Property;
 import android.view.View;
 
-import com.android.launcher3.util.Themes;
-
+import androidx.preference.Preference;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
 import androidx.recyclerview.widget.RecyclerView.State;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
+import com.android.launcher3.util.Themes;
+
 /**
  * Utility class for highlighting a preference
  */
@@ -62,14 +64,16 @@
     private final Paint mPaint = new Paint();
     private final RecyclerView mRv;
     private final int mIndex;
+    private final Preference mPreference;
+    private final RectF mDrawRect = new RectF();
 
     private boolean mHighLightStarted = false;
     private int mHighlightColor = END_COLOR;
 
-
-    public PreferenceHighlighter(RecyclerView rv, int index) {
+    public PreferenceHighlighter(RecyclerView rv, int index, Preference preference) {
         mRv = rv;
         mIndex = index;
+        mPreference = preference;
     }
 
     @Override
@@ -92,7 +96,8 @@
         if (!mHighLightStarted) {
             // Start highlight
             int colorTo = setColorAlphaBound(Themes.getColorAccent(mRv.getContext()), 66);
-            ObjectAnimator anim = ObjectAnimator.ofArgb(this, HIGHLIGHT_COLOR, END_COLOR, colorTo);
+            ObjectAnimator anim = ObjectAnimator.ofArgb(this, HIGHLIGHT_COLOR, END_COLOR,
+                    colorTo);
             anim.setDuration(HIGHLIGHT_FADE_IN_DURATION);
             anim.setRepeatMode(ValueAnimator.REVERSE);
             anim.setRepeatCount(4);
@@ -108,7 +113,11 @@
 
         View view = holder.itemView;
         mPaint.setColor(mHighlightColor);
-        c.drawRect(0, view.getY(), parent.getWidth(), view.getY() + view.getHeight(), mPaint);
+        mDrawRect.set(0, view.getY(), parent.getWidth(), view.getY() + view.getHeight());
+        if (mPreference instanceof HighlightDelegate) {
+            ((HighlightDelegate) mPreference).offsetHighlight(view, mDrawRect);
+        }
+        c.drawRect(mDrawRect, mPaint);
     }
 
     private void removeHighlight() {
@@ -124,4 +133,16 @@
         });
         anim.start();
     }
+
+    /**
+     * Interface to be implemented by a preference to customize the highlight are
+     */
+    public interface HighlightDelegate {
+
+        /**
+         * Allows the preference to update the highlight area
+         */
+        void offsetHighlight(View prefView, RectF bounds);
+
+    }
 }
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index d3213a1..915e140 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -18,17 +18,18 @@
 
 import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
 
-import static com.android.launcher3.SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY;
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
-import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
-import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
 
+import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
-import android.provider.Settings;
 import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.view.WindowCompat;
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
@@ -41,12 +42,17 @@
 import androidx.preference.PreferenceScreen;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.launcher3.DeviceProfile;
+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.model.WidgetsModel;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.util.SecureSettingsObserver;
+
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
@@ -55,55 +61,97 @@
         implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback,
         SharedPreferences.OnSharedPreferenceChangeListener{
 
+    /** List of fragments that can be hosted by this activity. */
+    private static final List<String> VALID_PREFERENCE_FRAGMENTS = Collections.singletonList(
+            DeveloperOptionsFragment.class.getName());
+
     private static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
     private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
 
     private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging";
-    /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
-    private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
 
     public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
     public static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
     private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
     public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
 
+    @VisibleForTesting
+    static final String EXTRA_FRAGMENT = ":settings:fragment";
+    @VisibleForTesting
+    static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setContentView(R.layout.settings_activity);
+        setActionBar(findViewById(R.id.action_bar));
+        WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
+        Intent intent = getIntent();
+        if (intent.hasExtra(EXTRA_FRAGMENT) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)) {
+            getActionBar().setDisplayHomeAsUpEnabled(true);
+        }
 
         if (savedInstanceState == null) {
-            Bundle args = new Bundle();
-            String prefKey = getIntent().getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
+            Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS);
+            if (args == null) {
+                args = new Bundle();
+            }
+
+            String prefKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
             if (!TextUtils.isEmpty(prefKey)) {
                 args.putString(EXTRA_FRAGMENT_ARG_KEY, prefKey);
             }
 
             final FragmentManager fm = getSupportFragmentManager();
             final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(),
-                    getString(R.string.settings_fragment_name));
+                    getPreferenceFragment());
             f.setArguments(args);
             // Display the fragment as the main content.
-            fm.beginTransaction().replace(android.R.id.content, f).commit();
+            fm.beginTransaction().replace(R.id.content_frame, f).commit();
         }
         Utilities.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
     }
 
+    /**
+     * Obtains the preference fragment to instantiate in this activity.
+     *
+     * @return the preference fragment class
+     * @throws IllegalArgumentException if the fragment is unknown to this activity
+     */
+    private String getPreferenceFragment() {
+        String preferenceFragment = getIntent().getStringExtra(EXTRA_FRAGMENT);
+        String defaultFragment = getString(R.string.settings_fragment_name);
+
+        if (TextUtils.isEmpty(preferenceFragment)) {
+            return defaultFragment;
+        } else if (!preferenceFragment.equals(defaultFragment)
+                && !VALID_PREFERENCE_FRAGMENTS.contains(preferenceFragment)) {
+            throw new IllegalArgumentException(
+                    "Invalid fragment for this activity: " + preferenceFragment);
+        } else {
+            return preferenceFragment;
+        }
+    }
+
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { }
 
-    private boolean startFragment(String fragment, Bundle args, String key) {
+    private boolean startPreference(String fragment, Bundle args, String key) {
         if (Utilities.ATLEAST_P && getSupportFragmentManager().isStateSaved()) {
             // Sometimes onClick can come after onPause because of being posted on the handler.
-            // Skip starting new fragments in that case.
+            // Skip starting new preferences in that case.
             return false;
         }
         final FragmentManager fm = getSupportFragmentManager();
         final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), fragment);
-        f.setArguments(args);
         if (f instanceof DialogFragment) {
-            ((DialogFragment) f).show(getSupportFragmentManager(), key);
+            f.setArguments(args);
+            ((DialogFragment) f).show(fm, key);
         } else {
-            fm.beginTransaction().replace(android.R.id.content, f).addToBackStack(key).commit();
+            startActivity(new Intent(this, SettingsActivity.class)
+                    .putExtra(EXTRA_FRAGMENT, fragment)
+                    .putExtra(EXTRA_FRAGMENT_ARGS, args));
         }
         return true;
     }
@@ -111,14 +159,23 @@
     @Override
     public boolean onPreferenceStartFragment(
             PreferenceFragmentCompat preferenceFragment, Preference pref) {
-        return startFragment(pref.getFragment(), pref.getExtras(), pref.getKey());
+        return startPreference(pref.getFragment(), pref.getExtras(), pref.getKey());
     }
 
     @Override
     public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
         Bundle args = new Bundle();
         args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.getKey());
-        return startFragment(getString(R.string.settings_fragment_name), args, pref.getKey());
+        return startPreference(getString(R.string.settings_fragment_name), args, pref.getKey());
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            onBackPressed();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
     }
 
     /**
@@ -126,10 +183,9 @@
      */
     public static class LauncherSettingsFragment extends PreferenceFragmentCompat {
 
-        private SecureSettingsObserver mNotificationDotsObserver;
-
         private String mHighLightKey;
         private boolean mPreferenceHighlighted = false;
+        private Preference mDeveloperOptionPref;
 
         @Override
         public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -153,6 +209,25 @@
                     screen.removePreference(preference);
                 }
             }
+
+            if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
+                getActivity().setTitle(getPreferenceScreen().getTitle());
+            }
+        }
+
+        @Override
+        public void onViewCreated(View view, Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
+            View listView = getListView();
+            final int bottomPadding = listView.getPaddingBottom();
+            listView.setOnApplyWindowInsetsListener((v, insets) -> {
+                v.setPadding(
+                        v.getPaddingLeft(),
+                        v.getPaddingTop(),
+                        v.getPaddingRight(),
+                        bottomPadding + insets.getSystemWindowInsetBottom());
+                return insets.consumeSystemWindowInsets();
+            });
         }
 
         @Override
@@ -172,32 +247,17 @@
         protected boolean initPreference(Preference preference) {
             switch (preference.getKey()) {
                 case NOTIFICATION_DOTS_PREFERENCE_KEY:
-                    if (!Utilities.ATLEAST_OREO ||
-                            !getResources().getBoolean(R.bool.notification_dots_enabled)) {
-                        return false;
-                    }
-
-                    // Listen to system notification dot settings while this UI is active.
-                    mNotificationDotsObserver = newNotificationSettingsObserver(
-                            getActivity(), (NotificationDotsPreference) preference);
-                    mNotificationDotsObserver.register();
-                    // Also listen if notification permission changes
-                    mNotificationDotsObserver.getResolver().registerContentObserver(
-                            Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), false,
-                            mNotificationDotsObserver);
-                    mNotificationDotsObserver.dispatchOnChange();
-                    return true;
-
-                case ADD_ICON_PREFERENCE_KEY:
-                    return Utilities.ATLEAST_OREO;
+                    return !WidgetsModel.GO_DISABLE_NOTIFICATION_DOTS;
 
                 case ALLOW_ROTATION_PREFERENCE_KEY:
-                    if (getResources().getBoolean(R.bool.allow_rotation)) {
+                    DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get(
+                            getContext()).getDeviceProfile(getContext());
+                    if (deviceProfile.allowRotation) {
                         // Launcher supports rotation by default. No need to show this setting.
                         return false;
                     }
                     // Initialize the UI once
-                    preference.setDefaultValue(getAllowRotationDefaultValue());
+                    preference.setDefaultValue(false);
                     return true;
 
                 case FLAGS_PREFERENCE_KEY:
@@ -205,18 +265,37 @@
                     return FeatureFlags.showFlagTogglerUi(getContext());
 
                 case DEVELOPER_OPTIONS_KEY:
-                    // Show if plugins are enabled or flag UI is enabled.
-                    return FeatureFlags.showFlagTogglerUi(getContext()) ||
-                            PluginManagerWrapper.hasPlugins(getContext());
+                    mDeveloperOptionPref = preference;
+                    return updateDeveloperOption();
             }
 
             return true;
         }
 
+        /**
+         * Show if plugins are enabled or flag UI is enabled.
+         * @return True if we should show the preference option.
+         */
+        private boolean updateDeveloperOption() {
+            boolean showPreference = FeatureFlags.showFlagTogglerUi(getContext())
+                    || PluginManagerWrapper.hasPlugins(getContext());
+            if (mDeveloperOptionPref != null) {
+                mDeveloperOptionPref.setEnabled(showPreference);
+                if (showPreference) {
+                    getPreferenceScreen().addPreference(mDeveloperOptionPref);
+                } else {
+                    getPreferenceScreen().removePreference(mDeveloperOptionPref);
+                }
+            }
+            return showPreference;
+        }
+
         @Override
         public void onResume() {
             super.onResume();
 
+            updateDeveloperOption();
+
             if (isAdded() && !mPreferenceHighlighted) {
                 PreferenceHighlighter highlighter = createHighlighter();
                 if (highlighter != null) {
@@ -241,7 +320,9 @@
             RecyclerView list = getListView();
             PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
             int position = callback.getPreferenceAdapterPosition(mHighLightKey);
-            return position >= 0 ? new PreferenceHighlighter(list, position) : null;
+            return position >= 0 ? new PreferenceHighlighter(
+                    list, position, screen.findPreference(mHighLightKey))
+                    : null;
         }
 
         private void requestAccessibilityFocus(@NonNull final RecyclerView rv) {
@@ -252,14 +333,5 @@
                 }
             });
         }
-
-        @Override
-        public void onDestroy() {
-            if (mNotificationDotsObserver != null) {
-                mNotificationDotsObserver.unregister();
-                mNotificationDotsObserver = null;
-            }
-            super.onDestroy();
-        }
     }
 }
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index eb68592..ad3f8df 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -132,6 +132,12 @@
         mLoadingStatePlaceholder.draw(canvas);
     }
 
+    @Override
+    protected void drawDotIfNecessary(Canvas canvas) {
+        // This view (with the text label to the side of the icon) is not designed for a dot to be
+        // drawn on top of it, so never draw one even if a notification for this shortcut exists.
+    }
+
     private void showLoadingState(boolean loading) {
         if (loading == mShowLoadingState) {
             return;
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index e9b92e2..71d288c 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -18,7 +18,13 @@
 
 import android.content.Context;
 import android.content.pm.ShortcutInfo;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
 import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
@@ -30,18 +36,22 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.BubbleTextHolder;
 
 /**
- * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
- * This lets us animate the DeepShortcutView (icon and text) separately from the background.
+ * A {@link android.widget.FrameLayout} that contains an icon and a {@link BubbleTextView} for text.
+ * This lets us animate the child BubbleTextView's background (transparent ripple) separately from
+ * the {@link DeepShortcutView} background color.
  */
-public class DeepShortcutView extends FrameLayout {
+public class DeepShortcutView extends FrameLayout implements BubbleTextHolder {
 
     private static final Point sTempPoint = new Point();
 
+    private final Drawable mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT);
+
     private BubbleTextView mBubbleText;
     private View mIconView;
-    private View mDivider;
 
     private WorkspaceItemInfo mInfo;
     private ShortcutInfo mDetail;
@@ -63,13 +73,46 @@
         super.onFinishInflate();
         mBubbleText = findViewById(R.id.bubble_text);
         mIconView = findViewById(R.id.icon);
-        mDivider = findViewById(R.id.divider);
+        tryUpdateTextBackground();
     }
 
-    public void setDividerVisibility(int visibility) {
-        mDivider.setVisibility(visibility);
+    @Override
+    public void setBackground(Drawable background) {
+        super.setBackground(background);
+        tryUpdateTextBackground();
     }
 
+    @Override
+    public void setBackgroundResource(int resid) {
+        super.setBackgroundResource(resid);
+        tryUpdateTextBackground();
+    }
+
+    /**
+     * Updates the text background to match the shape of this background (when applicable).
+     */
+    private void tryUpdateTextBackground() {
+        if (!(getBackground() instanceof GradientDrawable) || mBubbleText == null) {
+            return;
+        }
+        GradientDrawable background = (GradientDrawable) getBackground();
+
+        int color = Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight);
+        GradientDrawable backgroundMask = new GradientDrawable();
+        backgroundMask.setColor(color);
+        backgroundMask.setShape(GradientDrawable.RECTANGLE);
+        if (background.getCornerRadii() != null) {
+            backgroundMask.setCornerRadii(background.getCornerRadii());
+        } else {
+            backgroundMask.setCornerRadius(background.getCornerRadius());
+        }
+
+        RippleDrawable drawable = new RippleDrawable(ColorStateList.valueOf(color),
+                mTransparentDrawable, backgroundMask);
+        mBubbleText.setBackground(drawable);
+    }
+
+    @Override
     public BubbleTextView getBubbleText() {
         return mBubbleText;
     }
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index 3e59b61..cecbb0d 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.FastBitmapDrawable;
 
 /**
  * Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
@@ -42,15 +43,16 @@
     }
 
     @Override
-    public Bitmap createDragBitmap() {
+    public Drawable createDrawable() {
         if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
             int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
-            return BitmapRenderer.createHardwareBitmap(
-                    size + blurSizeOutline,
-                    size + blurSizeOutline,
-                    (c) -> drawDragViewOnBackground(c, size));
+            return new FastBitmapDrawable(
+                    BitmapRenderer.createHardwareBitmap(
+                            size + blurSizeOutline,
+                            size + blurSizeOutline,
+                            (c) -> drawDragViewOnBackground(c, size)));
         } else {
-            return createDragBitmapLegacy();
+            return new FastBitmapDrawable(createDragBitmapLegacy());
         }
     }
 
@@ -81,7 +83,7 @@
     }
 
     @Override
-    public float getScaleAndPosition(Bitmap preview, int[] outPos) {
+    public float getScaleAndPosition(Drawable preview, int[] outPos) {
         Launcher launcher = Launcher.getLauncher(mView.getContext());
         int iconSize = getDrawableBounds(mView.getBackground()).width();
         float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos);
@@ -91,9 +93,10 @@
             iconLeft = mView.getWidth() - iconSize - iconLeft;
         }
 
-        outPos[0] += Math.round(scale * iconLeft + (scale * iconSize - preview.getWidth()) / 2 +
-                mPositionShift.x);
-        outPos[1] += Math.round((scale * mView.getHeight() - preview.getHeight()) / 2
+        outPos[0] += Math.round(
+                scale * iconLeft + (scale * iconSize - preview.getIntrinsicWidth()) / 2
+                        + mPositionShift.x);
+        outPos[1] += Math.round((scale * mView.getHeight() - preview.getIntrinsicHeight()) / 2
                 + mPositionShift.y);
         float size = launcher.getDeviceProfile().iconSizePx;
         return scale * iconSize / size;
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index 3ca9490..0c6d675 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -30,6 +30,10 @@
         return componentName.getClassName();
     }
 
+    public String getPackageName() {
+        return componentName.getPackageName();
+    }
+
     /**
      * Creates a {@link ShortcutRequest} for this key
      */
diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java
index daec1d8..122573c 100644
--- a/src/com/android/launcher3/statemanager/BaseState.java
+++ b/src/com/android/launcher3/statemanager/BaseState.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 
+import com.android.launcher3.DeviceProfile;
+
 /**
  * Interface representing a state of a StatefulActivity
  */
@@ -52,4 +54,11 @@
      * Returns if the state has the provided flag
      */
     boolean hasFlag(int flagMask);
+
+    /**
+     * For this state, whether tasks should layout as a grid rather than a list.
+     */
+    default boolean displayOverviewTasksAsGrid(DeviceProfile deviceProfile) {
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 2792308..b34af97 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -16,7 +16,10 @@
 
 package com.android.launcher3.statemanager;
 
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
+
+import static com.android.launcher3.anim.AnimatorPlaybackController.callListenerCommandRecursively;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
@@ -24,15 +27,12 @@
 import android.animation.AnimatorSet;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.Log;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -78,6 +78,15 @@
         return mCurrentStableState;
     }
 
+    @Override
+    public String toString() {
+        return " StateManager(mLastStableState:" + mLastStableState
+                + ", mCurrentStableState:" + mCurrentStableState
+                + ", mState:" + mState
+                + ", mRestState:" + mRestState
+                + ", isInTransition:" + (mConfig.currentAnimation != null) + ")";
+    }
+
     public void dump(String prefix, PrintWriter writer) {
         writer.println(prefix + "StateManager:");
         writer.println(prefix + "\tmLastStableState:" + mLastStableState);
@@ -89,7 +98,9 @@
 
     public StateHandler[] getStateHandlers() {
         if (mStateHandlers == null) {
-            mStateHandlers = mActivity.createStateHandlers();
+            ArrayList<StateHandler> handlers = new ArrayList<>();
+            mActivity.collectStateHandlers(handlers);
+            mStateHandlers = handlers.toArray(new StateHandler[handlers.size()]);
         }
         return mStateHandlers;
     }
@@ -119,14 +130,14 @@
     }
 
     /**
-     * @see #goToState(STATE_TYPE, boolean, Runnable)
+     * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
      */
     public void goToState(STATE_TYPE state) {
         goToState(state, shouldAnimateStateChange());
     }
 
     /**
-     * @see #goToState(STATE_TYPE, boolean, Runnable)
+     * @see #goToState(STATE_TYPE, boolean, AnimatorListener)
      */
     public void goToState(STATE_TYPE state, boolean animated) {
         goToState(state, animated, 0, null);
@@ -139,15 +150,15 @@
      *                true otherwise
      * @paras onCompleteRunnable any action to perform at the end of the transition, of null.
      */
-    public void goToState(STATE_TYPE state, boolean animated, Runnable onCompleteRunnable) {
-        goToState(state, animated, 0, onCompleteRunnable);
+    public void goToState(STATE_TYPE state, boolean animated, AnimatorListener listener) {
+        goToState(state, animated, 0, listener);
     }
 
     /**
      * Changes the Launcher state to the provided state after the given delay.
      */
-    public void goToState(STATE_TYPE state, long delay, Runnable onCompleteRunnable) {
-        goToState(state, true, delay, onCompleteRunnable);
+    public void goToState(STATE_TYPE state, long delay, AnimatorListener listener) {
+        goToState(state, true, delay, listener);
     }
 
     /**
@@ -177,21 +188,20 @@
         }
     }
 
-    private void goToState(STATE_TYPE state, boolean animated, long delay,
-            final Runnable onCompleteRunnable) {
-        animated &= Utilities.areAnimationsEnabled(mActivity);
+    private void goToState(
+            STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
+        animated &= areAnimatorsEnabled();
         if (mActivity.isInState(state)) {
             if (mConfig.currentAnimation == null) {
                 // Run any queued runnable
-                if (onCompleteRunnable != null) {
-                    onCompleteRunnable.run();
+                if (listener != null) {
+                    listener.onAnimationEnd(null);
                 }
                 return;
             } else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
                 // We are running the same animation as requested
-                if (onCompleteRunnable != null) {
-                    mConfig.currentAnimation.addListener(
-                            AnimationSuccessListener.forRunnable(onCompleteRunnable));
+                if (listener != null) {
+                    mConfig.currentAnimation.addListener(listener);
                 }
                 return;
             }
@@ -199,7 +209,7 @@
 
         // Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
         STATE_TYPE fromState = mState;
-        mConfig.reset();
+        cancelAnimation();
 
         if (!animated) {
             mAtomicAnimationFactory.cancelAllStateElementAnimation();
@@ -211,8 +221,8 @@
             onStateTransitionEnd(state);
 
             // Run any queued runnable
-            if (onCompleteRunnable != null) {
-                onCompleteRunnable.run();
+            if (listener != null) {
+                listener.onAnimationEnd(null);
             }
             return;
         }
@@ -223,16 +233,16 @@
             int startChangeId = mConfig.changeId;
             mUiHandler.postDelayed(() -> {
                 if (mConfig.changeId == startChangeId) {
-                    goToStateAnimated(state, fromState, onCompleteRunnable);
+                    goToStateAnimated(state, fromState, listener);
                 }
             }, delay);
         } else {
-            goToStateAnimated(state, fromState, onCompleteRunnable);
+            goToStateAnimated(state, fromState, listener);
         }
     }
 
     private void goToStateAnimated(STATE_TYPE state, STATE_TYPE fromState,
-            Runnable onCompleteRunnable) {
+            AnimatorListener listener) {
         // Since state mBaseState can be reached from multiple states, just assume that the
         // transition plays in reverse and use the same duration as previous state.
         mConfig.duration = state == mBaseState
@@ -240,8 +250,8 @@
                 : state.getTransitionDuration(mActivity);
         prepareForAtomicAnimation(fromState, state, mConfig);
         AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).buildAnim();
-        if (onCompleteRunnable != null) {
-            animation.addListener(AnimationSuccessListener.forRunnable(onCompleteRunnable));
+        if (listener != null) {
+            animation.addListener(listener);
         }
         mUiHandler.post(new StartAnimRunnable(animation));
     }
@@ -279,21 +289,21 @@
      */
     public AnimatorPlaybackController createAnimationToNewWorkspace(
             STATE_TYPE state, long duration) {
-        return createAnimationToNewWorkspace(state, duration, ANIM_ALL_COMPONENTS);
+        return createAnimationToNewWorkspace(state, duration, 0 /* animFlags */);
     }
 
     public AnimatorPlaybackController createAnimationToNewWorkspace(
-            STATE_TYPE state, long duration, @AnimationFlags int animComponents) {
+            STATE_TYPE state, long duration, @AnimationFlags int animFlags) {
         StateAnimationConfig config = new StateAnimationConfig();
         config.duration = duration;
-        config.animFlags = animComponents;
+        config.animFlags = animFlags;
         return createAnimationToNewWorkspace(state, config);
     }
 
     public AnimatorPlaybackController createAnimationToNewWorkspace(STATE_TYPE state,
             StateAnimationConfig config) {
         config.userControlled = true;
-        mConfig.reset();
+        cancelAnimation();
         config.copyTo(mConfig);
         mConfig.playbackController = createAnimationToNewWorkspaceInternal(state)
                 .createPlaybackController();
@@ -301,12 +311,8 @@
     }
 
     private PendingAnimation createAnimationToNewWorkspaceInternal(final STATE_TYPE state) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "createAnimationToNewWorkspaceInternal: "
-                    + state);
-        }
         PendingAnimation builder = new PendingAnimation(mConfig.duration);
-        if (mConfig.getAnimComponents() != 0) {
+        if (!mConfig.hasAnimationFlag(SKIP_ALL_ANIMATIONS)) {
             for (StateHandler handler : getStateHandlers()) {
                 handler.setStateWithAnimation(state, mConfig, builder);
             }
@@ -327,9 +333,6 @@
 
             @Override
             public void onAnimationSuccess(Animator animator) {
-                if (TestProtocol.sDebugTracing) {
-                    Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "onAnimationSuccess: " + state);
-                }
                 onStateTransitionEnd(state);
             }
         };
@@ -390,6 +393,11 @@
      */
     public void cancelAnimation() {
         mConfig.reset();
+        // It could happen that a new animation is set as a result of an endListener on the
+        // existing animation.
+        while (mConfig.currentAnimation != null || mConfig.playbackController != null) {
+            mConfig.reset();
+        }
     }
 
     public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
@@ -505,20 +513,28 @@
          * Cancels the current animation and resets config variables.
          */
         public void reset() {
+            AnimatorSet anim = currentAnimation;
+            AnimatorPlaybackController pc = playbackController;
+
             DEFAULT.copyTo(this);
             targetState = null;
-
-            if (playbackController != null) {
-                playbackController.getAnimationPlayer().cancel();
-                playbackController.dispatchOnCancel();
-            } else if (currentAnimation != null) {
-                currentAnimation.setDuration(0);
-                currentAnimation.cancel();
-            }
-
             currentAnimation = null;
             playbackController = null;
             changeId++;
+
+            if (pc != null) {
+                pc.getAnimationPlayer().cancel();
+                pc.dispatchOnCancel().dispatchOnEnd();
+            } else if (anim != null) {
+                anim.setDuration(0);
+                if (!anim.isStarted()) {
+                    // If the animation is not started the listeners do not get notified,
+                    // notify manually.
+                    callListenerCommandRecursively(anim, AnimatorListener::onAnimationCancel);
+                    callListenerCommandRecursively(anim, AnimatorListener::onAnimationEnd);
+                }
+                anim.cancel();
+            }
         }
 
         @Override
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 601e117..8a35cb3 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -30,6 +30,8 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.views.BaseDragLayer;
 
+import java.util.List;
+
 /**
  * Abstract activity with state management
  * @param <STATE_TYPE> Type of state object
@@ -46,7 +48,7 @@
     /**
      * Create handlers to control the property changes for this activity
      */
-    protected abstract StateHandler<STATE_TYPE>[] createStateHandlers();
+    protected abstract void collectStateHandlers(List<StateHandler> out);
 
     /**
      * Returns true if the activity is in the provided state
@@ -150,8 +152,8 @@
 
     private void handleDeferredResume() {
         if (hasBeenResumed() && !getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)) {
-            onDeferredResumed();
             addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
+            onDeferredResumed();
 
             mDeferredResumePending = false;
         } else {
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index b8a184f..8b52016 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -15,11 +15,16 @@
  */
 package com.android.launcher3.states;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+
 import android.content.Context;
 
+import androidx.core.graphics.ColorUtils;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 
 /**
  * Scale down workspace/hotseat to hint at going to either overview (on pause) or first home screen.
@@ -30,7 +35,11 @@
             | FLAG_HAS_SYS_UI_SCRIM;
 
     public HintState(int id) {
-        super(id, ContainerType.DEFAULT_CONTAINERTYPE, STATE_FLAGS);
+        this(id, LAUNCHER_STATE_HOME);
+    }
+
+    public HintState(int id, int statsLogOrdinal) {
+        super(id, statsLogOrdinal, STATE_FLAGS);
     }
 
     @Override
@@ -44,18 +53,13 @@
     }
 
     @Override
-    public float getOverviewScrimAlpha(Launcher launcher) {
-        return 0.4f;
+    public int getWorkspaceScrimColor(Launcher launcher) {
+        return ColorUtils.setAlphaComponent(
+                Themes.getAttrColor(launcher, R.attr.overviewScrimColor), 100);
     }
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
         return new ScaleAndTranslation(0.92f, 0, 0);
     }
-
-    @Override
-    public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
-        // Treat the QSB as part of the hotseat so they move together.
-        return getHotseatScaleAndTranslation(launcher);
-    }
 }
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 5a60f53..87871b1 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,63 +18,40 @@
 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 android.app.Activity;
-import android.content.ContentResolver;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.Settings;
-import android.util.Log;
 
-import com.android.launcher3.R;
+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;
 
 /**
  * Utility class to manage launcher rotation
  */
-public class RotationHelper implements OnSharedPreferenceChangeListener {
+public class RotationHelper implements OnSharedPreferenceChangeListener,
+        DeviceProfile.OnDeviceProfileChangeListener {
 
     private static final String TAG = "RotationHelper";
 
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
 
-    private final ContentResolver mContentResolver;
-    private boolean mSystemAutoRotateEnabled;
-
-    private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
-        @Override
-        public void onChange(boolean selfChange) {
-            updateAutoRotateSetting();
-        }
-    };
-
-    public static boolean getAllowRotationDefaultValue() {
-        // 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.
-        Resources res = Resources.getSystem();
-        int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
-                * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
-        return originalSmallestWidth >= 600;
-    }
-
     public static final int REQUEST_NONE = 0;
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
 
-    private final Activity mActivity;
-    private final SharedPreferences mSharedPrefs;
+    private BaseActivity mActivity;
+    private SharedPreferences mSharedPrefs = null;
 
     private boolean mIgnoreAutoRotateSettings;
+    private boolean mForceAllowRotationForTesting;
     private boolean mHomeRotationEnabled;
 
     /**
      * Rotation request made by
-     * {@link com.android.launcher3.util.ActivityTracker.SchedulerCallback}.
+     * {@link ActivityTracker.SchedulerCallback}.
      * This supersedes any other request.
      */
     private int mStateHandlerRequest = REQUEST_NONE;
@@ -91,45 +68,48 @@
     private boolean mInitialized;
     private boolean mDestroyed;
 
-    private int mLastActivityFlags = -1;
+    // Initialize mLastActivityFlags to a value not used by SCREEN_ORIENTATION flags
+    private int mLastActivityFlags = -999;
 
-    public RotationHelper(Activity activity) {
+    public RotationHelper(BaseActivity activity) {
         mActivity = activity;
-
-        // On large devices we do not handle auto-rotate differently.
-        mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
-        if (!mIgnoreAutoRotateSettings) {
-            mSharedPrefs = Utilities.getPrefs(mActivity);
-            mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
-            mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
-                    getAllowRotationDefaultValue());
-        } else {
-            mSharedPrefs = null;
-        }
-
-        mContentResolver = activity.getContentResolver();
     }
 
-    private void updateAutoRotateSetting() {
-        int autoRotateEnabled = 0;
-        try {
-            autoRotateEnabled = Settings.System.getInt(mContentResolver,
-                    Settings.System.ACCELEROMETER_ROTATION);
-        } catch (Settings.SettingNotFoundException e) {
-            Log.e(TAG, "autorotate setting not found", e);
+    private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings) {
+        // On large devices we do not handle auto-rotate differently.
+        mIgnoreAutoRotateSettings = ignoreAutoRotateSettings;
+        if (!mIgnoreAutoRotateSettings) {
+            if (mSharedPrefs == null) {
+                mSharedPrefs = Utilities.getPrefs(mActivity);
+                mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
+            }
+            mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+                    mActivity.getDeviceProfile().allowRotation);
+        } else {
+            if (mSharedPrefs != null) {
+                mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
+                mSharedPrefs = null;
+            }
         }
-
-        mSystemAutoRotateEnabled = autoRotateEnabled == 1;
     }
 
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+        if (mDestroyed) return;
         boolean wasRotationEnabled = mHomeRotationEnabled;
         mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
-                getAllowRotationDefaultValue());
+                mActivity.getDeviceProfile().allowRotation);
         if (mHomeRotationEnabled != wasRotationEnabled) {
             notifyChange();
-            updateAutoRotateSetting();
+        }
+    }
+
+    @Override
+    public void onDeviceProfileChanged(DeviceProfile dp) {
+        boolean ignoreAutoRotateSettings = dp.allowRotation;
+        if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) {
+            setIgnoreAutoRotateSettings(ignoreAutoRotateSettings);
+            notifyChange();
         }
     }
 
@@ -156,30 +136,27 @@
 
     // Used by tests only.
     public void forceAllowRotationForTesting(boolean allowRotation) {
-        mIgnoreAutoRotateSettings =
-                allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation);
+        mForceAllowRotationForTesting = allowRotation;
         notifyChange();
     }
 
     public void initialize() {
         if (!mInitialized) {
             mInitialized = true;
+            setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().allowRotation);
+            mActivity.addOnDeviceProfileChangeListener(this);
             notifyChange();
-
-            mContentResolver.registerContentObserver(
-                    Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
-                    false, mSystemAutoRotateObserver);
-            updateAutoRotateSetting();
         }
     }
 
     public void destroy() {
         if (!mDestroyed) {
             mDestroyed = true;
+            mActivity.removeOnDeviceProfileChangeListener(this);
+            mActivity = null;
             if (mSharedPrefs != null) {
                 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
             }
-            mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
         }
     }
 
@@ -198,7 +175,7 @@
         } else if (mCurrentStateRequest == REQUEST_LOCK) {
             activityFlags = SCREEN_ORIENTATION_LOCKED;
         } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE
-                || mHomeRotationEnabled) {
+                || mHomeRotationEnabled || mForceAllowRotationForTesting) {
             activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
         } else {
             // If auto rotation is off, allow rotation on the activity, in case the user is using
@@ -224,10 +201,10 @@
 
     @Override
     public String toString() {
-        return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
-                + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b,"
-                        + " mSystemAutoRotateEnabled=%b]",
+        return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d, "
+                        + "mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, "
+                        + "mHomeRotationEnabled=%b, mForceAllowRotationForTesting=%b]",
                 mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
-                mIgnoreAutoRotateSettings, mHomeRotationEnabled, mSystemAutoRotateEnabled);
+                mIgnoreAutoRotateSettings, mHomeRotationEnabled, mForceAllowRotationForTesting);
     }
 }
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 2a4f887..8db1dbe 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.states;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+
 import android.content.Context;
 import android.graphics.Rect;
 
@@ -22,7 +24,6 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 /**
  * Definition for spring loaded state used during drag and drop.
@@ -35,7 +36,7 @@
             | FLAG_HIDE_BACK_BUTTON;
 
     public SpringLoadedState(int id) {
-        super(id, ContainerType.WORKSPACE, STATE_FLAGS);
+        super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
     }
 
     @Override
@@ -58,10 +59,11 @@
 
         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() - insets.bottom
+        float shrunkBottom = ws.getMeasuredHeight() - insetsBottom
                 - grid.workspacePadding.bottom
                 - grid.workspaceSpringLoadedBottomSpace;
         float totalShrunkSpace = shrunkBottom - shrunkTop;
@@ -86,7 +88,12 @@
     }
 
     @Override
-    public float getWorkspaceScrimAlpha(Launcher launcher) {
-        return 0.3f;
+    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/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 8b72177..bd6f7d3 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -27,32 +27,23 @@
  */
 public class StateAnimationConfig {
 
-    // We separate the state animations into "atomic" and "non-atomic" components. The atomic
-    // components may be run atomically - that is, all at once, instead of user-controlled. However,
-    // atomic components are not restricted to this purpose; they can be user-controlled alongside
-    // non atomic components as well. Note that each gesture model has exactly one atomic component,
-    // PLAY_ATOMIC_OVERVIEW_SCALE *or* PLAY_ATOMIC_OVERVIEW_PEEK.
     @IntDef(flag = true, value = {
-            PLAY_NON_ATOMIC,
-            PLAY_ATOMIC_OVERVIEW_SCALE,
-            PLAY_ATOMIC_OVERVIEW_PEEK,
+            SKIP_ALL_ANIMATIONS,
             SKIP_OVERVIEW,
-            SKIP_DEPTH_CONTROLLER
+            SKIP_DEPTH_CONTROLLER,
+            SKIP_SCRIM,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimationFlags {}
-    public static final int PLAY_NON_ATOMIC = 1 << 0;
-    public static final int PLAY_ATOMIC_OVERVIEW_SCALE = 1 << 1;
-    public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
-    public static final int SKIP_OVERVIEW = 1 << 3;
-    public static final int SKIP_DEPTH_CONTROLLER = 1 << 4;
+    public static final int SKIP_ALL_ANIMATIONS = 1 << 0;
+    public static final int SKIP_OVERVIEW = 1 << 1;
+    public static final int SKIP_DEPTH_CONTROLLER = 1 << 2;
+    public static final int SKIP_SCRIM = 1 << 3;
 
     public long duration;
     public boolean userControlled;
-    public @AnimationFlags int animFlags = ANIM_ALL_COMPONENTS;
+    public @AnimationFlags int animFlags = 0;
 
-    public static final int ANIM_ALL_COMPONENTS = PLAY_NON_ATOMIC | PLAY_ATOMIC_OVERVIEW_SCALE
-            | PLAY_ATOMIC_OVERVIEW_PEEK;
 
     // Various types of animation state transition
     @IntDef(value = {
@@ -67,8 +58,7 @@
             ANIM_OVERVIEW_TRANSLATE_Y,
             ANIM_OVERVIEW_FADE,
             ANIM_ALL_APPS_FADE,
-            ANIM_OVERVIEW_SCRIM_FADE,
-            ANIM_ALL_APPS_HEADER_FADE,
+            ANIM_SCRIM_FADE,
             ANIM_OVERVIEW_MODAL,
             ANIM_DEPTH,
             ANIM_OVERVIEW_ACTIONS_FADE,
@@ -86,13 +76,12 @@
     public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
     public static final int ANIM_OVERVIEW_FADE = 9;
     public static final int ANIM_ALL_APPS_FADE = 10;
-    public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
-    public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
-    public static final int ANIM_OVERVIEW_MODAL = 13;
-    public static final int ANIM_DEPTH = 14;
-    public static final int ANIM_OVERVIEW_ACTIONS_FADE = 15;
+    public static final int ANIM_SCRIM_FADE = 11;
+    public static final int ANIM_OVERVIEW_MODAL = 12;
+    public static final int ANIM_DEPTH = 13;
+    public static final int ANIM_OVERVIEW_ACTIONS_FADE = 14;
 
-    private static final int ANIM_TYPES_COUNT = 16;
+    private static final int ANIM_TYPES_COUNT = 15;
 
     protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
 
@@ -127,37 +116,9 @@
     }
 
     /**
-     * @return Whether Overview is scaling as part of this animation. If this is the only
-     * component (i.e. NON_ATOMIC_COMPONENT isn't included), then this scaling is happening
-     * atomically, rather than being part of a normal state animation. StateHandlers can use
-     * this to designate part of their animation that should scale with Overview.
-     */
-    public boolean playAtomicOverviewScaleComponent() {
-        return hasAnimationFlag(StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE);
-    }
-
-    /**
-     * @return Whether this animation will play atomically at the same time as a different,
-     * user-controlled state transition. StateHandlers, which contribute to both animations, can
-     * use this to avoid animating the same properties in both animations, since they'd conflict
-     * with one another.
-     */
-    public boolean onlyPlayAtomicComponent() {
-        return getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE
-                || getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-    }
-
-    /**
      * Returns true if the config and any of the provided component flags
      */
     public boolean hasAnimationFlag(@AnimationFlags int a) {
         return (animFlags & a) != 0;
     }
-
-    /**
-     * @return Only the flags that determine which animation components to play.
-     */
-    public @AnimationFlags int getAnimComponents() {
-        return animFlags & StateAnimationConfig.ANIM_ALL_COMPONENTS;
-    }
 }
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 38b3712..4261d08 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
 
 import java.util.concurrent.ExecutionException;
 import java.util.function.Function;
@@ -92,6 +93,11 @@
                         l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
             }
 
+            case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
+                return getLauncherUIProperty(Bundle::putInt,
+                        l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
+            }
+
             case TestProtocol.REQUEST_WINDOW_INSETS: {
                 return getUIProperty(Bundle::putParcelable, a -> {
                     WindowInsets insets = a.getWindow()
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 58bff09..b6da7fc 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -27,12 +27,13 @@
     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;
-    public static final int OVERVIEW_PEEK_STATE_ORDINAL = 3;
-    public static final int OVERVIEW_MODAL_TASK_STATE_ORDINAL = 4;
-    public static final int QUICK_SWITCH_STATE_ORDINAL = 5;
-    public static final int ALL_APPS_STATE_ORDINAL = 6;
-    public static final int BACKGROUND_APP_STATE_ORDINAL = 7;
-    public static final int HINT_STATE_ORDINAL = 8;
+    public static final int OVERVIEW_MODAL_TASK_STATE_ORDINAL = 3;
+    public static final int QUICK_SWITCH_STATE_ORDINAL = 4;
+    public static final int ALL_APPS_STATE_ORDINAL = 5;
+    public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
+    public static final int HINT_STATE_ORDINAL = 7;
+    public static final int HINT_STATE_TWO_BUTTON_ORDINAL = 8;
+    public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 9;
     public static final String TAPL_EVENTS_TAG = "TaplEvents";
     public static final String SEQUENCE_MAIN = "Main";
     public static final String SEQUENCE_TIS = "TIS";
@@ -46,8 +47,6 @@
                 return "SpringLoaded";
             case OVERVIEW_STATE_ORDINAL:
                 return "Overview";
-            case OVERVIEW_PEEK_STATE_ORDINAL:
-                return "OverviewPeek";
             case OVERVIEW_MODAL_TASK_STATE_ORDINAL:
                 return "OverviewModal";
             case QUICK_SWITCH_STATE_ORDINAL:
@@ -58,6 +57,10 @@
                 return "Background";
             case HINT_STATE_ORDINAL:
                 return "Hint";
+            case HINT_STATE_TWO_BUTTON_ORDINAL:
+                return "Hint2Button";
+            case OVERVIEW_SPLIT_SELECT_ORDINAL:
+                return "OverviewSplitSelect";
             default:
                 return "Unknown";
         }
@@ -81,11 +84,10 @@
     public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
     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_WINDOW_INSETS = "window-insets";
     public static final String REQUEST_PID = "pid";
-    public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
-    public static final String REQUEST_JAVA_LEAK = "java-leak";
-    public static final String REQUEST_NATIVE_LEAK = "native-leak";
+    public static final String REQUEST_FORCE_GC = "gc";
     public static final String REQUEST_VIEW_LEAK = "view-leak";
     public static final String REQUEST_RECENT_TASKS_LIST = "recent-tasks-list";
     public static final String REQUEST_START_EVENT_LOGGING = "start-event-logging";
@@ -97,15 +99,14 @@
     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_ACTIONS_ENABLED = "overview-actions-enabled";
     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 PAUSE_NOT_DETECTED = "b/139891609";
-    public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
-    public static final String NO_SWIPE_TO_HOME = "b/158017601";
     public static final String WORK_PROFILE_REMOVED = "b/159671700";
+    public static final String FALLBACK_ACTIVITY_NO_SET = "b/181019015";
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 3c78b08..5f8a4d4 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -15,43 +15,32 @@
  */
 package com.android.launcher3.touch;
 
-import static com.android.launcher3.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 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;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
+import android.animation.Animator.AnimatorListener;
 import android.animation.ValueAnimator;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.FlingBlockCheck;
 import com.android.launcher3.util.TouchController;
 
@@ -61,51 +50,30 @@
 public abstract class AbstractStateChangeTouchController
         implements TouchController, SingleAxisSwipeDetector.Listener {
 
-    // Progress after which the transition is assumed to be a success in case user does not fling
-    public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
-
-    /**
-     * Play an atomic recents animation when the progress from NORMAL to OVERVIEW reaches this.
-     */
-    public static final float ATOMIC_OVERVIEW_ANIM_THRESHOLD = 0.5f;
-    protected final long ATOMIC_DURATION = getAtomicDuration();
-
     protected final Launcher mLauncher;
     protected final SingleAxisSwipeDetector mDetector;
     protected final SingleAxisSwipeDetector.Direction mSwipeDirection;
 
-    private boolean mNoIntercept;
-    private boolean mIsLogContainerSet;
+    protected final AnimatorListener mClearStateOnCancelListener =
+            newCancelListener(this::clearState);
+    private final FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
+
     protected int mStartContainerType;
 
     protected LauncherState mStartState;
     protected LauncherState mFromState;
     protected LauncherState mToState;
     protected AnimatorPlaybackController mCurrentAnimation;
-    protected PendingAnimation mPendingAnimation;
-
-    private float mStartProgress;
+    protected boolean mGoingBetweenStates = true;
     // Ratio of transition process [0, 1] to drag displacement (px)
-    private float mProgressMultiplier;
+    protected float mProgressMultiplier;
+
+    private boolean mNoIntercept;
+    private boolean mIsLogContainerSet;
+    private float mStartProgress;
     private float mDisplacementShift;
     private boolean mCanBlockFling;
-    private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
-
-    protected AnimatorSet mAtomicAnim;
-    // True if we want to resume playing atomic components when mAtomicAnim completes.
-    private boolean mScheduleResumeAtomicComponent;
-    private AutoPlayAtomicAnimationInfo mAtomicAnimAutoPlayInfo;
-
-    private boolean mPassedOverviewAtomicThreshold;
-    // mAtomicAnim plays the atomic components of the state animations when we pass the threshold.
-    // However, if we reinit to transition to a new state (e.g. OVERVIEW -> ALL_APPS) before the
-    // atomic animation finishes, we only control the non-atomic components so that we don't
-    // interfere with the atomic animation. When the atomic animation ends, we start controlling
-    // the atomic components as well, using this controller.
-    private AnimatorPlaybackController mAtomicComponentsController;
-    private LauncherState mAtomicComponentsTargetState = NORMAL;
-
-    private float mAtomicComponentsStartProgress;
+    private boolean mAllAppsOvershootStarted;
 
     public AbstractStateChangeTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
         mLauncher = l;
@@ -113,14 +81,10 @@
         mSwipeDirection = dir;
     }
 
-    protected long getAtomicDuration() {
-        return 200;
-    }
-
     protected abstract boolean canInterceptTouch(MotionEvent ev);
 
     @Override
-    public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mNoIntercept = !canInterceptTouch(ev);
             if (mNoIntercept) {
@@ -168,9 +132,6 @@
 
     @Override
     public final boolean onControllerTouchEvent(MotionEvent ev) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "onControllerTouchEvent");
-        }
         return mDetector.onTouchEvent(ev);
     }
 
@@ -185,61 +146,35 @@
     protected abstract LauncherState getTargetState(LauncherState fromState,
             boolean isDragTowardPositive);
 
-    protected abstract float initCurrentAnimation(@AnimationFlags int animComponents);
-
-    /**
-     * Returns the container that the touch started from when leaving NORMAL state.
-     */
-    protected abstract int getLogContainerTypeForNormalState(MotionEvent ev);
+    protected abstract float initCurrentAnimation();
 
     private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
         LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
                 : reachedToState ? mToState : mFromState;
         LauncherState newToState = getTargetState(newFromState, isDragTowardPositive);
 
+        onReinitToState(newToState);
+
         if (newFromState == mFromState && newToState == mToState || (newFromState == newToState)) {
             return false;
         }
 
         mFromState = newFromState;
         mToState = newToState;
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "reinitCurrentAnimation: "
-                    + newToState.ordinal + " " + getClass().getSimpleName());
-        }
 
         mStartProgress = 0;
-        mPassedOverviewAtomicThreshold = false;
         if (mCurrentAnimation != null) {
-            mCurrentAnimation.setOnCancelRunnable(null);
+            mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
         }
-        int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
-                ? PLAY_NON_ATOMIC : ANIM_ALL_COMPONENTS;
-        mScheduleResumeAtomicComponent = false;
-        if (mAtomicAnim != null) {
-            animComponents = PLAY_NON_ATOMIC;
-            // Control the non-atomic components until the atomic animation finishes, then control
-            // the atomic components as well.
-            mScheduleResumeAtomicComponent = true;
-        }
-        if (goingBetweenNormalAndOverview(mFromState, mToState)
-                || mAtomicComponentsTargetState != mToState) {
-            cancelAtomicComponentsController();
-        }
-
-        if (mAtomicComponentsController != null) {
-            animComponents &= ~PLAY_ATOMIC_OVERVIEW_SCALE;
-        }
-        mProgressMultiplier = initCurrentAnimation(animComponents);
+        mProgressMultiplier = initCurrentAnimation();
         mCurrentAnimation.dispatchOnStart();
         return true;
     }
 
-    protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
-            LauncherState toState) {
-        return (fromState == NORMAL || fromState == OVERVIEW)
-                && (toState == NORMAL || toState == OVERVIEW)
-                && mPendingAnimation == null;
+    protected void onReinitToState(LauncherState newToState) {
+    }
+
+    protected void onReachedFinalState(LauncherState newToState) {
     }
 
     @Override
@@ -256,18 +191,9 @@
         } else {
             mCurrentAnimation.pause();
             mStartProgress = mCurrentAnimation.getProgressFraction();
-
-            mAtomicAnimAutoPlayInfo = null;
-            if (mAtomicComponentsController != null) {
-                mAtomicComponentsController.pause();
-            }
         }
         mCanBlockFling = mFromState == NORMAL;
         mFlingBlockCheck.unblockFling();
-        // Must be called after all the animation controllers have been paused
-        if (mToState == ALL_APPS || mToState == NORMAL) {
-            mLauncher.getAllAppsController().onDragStart(mToState == ALL_APPS);
-        }
     }
 
     @Override
@@ -291,8 +217,15 @@
                     mFlingBlockCheck.blockFling();
                 }
             }
+            if (mToState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+                mAllAppsOvershootStarted = true;
+                // 1f, value when all apps container hit the top
+                mLauncher.getAppsView().onPull(progress - 1f, progress - 1f);
+            }
+
         } else {
             mFlingBlockCheck.onEvent();
+
         }
 
         return true;
@@ -302,11 +235,11 @@
     public boolean onDrag(float displacement, MotionEvent ev) {
         if (!mIsLogContainerSet) {
             if (mStartState == ALL_APPS) {
-                mStartContainerType = ContainerType.ALLAPPS;
+                mStartContainerType = LAUNCHER_STATE_ALLAPPS;
             } else if (mStartState == NORMAL) {
-                mStartContainerType = getLogContainerTypeForNormalState(ev);
+                mStartContainerType = LAUNCHER_STATE_HOME;
             } else if (mStartState == OVERVIEW) {
-                mStartContainerType = ContainerType.TASKSWITCHER;
+                mStartContainerType = LAUNCHER_STATE_OVERVIEW;
             }
             mIsLogContainerSet = true;
         }
@@ -318,71 +251,6 @@
             return;
         }
         mCurrentAnimation.setPlayFraction(fraction);
-        if (mAtomicComponentsController != null) {
-            // Make sure we don't divide by 0, and have at least a small runway.
-            float start = Math.min(mAtomicComponentsStartProgress, 0.9f);
-            mAtomicComponentsController.setPlayFraction((fraction - start) / (1 - start));
-        }
-        maybeUpdateAtomicAnim(mFromState, mToState, fraction);
-    }
-
-    /**
-     * When going between normal and overview states, see if we passed the overview threshold and
-     * play the appropriate atomic animation if so.
-     */
-    private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
-            float progress) {
-        if (!goingBetweenNormalAndOverview(fromState, toState)) {
-            return;
-        }
-        float threshold = toState == OVERVIEW ? ATOMIC_OVERVIEW_ANIM_THRESHOLD
-                : 1f - ATOMIC_OVERVIEW_ANIM_THRESHOLD;
-        boolean passedThreshold = progress >= threshold;
-        if (passedThreshold != mPassedOverviewAtomicThreshold) {
-            LauncherState atomicFromState = passedThreshold ? fromState: toState;
-            LauncherState atomicToState = passedThreshold ? toState : fromState;
-            mPassedOverviewAtomicThreshold = passedThreshold;
-            if (mAtomicAnim != null) {
-                mAtomicAnim.cancel();
-            }
-            mAtomicAnim = createAtomicAnimForState(atomicFromState, atomicToState, ATOMIC_DURATION);
-            mAtomicAnim.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    super.onAnimationEnd(animation);
-                    mAtomicAnim = null;
-                    mScheduleResumeAtomicComponent = false;
-                }
-
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (!mScheduleResumeAtomicComponent) {
-                        return;
-                    }
-                    cancelAtomicComponentsController();
-
-                    if (mCurrentAnimation != null) {
-                        mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
-                        long duration = (long) (getShiftRange() * 2);
-                        mAtomicComponentsController = AnimatorPlaybackController.wrap(
-                                createAtomicAnimForState(mFromState, mToState, duration), duration);
-                        mAtomicComponentsController.dispatchOnStart();
-                        mAtomicComponentsTargetState = mToState;
-                        maybeAutoPlayAtomicComponentsAnim();
-                    }
-                }
-            });
-            mAtomicAnim.start();
-            mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
-        }
-    }
-
-    private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
-            long duration) {
-        StateAnimationConfig config = getConfigForStates(fromState, targetState);
-        config.animFlags = PLAY_ATOMIC_OVERVIEW_SCALE;
-        config.duration = duration;
-        return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, config);
     }
 
     /**
@@ -395,8 +263,13 @@
 
     @Override
     public void onDragEnd(float velocity) {
+        if (mCurrentAnimation == null) {
+            // Unlikely, but we may have been canceled just before onDragEnd(). We assume whoever
+            // canceled us will handle a new state transition to clean up.
+            return;
+        }
+
         boolean fling = mDetector.isFling(velocity);
-        final int logAction = fling ? Touch.FLING : Touch.SWIPE;
 
         boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
         if (blockedFling) {
@@ -413,9 +286,8 @@
                             ? mToState : mFromState;
             // snap to top or bottom using the release velocity
         } else {
-            float successProgress = mToState == ALL_APPS
-                    ? MIN_PROGRESS_TO_ALL_APPS : SUCCESS_TRANSITION_PROGRESS;
-            targetState = (interpolatedProgress > successProgress) ? mToState : mFromState;
+            targetState =
+                    (interpolatedProgress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
         }
 
         final float endProgress;
@@ -439,7 +311,8 @@
         } else {
             // Let the state manager know that the animation didn't go to the target state,
             // but don't cancel ourselves (we already clean up when the animation completes).
-            mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable();
+            mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
+            mCurrentAnimation.dispatchOnCancel();
 
             endProgress = 0;
             if (progress <= 0) {
@@ -452,71 +325,23 @@
                         Math.min(progress, 1) - endProgress) * durationMultiplier;
             }
         }
-
-        mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction));
+        if (targetState != mStartState) {
+            logReachedState(targetState);
+        }
+        mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState));
         ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
         anim.setFloatValues(startProgress, endProgress);
-        maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
-        updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
-                targetState, velocity, fling);
+        updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
         mCurrentAnimation.dispatchOnStart();
-        if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
-            mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
+        if (targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
+            if (mAllAppsOvershootStarted) {
+                mLauncher.getAppsView().onRelease();
+                mAllAppsOvershootStarted = false;
+            } else {
+                mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity, progress);
+            }
         }
         anim.start();
-        mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, anim.getDuration());
-        maybeAutoPlayAtomicComponentsAnim();
-    }
-
-    /**
-     * Animates the atomic components from the current progress to the final progress.
-     *
-     * Note that this only applies when we are controlling the atomic components separately from
-     * the non-atomic components, which only happens if we reinit before the atomic animation
-     * finishes.
-     */
-    private void maybeAutoPlayAtomicComponentsAnim() {
-        if (mAtomicComponentsController == null || mAtomicAnimAutoPlayInfo == null) {
-            return;
-        }
-
-        final AnimatorPlaybackController controller = mAtomicComponentsController;
-        ValueAnimator atomicAnim = controller.getAnimationPlayer();
-        atomicAnim.setFloatValues(controller.getProgressFraction(),
-                mAtomicAnimAutoPlayInfo.toProgress);
-        long duration = mAtomicAnimAutoPlayInfo.endTime - SystemClock.elapsedRealtime();
-        mAtomicAnimAutoPlayInfo = null;
-        if (duration <= 0) {
-            atomicAnim.start();
-            atomicAnim.end();
-            mAtomicComponentsController = null;
-        } else {
-            atomicAnim.setDuration(duration);
-            atomicAnim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mAtomicComponentsController == controller) {
-                        mAtomicComponentsController = null;
-                    }
-                }
-            });
-            atomicAnim.start();
-        }
-    }
-
-    private long getRemainingAtomicDuration() {
-        if (mAtomicAnim == null) {
-            return 0;
-        }
-        if (Utilities.ATLEAST_OREO) {
-            return mAtomicAnim.getTotalDuration() - mAtomicAnim.getCurrentPlayTime();
-        } else {
-            long remainingDuration = 0;
-            for (Animator anim : mAtomicAnim.getChildAnimations()) {
-                remainingDuration = Math.max(remainingDuration, anim.getDuration());
-            }
-            return remainingDuration;
-        }
     }
 
     protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
@@ -525,94 +350,49 @@
                 .setInterpolator(scrollInterpolatorForVelocity(velocity));
     }
 
-    protected int getDirectionForLog() {
-        return mToState.ordinal > mFromState.ordinal ? Direction.UP : Direction.DOWN;
-    }
-
-    protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
-        if (mAtomicComponentsController != null) {
-            mAtomicComponentsController.getAnimationPlayer().end();
-            mAtomicComponentsController = null;
-        }
+    protected void onSwipeInteractionCompleted(LauncherState targetState) {
+        onReachedFinalState(mToState);
         clearState();
-        boolean shouldGoToTargetState = true;
-        if (mPendingAnimation != null) {
-            boolean reachedTarget = mToState == targetState;
-            mPendingAnimation.finish(reachedTarget, logAction);
-            mPendingAnimation = null;
-            shouldGoToTargetState = !reachedTarget;
-        }
+        boolean shouldGoToTargetState = mGoingBetweenStates || (mToState != targetState);
         if (shouldGoToTargetState) {
-            goToTargetState(targetState, logAction);
+            goToTargetState(targetState);
         }
     }
 
-    protected void goToTargetState(LauncherState targetState, int logAction) {
-        if (targetState != mStartState) {
-            logReachedState(logAction, targetState);
-        }
+    protected void goToTargetState(LauncherState targetState) {
         if (!mLauncher.isInState(targetState)) {
             // If we're already in the target state, don't jump to it at the end of the animation in
             // case the user started interacting with it before the animation finished.
             mLauncher.getStateManager().goToState(targetState, false /* animated */);
         }
-        mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(1f).setDuration(0).start();
+        mLauncher.getRootView().getSysUiScrim().createSysuiMultiplierAnim(
+                1f).setDuration(0).start();
     }
 
-    private void logReachedState(int logAction, LauncherState targetState) {
+    private void logReachedState(LauncherState targetState) {
         // Transition complete. log the action
-        mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
-                getDirectionForLog(), mDetector.getDownX(), mDetector.getDownY(),
-                mStartContainerType /* e.g., hotseat */,
-                mStartState.containerType /* e.g., workspace */,
-                targetState.containerType,
-                mLauncher.getWorkspace().getCurrentPage());
         mLauncher.getStatsLogManager().logger()
-                .withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
-                .withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
+                .withSrcState(mStartState.statsLogOrdinal)
+                .withDstState(targetState.statsLogOrdinal)
                 .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
                         .setWorkspace(
                                 LauncherAtom.WorkspaceContainer.newBuilder()
                                         .setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
                         .build())
-                .log(StatsLogManager.getLauncherAtomEvent(mStartState.containerType,
-                            targetState.containerType, mToState.ordinal > mFromState.ordinal
+                .log(StatsLogManager.getLauncherAtomEvent(mStartState.statsLogOrdinal,
+                            targetState.statsLogOrdinal, mToState.ordinal > mFromState.ordinal
                                     ? LAUNCHER_UNKNOWN_SWIPEUP
                                     : LAUNCHER_UNKNOWN_SWIPEDOWN));
     }
 
     protected void clearState() {
         cancelAnimationControllers();
-        if (mAtomicAnim != null) {
-            mAtomicAnim.cancel();
-            mAtomicAnim = null;
-        }
-        mScheduleResumeAtomicComponent = false;
+        mGoingBetweenStates = true;
         mDetector.finishedScrolling();
         mDetector.setDetectableScrollConditions(0, false);
     }
 
     private void cancelAnimationControllers() {
         mCurrentAnimation = null;
-        cancelAtomicComponentsController();
-    }
-
-    private void cancelAtomicComponentsController() {
-        if (mAtomicComponentsController != null) {
-            mAtomicComponentsController.getAnimationPlayer().cancel();
-            mAtomicComponentsController = null;
-        }
-        mAtomicAnimAutoPlayInfo = null;
-    }
-
-    private static class AutoPlayAtomicAnimationInfo {
-
-        public final float toProgress;
-        public final long endTime;
-
-        AutoPlayAtomicAnimationInfo(float toProgress, long duration) {
-            this.toProgress = toProgress;
-            this.endTime = duration + SystemClock.elapsedRealtime();
-        }
     }
 }
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 4a202b6..ab2652a 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -23,25 +23,18 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 /**
  * TouchController to switch between NORMAL and ALL_APPS state.
  */
 public class AllAppsSwipeController extends AbstractStateChangeTouchController {
 
-    private MotionEvent mTouchDownEvent;
-
     public AllAppsSwipeController(Launcher l) {
         super(l, SingleAxisSwipeDetector.VERTICAL);
     }
 
     @Override
     protected boolean canInterceptTouch(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mTouchDownEvent = ev;
-        }
         if (mCurrentAnimation != null) {
             // If we are already animating from a previous state, we can intercept.
             return true;
@@ -70,17 +63,11 @@
     }
 
     @Override
-    protected int getLogContainerTypeForNormalState(MotionEvent ev) {
-        return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent)
-                ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
-    }
-
-    @Override
-    protected float initCurrentAnimation(@AnimationFlags int animComponents) {
+    protected float initCurrentAnimation() {
         float range = getShiftRange();
         long maxAccuracy = (long) (2 * range);
         mCurrentAnimation = mLauncher.getStateManager()
-                .createAnimationToNewWorkspace(mToState, maxAccuracy, animComponents);
+                .createAnimationToNewWorkspace(mToState, maxAccuracy);
         float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
         float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
         float totalShift = endVerticalShift - startVerticalShift;
diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
index 01b33d8..1276ece 100644
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -26,8 +26,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.testing.TestProtocol;
-
 import java.util.LinkedList;
 import java.util.Queue;
 
@@ -175,9 +173,6 @@
                 if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
                     setState(ScrollState.DRAGGING);
                 }
-                if (TestProtocol.sDebugTracing) {
-                    Log.d(TestProtocol.PAUSE_NOT_DETECTED, "before report dragging");
-                }
                 if (mState == ScrollState.DRAGGING) {
                     reportDragging(ev);
                 }
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index f50b2b3..b53f96e 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -17,9 +17,8 @@
 
 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_APP_LAUNCH_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_HOTSEAT;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
@@ -27,7 +26,10 @@
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 
 import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.os.Process;
@@ -38,12 +40,8 @@
 import android.view.View.OnClickListener;
 import android.widget.Toast;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.folder.Folder;
@@ -52,14 +50,16 @@
 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.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.SearchActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.views.FloatingIconView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 import com.android.launcher3.widget.WidgetManagerHelper;
@@ -74,13 +74,9 @@
     /**
      * Instance used for click handling on items
      */
-    public static final OnClickListener INSTANCE = getInstance(null);
+    public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
 
-    public static final OnClickListener getInstance(String sourceContainer) {
-        return v -> onClick(v, sourceContainer);
-    }
-
-    private static void onClick(View v, String sourceContainer) {
+    private static void onClick(View v) {
         // Make sure that rogue clicks don't get through while allapps is launching, or after the
         // view has detached (it's possible for this to happen if the view is removed mid touch).
         if (v.getWindowToken() == null) return;
@@ -90,18 +86,20 @@
 
         Object tag = v.getTag();
         if (tag instanceof WorkspaceItemInfo) {
-            onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher, sourceContainer);
+            onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
         } else if (tag instanceof FolderInfo) {
             if (v instanceof FolderIcon) {
                 onClickFolderIcon(v);
             }
         } else if (tag instanceof AppInfo) {
-            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher,
-                    sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer);
+            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher
+            );
         } else if (tag instanceof LauncherAppWidgetInfo) {
             if (v instanceof PendingAppWidgetHostView) {
                 onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
             }
+        } else if (tag instanceof SearchActionItemInfo) {
+            onClickSearchAction(launcher, (SearchActionItemInfo) tag);
         }
     }
 
@@ -183,7 +181,7 @@
                 LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
                 try {
                     launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
-                            launcher.getActivityLaunchOptionsAsBundle(v));
+                            launcher.getActivityLaunchOptions(v, item).toBundle());
                     return;
                 } catch (Exception e) {
                     Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
@@ -193,7 +191,40 @@
 
         // Fallback to using custom market intent.
         Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
-        launcher.startActivitySafely(v, intent, item, null);
+        launcher.startActivitySafely(v, intent, item);
+    }
+
+    /**
+     * Handles clicking on a disabled shortcut
+     *
+     * @return true iff the disabled item click has been handled.
+     */
+    public static boolean handleDisabledItemClicked(WorkspaceItemInfo shortcut, Context context) {
+        final int disabledFlags = shortcut.runtimeStatusFlags
+                & WorkspaceItemInfo.FLAG_DISABLED_MASK;
+        if ((disabledFlags
+                & ~FLAG_DISABLED_SUSPENDED
+                & ~FLAG_DISABLED_QUIET_USER) == 0) {
+            // If the app is only disabled because of the above flags, launch activity anyway.
+            // Framework will tell the user why the app is suspended.
+            return false;
+        } else {
+            if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
+                // Use a message specific to this shortcut, if it has one.
+                Toast.makeText(context, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
+                return true;
+            }
+            // Otherwise just use a generic error message.
+            int error = R.string.activity_not_available;
+            if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
+                error = R.string.safemode_shortcut_error;
+            } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0
+                    || (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
+                error = R.string.shortcut_not_available;
+            }
+            Toast.makeText(context, error, Toast.LENGTH_SHORT).show();
+            return true;
+        }
     }
 
     /**
@@ -201,33 +232,9 @@
      *
      * @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
      */
-    public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher,
-            @Nullable String sourceContainer) {
-        if (shortcut.isDisabled()) {
-            final int disabledFlags = shortcut.runtimeStatusFlags
-                    & WorkspaceItemInfo.FLAG_DISABLED_MASK;
-            if ((disabledFlags &
-                    ~FLAG_DISABLED_SUSPENDED &
-                    ~FLAG_DISABLED_QUIET_USER) == 0) {
-                // If the app is only disabled because of the above flags, launch activity anyway.
-                // Framework will tell the user why the app is suspended.
-            } else {
-                if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
-                    // Use a message specific to this shortcut, if it has one.
-                    Toast.makeText(launcher, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
-                    return;
-                }
-                // Otherwise just use a generic error message.
-                int error = R.string.activity_not_available;
-                if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
-                    error = R.string.safemode_shortcut_error;
-                } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
-                        (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
-                    error = R.string.shortcut_not_available;
-                }
-                Toast.makeText(launcher, error, Toast.LENGTH_SHORT).show();
-                return;
-            }
+    public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
+        if (shortcut.isDisabled() && handleDisabledItemClicked(shortcut, launcher)) {
+            return;
         }
 
         // Check for abandoned promise
@@ -236,30 +243,60 @@
                     ? shortcut.getIntent().getComponent().getPackageName()
                     : shortcut.getIntent().getPackage();
             if (!TextUtils.isEmpty(packageName)) {
-                onClickPendingAppItem(v, launcher, packageName,
-                        shortcut.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE));
+                onClickPendingAppItem(
+                        v,
+                        launcher,
+                        packageName,
+                        (shortcut.runtimeStatusFlags
+                                & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0);
                 return;
             }
         }
 
-        if (sourceContainer == null && (
-                shortcut.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
-                        || shortcut.container
-                        == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION)) {
-            sourceContainer = CONTAINER_HOTSEAT;
-        }
         // Start activities
-        startAppShortcutOrInfoActivity(v, shortcut, launcher, sourceContainer);
+        startAppShortcutOrInfoActivity(v, shortcut, launcher);
     }
 
-    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher,
-            @Nullable String sourceContainer) {
+    /**
+     * Event handler for a {@link SearchActionItemInfo} click
+     */
+    public static void onClickSearchAction(Launcher launcher, SearchActionItemInfo itemInfo) {
+        if (itemInfo.getIntent() != null) {
+            if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
+                launcher.startActivityForResult(itemInfo.getIntent(), 0);
+            } else {
+                launcher.startActivity(itemInfo.getIntent());
+            }
+        } else if (itemInfo.getPendingIntent() != null) {
+            try {
+                PendingIntent pendingIntent = itemInfo.getPendingIntent();
+                if (!itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START)) {
+                    pendingIntent.send();
+                } else if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
+                    launcher.startIntentSenderForResult(pendingIntent.getIntentSender(), 0, null, 0,
+                            0, 0);
+                } else {
+                    launcher.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
+                }
+            } catch (PendingIntent.CanceledException | IntentSender.SendIntentException e) {
+                Toast.makeText(launcher,
+                        launcher.getResources().getText(R.string.shortcut_not_available),
+                        Toast.LENGTH_SHORT).show();
+            }
+        }
+        launcher.getStatsLogManager().logger().withItemInfo(itemInfo).log(LAUNCHER_APP_LAUNCH_TAP);
+    }
+
+    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
         TestLogging.recordEvent(
                 TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
         Intent intent;
-        if (item instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
-            intent = promiseAppInfo.getMarketIntent(launcher);
+        if (item instanceof ItemInfoWithIcon
+                && (((ItemInfoWithIcon) item).runtimeStatusFlags
+                & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
+            intent = new PackageManagerHelper(launcher)
+                    .getMarketIntent(appInfo.getTargetComponent().getPackageName());
         } else {
             intent = item.getIntent();
         }
@@ -278,10 +315,10 @@
                 intent.setPackage(null);
             }
         }
-        if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation()) {
+        if (v != null && launcher.supportsAdaptiveIconAnimation(v)) {
             // Preload the icon to reduce latency b/w swapping the floating view with the original.
             FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
         }
-        launcher.startActivitySafely(v, intent, item, sourceContainer);
+        launcher.startActivitySafely(v, intent, item);
     }
 }
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 7baeab8..f876dd9 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED;
 
 import android.view.View;
 import android.view.View.OnLongClickListener;
@@ -32,6 +33,7 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
@@ -55,7 +57,7 @@
         if (!(v.getTag() instanceof ItemInfo)) return false;
 
         launcher.setWaitingForResult(null);
-        beginDrag(v, launcher, (ItemInfo) v.getTag(), new DragOptions());
+        beginDrag(v, launcher, (ItemInfo) v.getTag(), launcher.getDefaultWorkspaceDragOptions());
         return true;
     }
 
@@ -86,6 +88,12 @@
         if (!launcher.isInState(ALL_APPS) && !launcher.isInState(OVERVIEW)) return false;
         if (launcher.getWorkspace().isSwitchingState()) return false;
 
+        StatsLogger logger = launcher.getStatsLogManager().logger();
+        if (v.getTag() instanceof ItemInfo) {
+            logger.withItemInfo((ItemInfo) v.getTag());
+        }
+        logger.log(LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED);
+
         // Start the drag
         final DragController dragController = launcher.getDragController();
         dragController.addDragListener(new DragController.DragListener() {
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index bfa24eb..d047eca 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -21,11 +21,15 @@
 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.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;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.ShapeDrawable;
 import android.util.FloatProperty;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -35,9 +39,13 @@
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.OverScroller;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.views.BaseDragLayer;
+
+import java.util.Collections;
+import java.util.List;
 
 public class LandscapePagedViewHandler implements PagedOrientationHandler {
 
@@ -52,31 +60,23 @@
     }
 
     @Override
-    public void delegateScrollTo(PagedView pagedView, int secondaryScroll, int minMaxScroll) {
-        pagedView.superScrollTo(secondaryScroll, minMaxScroll);
+    public int getPrimaryValue(int x, int y) {
+        return y;
     }
 
     @Override
-    public void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y) {
-        pagedView.scrollTo(pagedView.getScrollX() + x, unboundedScroll + y);
+    public int getSecondaryValue(int x, int y) {
+        return x;
     }
 
     @Override
-    public void scrollerStartScroll(OverScroller scroller, int newPosition) {
-        scroller.startScroll(scroller.getCurrPos(), newPosition - scroller.getCurrPos());
+    public float getPrimaryValue(float x, float y) {
+        return y;
     }
 
     @Override
-    public void getCurveProperties(PagedView view, Rect insets, CurveProperties out) {
-        out.scroll = view.getScrollY();
-        out.halfPageSize = view.getNormalChildHeight() / 2;
-        out.halfScreenSize = view.getMeasuredHeight() / 2;
-        out.screenCenter = insets.top + view.getPaddingTop() + out.scroll + out.halfPageSize;
-    }
-
-    @Override
-    public boolean isGoingUp(float displacement, boolean isRtl) {
-        return isRtl ? displacement < 0 : displacement > 0;
+    public float getSecondaryValue(float x, float y) {
+        return x;
     }
 
     @Override
@@ -92,11 +92,6 @@
     }
 
     @Override
-    public void delegateScrollTo(PagedView pagedView, int primaryScroll) {
-        pagedView.superScrollTo(pagedView.getScrollX(), primaryScroll);
-    }
-
-    @Override
     public <T> void set(T target, Int2DAction<T> action, int param) {
         action.call(target, 0, param);
     }
@@ -127,12 +122,27 @@
     }
 
     @Override
+    public int getPrimarySize(View view) {
+        return view.getHeight();
+    }
+
+    @Override
     public float getPrimarySize(RectF rect) {
         return rect.height();
     }
 
     @Override
-    public int getClearAllScrollOffset(View view, boolean isRtl) {
+    public float getStart(RectF rect) {
+        return rect.top;
+    }
+
+    @Override
+    public float getEnd(RectF rect) {
+        return rect.bottom;
+    }
+
+    @Override
+    public int getClearAllSidePadding(View view, boolean isRtl) {
         return (isRtl ? view.getPaddingBottom() : - view.getPaddingTop()) / 2;
     }
 
@@ -152,9 +162,16 @@
     }
 
     @Override
-    public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
-        view.setTranslationX(0);
-        view.setTranslationY(translation);
+    public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+            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) {
+            // Top (visually left) side
+            return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+        }
+        throw new IllegalStateException("Invalid split stage position: " +
+                splitPosition.mStagePosition);
     }
 
     @Override
@@ -214,11 +231,6 @@
     }
 
     @Override
-    public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() {
-        return HORIZONTAL;
-    }
-
-    @Override
     public int getPrimaryTranslationDirectionFactor() {
         return -1;
     }
@@ -228,18 +240,27 @@
     }
 
     @Override
-    public int getTaskDragDisplacementFactor(boolean isRtl) {
-        return isRtl ? 1 : -1;
+    public int getSplitTranslationDirectionFactor(int stagePosition) {
+        if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+            return -1;
+        } else {
+            return 1;
+        }
     }
 
     @Override
-    public float getTaskMenuX(float x, View thumbnailView) {
+    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
+        return translationOffset;
+    }
+
+    @Override
+    public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
         return thumbnailView.getMeasuredWidth() + x;
     }
 
     @Override
-    public float getTaskMenuY(float y, View thumbnailView) {
-        return y;
+    public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
+        return y + overScroll;
     }
 
     @Override
@@ -248,19 +269,63 @@
     }
 
     @Override
-    public int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate,
-        LinearLayout taskMenuLayout) {
-        return LinearLayout.HORIZONTAL;
+    public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
+            LinearLayout taskMenuLayout, int dividerSpacing,
+            ShapeDrawable dividerDrawable) {
+        taskMenuLayout.setOrientation(LinearLayout.HORIZONTAL);
+        dividerDrawable.setIntrinsicWidth(dividerSpacing);
+        taskMenuLayout.setDividerDrawable(dividerDrawable);
     }
 
     @Override
-    public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp) {
+    public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
+            LinearLayout viewGroup, DeviceProfile deviceProfile) {
+        // Phone fake landscape
+        viewGroup.setOrientation(LinearLayout.VERTICAL);
         lp.width = 0;
         lp.height = WRAP_CONTENT;
         lp.weight = 1;
+        Utilities.setStartMarginForView(viewGroup.findViewById(R.id.text), 0);
+        Utilities.setStartMarginForView(viewGroup.findViewById(R.id.icon), 0);
     }
 
     @Override
+    public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
+        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
+        lp.topMargin += margin;
+    }
+
+    @Override
+    public PointF getAdditionalInsetForTaskMenu(float margin) {
+        return new PointF(margin, 0);
+    }
+
+    /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+
+    @Override
+    public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
+        return HORIZONTAL;
+    }
+
+    @Override
+    public int getUpDirection(boolean isRtl) {
+        return isRtl ? SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+                : SingleAxisSwipeDetector.DIRECTION_POSITIVE;
+    }
+
+    @Override
+    public boolean isGoingUp(float displacement, boolean isRtl) {
+        return isRtl ? displacement < 0 : displacement > 0;
+    }
+
+    @Override
+    public int getTaskDragDisplacementFactor(boolean isRtl) {
+        return isRtl ? 1 : -1;
+    }
+
+    /* -------------------- */
+
+    @Override
     public ChildBounds getChildBounds(View child, int childStart, int pageCenter,
         boolean layoutChild) {
         final int childHeight = child.getMeasuredHeight();
@@ -278,4 +343,18 @@
     public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
         return rect.left;
     }
+
+    @Override
+    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,
+                STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+    }
+
+    @Override
+    public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
+            DeviceProfile deviceProfile) {
+        return primary;
+    }
 }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index e7d2ad5..266e05f 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -22,6 +22,7 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.ShapeDrawable;
 import android.util.FloatProperty;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -30,8 +31,10 @@
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.PagedView;
-import com.android.launcher3.util.OverScroller;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+
+import java.util.List;
 
 /**
  * Abstraction layer to separate horizontal and vertical specific implementations
@@ -40,6 +43,10 @@
  */
 public interface PagedOrientationHandler {
 
+    int SPLIT_TRANSLATE_PRIMARY_POSITIVE = 0;
+    int SPLIT_TRANSLATE_PRIMARY_NEGATIVE = 1;
+    int SPLIT_TRANSLATE_SECONDARY_NEGATIVE = 2;
+
     PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
     PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
     PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
@@ -61,12 +68,21 @@
     float getPrimaryDirection(MotionEvent event, int pointerIndex);
     float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
     int getMeasuredSize(View view);
+    int getPrimarySize(View view);
     float getPrimarySize(RectF rect);
-    int getClearAllScrollOffset(View view, boolean isRtl);
+    float getStart(RectF rect);
+    float getEnd(RectF rect);
+    int getClearAllSidePadding(View view, boolean isRtl);
     int getSecondaryDimension(View view);
     FloatProperty<View> getPrimaryViewTranslate();
     FloatProperty<View> getSecondaryViewTranslate();
-    void setPrimaryAndResetSecondaryTranslate(View view, float translation);
+
+    /**
+     * @param splitPosition 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 getPrimaryScroll(View view);
     float getPrimaryScale(View view);
     int getChildStart(View view);
@@ -74,31 +90,68 @@
     int getCenterForPage(View view, Rect insets);
     int getScrollOffsetStart(View view, Rect insets);
     int getScrollOffsetEnd(View view, Rect insets);
-    SingleAxisSwipeDetector.Direction getOppositeSwipeDirection();
     int getPrimaryTranslationDirectionFactor();
     int getSecondaryTranslationDirectionFactor();
-    int getTaskDragDisplacementFactor(boolean isRtl);
+    int getSplitTranslationDirectionFactor(@StagePosition int stagePosition);
+    int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp);
     ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild);
     void setMaxScroll(AccessibilityEvent event, int maxScroll);
     boolean getRecentsRtlSetting(Resources resources);
     float getDegreesRotated();
     int getRotation();
+
     <T> T getPrimaryValue(T x, T y);
     <T> T getSecondaryValue(T x, T y);
-    void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll);
-    /** Uses {@params pagedView}.getScroll[X|Y]() method for the secondary amount*/
-    void delegateScrollTo(PagedView pagedView, int primaryScroll);
-    void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y);
-    void scrollerStartScroll(OverScroller scroller, int newPosition);
-    void getCurveProperties(PagedView view, Rect insets, CurveProperties out);
-    boolean isGoingUp(float displacement, boolean isRtl);
+
+    int getPrimaryValue(int x, int y);
+    int getSecondaryValue(int x, int y);
+
+    float getPrimaryValue(float x, float y);
+    float getSecondaryValue(float x, float y);
+
     boolean isLayoutNaturalToLauncher();
-    float getTaskMenuX(float x, View thumbnailView);
-    float getTaskMenuY(float y, View thumbnailView);
-    int getTaskMenuWidth(View view);
-    int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate, LinearLayout taskMenuLayout);
-    void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp);
+    FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
+            DeviceProfile deviceProfile);
     int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
+    List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp);
+
+    // Overview TaskMenuView methods
+    float getTaskMenuX(float x, View thumbnailView, int overScroll);
+    float getTaskMenuY(float y, View thumbnailView, int overScroll);
+    int getTaskMenuWidth(View view);
+    /**
+     * Sets linear layout orientation for {@link com.android.launcher3.popup.SystemShortcut} items
+     * inside task menu view.
+     */
+    void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
+            LinearLayout taskMenuLayout, int dividerSpacing,
+            ShapeDrawable dividerDrawable);
+    /**
+     * Sets layout param attributes for {@link com.android.launcher3.popup.SystemShortcut} child
+     * views inside task menu view.
+     */
+    void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
+            LinearLayout viewGroup, DeviceProfile deviceProfile);
+    /**
+     * Adjusts margins for the entire task menu view itself, which comprises of both app title and
+     * shortcut options.
+     */
+    void setTaskMenuAroundTaskView(LinearLayout taskView, float margin);
+    /**
+     * Since the task menu layout is manually positioned on top of recents view, this method returns
+     * additional adjustments to the positioning based on fake land/seascape
+     */
+    PointF getAdditionalInsetForTaskMenu(float margin);
+
+    // The following are only used by TaskViewTouchHandler.
+    /** @return Either VERTICAL or HORIZONTAL. */
+    SingleAxisSwipeDetector.Direction getUpDownSwipeDirection();
+    /** @return Given {@link #getUpDownSwipeDirection()}, whether POSITIVE or NEGATIVE is up. */
+    int getUpDirection(boolean isRtl);
+    /** @return Whether the displacement is going towards the top of the screen. */
+    boolean isGoingUp(float displacement, boolean isRtl);
+    /** @return Either 1 or -1, a factor to multiply by so the animation goes the correct way. */
+    int getTaskDragDisplacementFactor(boolean isRtl);
 
     /**
      * Maps the velocity from the coordinate plane of the foreground app to that
@@ -106,13 +159,6 @@
      */
     void adjustFloatingIconStartVelocity(PointF velocity);
 
-    class CurveProperties {
-        public int scroll;
-        public int halfPageSize;
-        public int screenCenter;
-        public int halfScreenSize;
-    }
-
     class ChildBounds {
 
         public final int primaryDimension;
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index e5a8967..dd97af5 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -19,11 +19,15 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
 
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.ShapeDrawable;
 import android.util.FloatProperty;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -33,9 +37,13 @@
 import android.widget.LinearLayout;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.OverScroller;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.views.BaseDragLayer;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class PortraitPagedViewHandler implements PagedOrientationHandler {
 
@@ -50,32 +58,23 @@
     }
 
     @Override
-    public void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll) {
-        pagedView.superScrollTo(primaryScroll, secondaryScroll);
+    public int getPrimaryValue(int x, int y) {
+        return x;
     }
 
     @Override
-    public void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y) {
-        pagedView.scrollTo(unboundedScroll + x, pagedView.getScrollY() + y);
+    public int getSecondaryValue(int x, int y) {
+        return y;
     }
 
     @Override
-    public void scrollerStartScroll(OverScroller scroller, int newPosition) {
-        scroller.startScroll(newPosition - scroller.getCurrPos(), scroller.getCurrPos());
+    public float getPrimaryValue(float x, float y) {
+        return x;
     }
 
     @Override
-    public void getCurveProperties(PagedView view, Rect insets, CurveProperties out) {
-        out.scroll = view.getScrollX();
-        out.halfPageSize = view.getNormalChildWidth() / 2;
-        out.halfScreenSize = view.getMeasuredWidth() / 2;
-        out.screenCenter = insets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize;
-    }
-
-    @Override
-    public boolean isGoingUp(float displacement, boolean isRtl) {
-        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
-        return displacement < 0;
+    public float getSecondaryValue(float x, float y) {
+        return y;
     }
 
     @Override
@@ -89,11 +88,6 @@
     }
 
     @Override
-    public void delegateScrollTo(PagedView pagedView, int primaryScroll) {
-        pagedView.superScrollTo(primaryScroll, pagedView.getScrollY());
-    }
-
-    @Override
     public <T> void set(T target, Int2DAction<T> action, int param) {
         action.call(target, param, 0);
     }
@@ -124,12 +118,27 @@
     }
 
     @Override
+    public int getPrimarySize(View view) {
+        return view.getWidth();
+    }
+
+    @Override
     public float getPrimarySize(RectF rect) {
         return rect.width();
     }
 
     @Override
-    public int getClearAllScrollOffset(View view, boolean isRtl) {
+    public float getStart(RectF rect) {
+        return rect.left;
+    }
+
+    @Override
+    public float getEnd(RectF rect) {
+        return rect.right;
+    }
+
+    @Override
+    public int getClearAllSidePadding(View view, boolean isRtl) {
         return (isRtl ? view.getPaddingRight() : - view.getPaddingLeft()) / 2;
     }
 
@@ -149,9 +158,22 @@
     }
 
     @Override
-    public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
-        view.setTranslationX(translation);
-        view.setTranslationY(0);
+    public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+            DeviceProfile dp) {
+        if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+            if (dp.isLandscape) {
+                // Left side
+                return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+            } else {
+                // Top side
+                return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
+            }
+        } else if (splitPosition.mStagePosition == 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);
     }
 
     @Override
@@ -211,11 +233,6 @@
     }
 
     @Override
-    public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() {
-        return VERTICAL;
-    }
-
-    @Override
     public int getPrimaryTranslationDirectionFactor() {
         return 1;
     }
@@ -225,18 +242,29 @@
     }
 
     @Override
-    public int getTaskDragDisplacementFactor(boolean isRtl) {
-        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
-        return 1;
+    public int getSplitTranslationDirectionFactor(int stagePosition) {
+        if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+            return -1;
+        } else {
+            return 1;
+        }
     }
 
     @Override
-    public float getTaskMenuX(float x, View thumbnailView) {
-        return x;
+    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
+        if (dp.isLandscape) {
+            return translationOffset;
+        }
+        return 0;
     }
 
     @Override
-    public float getTaskMenuY(float y, View thumbnailView) {
+    public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
+        return x + overScroll;
+    }
+
+    @Override
+    public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
         return y;
     }
 
@@ -246,17 +274,80 @@
     }
 
     @Override
-    public int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate,
-        LinearLayout taskMenuLayout) {
-        return canRecentsActivityRotate ? taskMenuLayout.getOrientation() : LinearLayout.VERTICAL;
+    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.setDividerDrawable(dividerDrawable);
     }
 
     @Override
-    public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp) {
-        // no-op, defaults are fine
+    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;
     }
 
     @Override
+    public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
+        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
+        lp.topMargin += margin;
+        lp.leftMargin += margin;
+    }
+
+    @Override
+    public PointF getAdditionalInsetForTaskMenu(float margin) {
+        return new PointF(0, 0);
+    }
+
+    /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+
+    @Override
+    public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
+        return VERTICAL;
+    }
+
+    @Override
+    public int getUpDirection(boolean isRtl) {
+        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+        return SingleAxisSwipeDetector.DIRECTION_POSITIVE;
+    }
+
+    @Override
+    public boolean isGoingUp(float displacement, boolean isRtl) {
+        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+        return displacement < 0;
+    }
+
+    @Override
+    public int getTaskDragDisplacementFactor(boolean isRtl) {
+        // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+        return 1;
+    }
+
+    /* -------------------- */
+
+    @Override
     public ChildBounds getChildBounds(View child, int childStart, int pageCenter,
         boolean layoutChild) {
         final int childWidth = child.getMeasuredWidth();
@@ -273,4 +364,46 @@
     public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
         return dp.heightPx - rect.bottom;
     }
+
+    @Override
+    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,
+                    STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
+            options.add(new SplitPositionOption(
+                    R.drawable.ic_split_screen, 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,
+                        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,
+                        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,
+                        STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+            }
+        }
+        return options;
+    }
+
+    @Override
+    public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
+            DeviceProfile dp) {
+        if (dp.isLandscape) { // or seascape
+            return primary;
+        } else {
+            return secondary;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 7153452..91d44bd 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -16,14 +16,26 @@
 
 package com.android.launcher3.touch;
 
+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;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
+
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.view.Surface;
 import android.view.View;
+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.views.BaseDragLayer;
+
+import java.util.Collections;
+import java.util.List;
 
 public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
 
@@ -33,8 +45,17 @@
     }
 
     @Override
-    public int getTaskDragDisplacementFactor(boolean isRtl) {
-        return isRtl ? -1 : 1;
+    public int getSplitTranslationDirectionFactor(int stagePosition) {
+        if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+
+    @Override
+    public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) {
+        return translationOffset;
     }
 
     @Override
@@ -53,11 +74,6 @@
     }
 
     @Override
-    public boolean isGoingUp(float displacement, boolean isRtl) {
-        return isRtl ? displacement > 0 : displacement < 0;
-    }
-
-    @Override
     public void adjustFloatingIconStartVelocity(PointF velocity) {
         float oldX = velocity.x;
         float oldY = velocity.y;
@@ -65,23 +81,61 @@
     }
 
     @Override
-    public float getTaskMenuX(float x, View thumbnailView) {
+    public float getTaskMenuX(float x, View thumbnailView, int overScroll) {
         return x;
     }
 
     @Override
-    public float getTaskMenuY(float y, View thumbnailView) {
-        return y + thumbnailView.getMeasuredHeight();
+    public float getTaskMenuY(float y, View thumbnailView, int overScroll) {
+        return y + thumbnailView.getMeasuredHeight() + overScroll;
     }
 
     @Override
-    public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
-        view.setTranslationX(0);
-        view.setTranslationY(translation);
+    public void setTaskMenuAroundTaskView(LinearLayout taskView, float margin) {
+        BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskView.getLayoutParams();
+        lp.bottomMargin += margin;
+    }
+
+    @Override
+    public PointF getAdditionalInsetForTaskMenu(float margin) {
+        return new PointF(-margin, margin);
     }
 
     @Override
     public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
         return dp.widthPx - rect.right;
     }
+
+    @Override
+    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,
+                STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
+    }
+
+    /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+
+    @Override
+    public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
+        return HORIZONTAL;
+    }
+
+    @Override
+    public int getUpDirection(boolean isRtl) {
+        return isRtl ? SingleAxisSwipeDetector.DIRECTION_POSITIVE
+                : SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+    }
+
+    @Override
+    public boolean isGoingUp(float displacement, boolean isRtl) {
+        return isRtl ? displacement > 0 : displacement < 0;
+    }
+
+    @Override
+    public int getTaskDragDisplacementFactor(boolean isRtl) {
+        return isRtl ? -1 : 1;
+    }
+
+    /* -------------------- */
 }
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
index 875eefb..f751b7d 100644
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -17,7 +17,6 @@
 
 import android.content.Context;
 import android.graphics.PointF;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
@@ -25,7 +24,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
 
 /**
  * One dimensional scroll/drag/swipe gesture detector (either HORIZONTAL or VERTICAL).
@@ -60,6 +58,11 @@
             return direction.x;
         }
 
+        @NonNull
+        @Override
+        public String toString() {
+            return "VERTICAL";
+        }
     };
 
     public static final Direction HORIZONTAL = new Direction() {
@@ -86,6 +89,11 @@
             return direction.y;
         }
 
+        @NonNull
+        @Override
+        public String toString() {
+            return "HORIZONTAL";
+        }
     };
 
     private final Direction mDir;
@@ -94,6 +102,8 @@
 
     private int mScrollDirections;
 
+    private float mTouchSlopMultiplier = 1f;
+
     public SingleAxisSwipeDetector(@NonNull Context context, @NonNull Listener l,
             @NonNull Direction dir) {
         this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
@@ -105,11 +115,19 @@
         super(config, isRtl);
         mListener = l;
         mDir = dir;
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector.ctor "
-                    + l.getClass().getSimpleName()
-                    + " @ " + android.util.Log.getStackTraceString(new Throwable()));
-        }
+    }
+
+    /**
+     * Provides feasibility to adjust touch slop when visible window size changed. When visible
+     * bounds translate become smaller, multiply a larger multiplier could ensure the UX
+     * more consistent.
+     *
+     * @see #shouldScrollStart(PointF)
+     *
+     * @param touchSlopMultiplier the value to multiply original touch slop.
+     */
+    public void setTouchSlopMultiplier(float touchSlopMultiplier) {
+        mTouchSlopMultiplier = touchSlopMultiplier;
     }
 
     public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
@@ -117,10 +135,6 @@
         mIgnoreSlopWhenSettling = ignoreSlop;
     }
 
-    public int getScrollDirections() {
-        return mScrollDirections;
-    }
-
     /**
      * Returns if the start drag was towards the positive direction or negative.
      *
@@ -134,7 +148,7 @@
     @Override
     protected boolean shouldScrollStart(PointF displacement) {
         // Reject cases where the angle or slop condition is not met.
-        float minDisplacement = Math.max(mTouchSlop,
+        float minDisplacement = Math.max(mTouchSlop * mTouchSlopMultiplier,
                 Math.abs(mDir.extractOrthogonalDirection(displacement)));
         if (Math.abs(mDir.extractDirection(displacement)) < minDisplacement) {
             return false;
@@ -161,10 +175,6 @@
 
     @Override
     protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector "
-                    + mListener.getClass().getSimpleName());
-        }
         mListener.onDrag(mDir.extractDirection(displacement),
                 mDir.extractOrthogonalDirection(displacement), event);
     }
diff --git a/src/com/android/launcher3/util/ActivityOptionsWrapper.java b/src/com/android/launcher3/util/ActivityOptionsWrapper.java
new file mode 100644
index 0000000..99cc1f7
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityOptionsWrapper.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.util;
+
+
+import android.app.ActivityOptions;
+import android.os.Bundle;
+
+/**
+ * A wrapper around {@link ActivityOptions} to allow custom functionality in launcher
+ */
+public class ActivityOptionsWrapper {
+
+    public final ActivityOptions options;
+    public final RunnableList onEndCallback;
+
+    public ActivityOptionsWrapper(ActivityOptions options, RunnableList onEndCallback) {
+        this.options = options;
+        this.onEndCallback = onEndCallback;
+    }
+
+    /**
+     * @see {@link ActivityOptions#toBundle()}
+     */
+    public Bundle toBundle() {
+        return options.toBundle();
+    }
+}
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index 59266b4..7af1a13 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -15,15 +15,14 @@
  */
 package com.android.launcher3.util;
 
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * Helper class to statically track activity creation
@@ -32,8 +31,7 @@
 public final class ActivityTracker<T extends BaseActivity> {
 
     private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
-
-    private static final String EXTRA_SCHEDULER_CALLBACK = "launcher.scheduler_callback";
+    private CopyOnWriteArrayList<SchedulerCallback<T>> mCallbacks = new CopyOnWriteArrayList<>();
 
     @Nullable
     public <R extends T> R getCreatedActivity() {
@@ -47,44 +45,50 @@
     }
 
     /**
-     * Call {@link SchedulerCallback#init(BaseActivity, boolean)} when the activity is ready.
-     * If the activity is already created, this is called immediately, otherwise we add the
-     * callback as an extra on the intent, and will call init() when we get handleIntent().
+     * Call {@link SchedulerCallback#init(BaseActivity, boolean)} when the
+     * activity is ready. If the activity is already created, this is called immediately.
+     *
+     * The tracker maintains a strong ref to the callback, so it is up to the caller to return
+     * {@code false} in the callback OR to unregister the callback explicitly.
+     *
      * @param callback The callback to call init() on when the activity is ready.
-     * @param intent The intent that will be used to initialize the activity, if the activity
-     *               doesn't already exist. We add the callback as an extra on this intent.
      */
-    public void runCallbackWhenActivityExists(SchedulerCallback<T> callback, Intent intent) {
+    public void registerCallback(SchedulerCallback<T> callback) {
         T activity = mCurrentActivity.get();
+        mCallbacks.add(callback);
         if (activity != null) {
-            callback.init(activity, activity.isStarted());
-        } else {
-            callback.addToIntent(intent);
+            if (!callback.init(activity, activity.isStarted())) {
+                unregisterCallback(callback);
+            }
         }
     }
 
+    /**
+     * Unregisters a registered callback.
+     */
+    public void unregisterCallback(SchedulerCallback<T> callback) {
+        mCallbacks.remove(callback);
+    }
+
     public boolean handleCreate(T activity) {
         mCurrentActivity = new WeakReference<>(activity);
-        return handleIntent(activity, activity.getIntent(), false);
+        return handleIntent(activity, false /* alreadyOnHome */);
     }
 
-    public boolean handleNewIntent(T activity, Intent intent) {
-        return handleIntent(activity, intent, activity.isStarted());
+    public boolean handleNewIntent(T activity) {
+        return handleIntent(activity, activity.isStarted());
     }
 
-    private boolean handleIntent(T activity, Intent intent, boolean alreadyOnHome) {
-        if (intent != null && intent.getExtras() != null) {
-            IBinder stateBinder = intent.getExtras().getBinder(EXTRA_SCHEDULER_CALLBACK);
-            if (stateBinder instanceof ObjectWrapper) {
-                SchedulerCallback<T> handler =
-                        ((ObjectWrapper<SchedulerCallback>) stateBinder).get();
-                if (!handler.init(activity, alreadyOnHome)) {
-                    intent.getExtras().remove(EXTRA_SCHEDULER_CALLBACK);
-                }
-                return true;
+    private boolean handleIntent(T activity, boolean alreadyOnHome) {
+        boolean handled = false;
+        for (SchedulerCallback<T> cb : mCallbacks) {
+            if (!cb.init(activity, alreadyOnHome)) {
+                // Callback doesn't want any more updates
+                unregisterCallback(cb);
             }
+            handled = true;
         }
-        return false;
+        return handled;
     }
 
     public interface SchedulerCallback<T extends BaseActivity> {
@@ -95,17 +99,5 @@
          * @return Whether to continue receiving callbacks (i.e. if the activity is recreated).
          */
         boolean init(T activity, boolean alreadyOnHome);
-
-        /**
-         * Adds this callback as an extra on the intent, so we can retrieve it in handleIntent() and
-         * call {@link #init}. The intent should be used to start the activity after calling this
-         * method in order for us to get handleIntent().
-         */
-        default Intent addToIntent(Intent intent) {
-            Bundle extras = new Bundle();
-            extras.putBinder(EXTRA_SCHEDULER_CALLBACK, ObjectWrapper.wrap(this));
-            intent.putExtras(extras);
-            return intent;
-        }
     }
 }
diff --git a/src/com/android/launcher3/util/BgObjectWithLooper.java b/src/com/android/launcher3/util/BgObjectWithLooper.java
new file mode 100644
index 0000000..1483c43
--- /dev/null
+++ b/src/com/android/launcher3/util/BgObjectWithLooper.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.os.Looper;
+
+import androidx.annotation.WorkerThread;
+
+/**
+ * Utility class to define an object which does most of it's processing on a
+ * dedicated background thread.
+ */
+public abstract class BgObjectWithLooper {
+
+    /**
+     * Start initialization of the object
+     */
+    public final void initializeInBackground(String threadName) {
+        new Thread(this::runOnThread, threadName).start();
+    }
+
+    private void runOnThread() {
+        Looper.prepare();
+        onInitialized(Looper.myLooper());
+        Looper.loop();
+    }
+
+    /**
+     * Called on the background thread to handle initialization
+     */
+    @WorkerThread
+    protected abstract void onInitialized(Looper looper);
+}
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
deleted file mode 100644
index 0f81520..0000000
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package com.android.launcher3.util;
-
-/**
- * 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.
- */
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.util.Log;
-
-import java.util.function.Consumer;
-
-/**
- * {@link BroadcastReceiver} which watches configuration changes and
- * notifies the callback in case changes which affect the device profile occur.
- */
-public class ConfigMonitor extends BroadcastReceiver implements
-        DefaultDisplay.DisplayInfoChangeListener {
-
-    private static final String TAG = "ConfigMonitor";
-
-    private final Point mTmpPoint1 = new Point();
-    private final Point mTmpPoint2 = new Point();
-
-    private final Context mContext;
-    private final float mFontScale;
-    private final int mDensity;
-
-    private final int mDisplayId;
-    private final Point mRealSize;
-    private final Point mSmallestSize, mLargestSize;
-
-    private Consumer<Context> mCallback;
-
-    public ConfigMonitor(Context context, Consumer<Context> callback) {
-        mContext = context;
-
-        Configuration config = context.getResources().getConfiguration();
-        mFontScale = config.fontScale;
-        mDensity = config.densityDpi;
-
-        DefaultDisplay display = DefaultDisplay.INSTANCE.get(context);
-        display.addChangeListener(this);
-        DefaultDisplay.Info displayInfo = display.getInfo();
-        mDisplayId = displayInfo.id;
-
-        mRealSize = new Point(displayInfo.realSize);
-        mSmallestSize = new Point(displayInfo.smallestSize);
-        mLargestSize = new Point(displayInfo.largestSize);
-
-        mCallback = callback;
-
-        // Listen for configuration change
-        mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        Configuration config = context.getResources().getConfiguration();
-        if (mFontScale != config.fontScale || mDensity != config.densityDpi) {
-            Log.d(TAG, "Configuration changed.");
-            notifyChange();
-        }
-    }
-
-    @Override
-    public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
-        if (info.id != mDisplayId) {
-            return;
-        }
-        mTmpPoint1.set(info.realSize.x, info.realSize.y);
-        if (!mRealSize.equals(mTmpPoint1) && !mRealSize.equals(mTmpPoint1.y, mTmpPoint1.x)) {
-            Log.d(TAG, String.format("Display size changed from %s to %s", mRealSize, mTmpPoint1));
-            notifyChange();
-            return;
-        }
-
-        mTmpPoint1.set(info.smallestSize.x, info.smallestSize.y);
-        mTmpPoint2.set(info.largestSize.x, info.largestSize.y);
-        if (!mSmallestSize.equals(mTmpPoint1) || !mLargestSize.equals(mTmpPoint2)) {
-            Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
-                    mSmallestSize, mLargestSize, mTmpPoint1, mTmpPoint2));
-            notifyChange();
-        }
-    }
-
-    private synchronized void notifyChange() {
-        if (mCallback != null) {
-            Consumer<Context> callback = mCallback;
-            mCallback = null;
-            MAIN_EXECUTOR.execute(() -> callback.accept(mContext));
-        }
-    }
-
-    public void unregister() {
-        try {
-            mContext.unregisterReceiver(this);
-            DefaultDisplay display = DefaultDisplay.INSTANCE.get(mContext);
-            display.removeChangeListener(this);
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to unregister config monitor", e);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 30c9ff9..ee64e98 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -113,14 +113,26 @@
 
     public static final class CommitParams {
 
-        final Uri mUri = LauncherSettings.Favorites.CONTENT_URI;
-        String mWhere;
-        String[] mSelectionArgs;
+        final Uri mUri;
+        final String mWhere;
+        final String[] mSelectionArgs;
 
         public CommitParams(String where, String[] selectionArgs) {
+            this(LauncherSettings.Favorites.CONTENT_URI, where, selectionArgs);
+        }
+
+        private CommitParams(Uri uri, String where, String[] selectionArgs) {
+            mUri = uri;
             mWhere = where;
             mSelectionArgs = selectionArgs;
         }
 
+        /**
+         * Creates commit params for backup table.
+         */
+        public static CommitParams backupCommitParams(String where, String[] selectionArgs) {
+            return new CommitParams(
+                    LauncherSettings.Favorites.BACKUP_CONTENT_URI, where, selectionArgs);
+        }
     }
 }
diff --git a/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java
deleted file mode 100644
index 35788a5..0000000
--- a/src/com/android/launcher3/util/DefaultDisplay.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.Handler;
-import android.os.Message;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Display;
-
-import androidx.annotation.VisibleForTesting;
-
-import java.util.ArrayList;
-
-/**
- * Utility class to cache properties of default display to avoid a system RPC on every call.
- */
-public class DefaultDisplay implements DisplayListener {
-
-    public static final MainThreadInitializedObject<DefaultDisplay> INSTANCE =
-            new MainThreadInitializedObject<>(DefaultDisplay::new);
-
-    private static final String TAG = "DefaultDisplay";
-
-    public static final int CHANGE_SIZE = 1 << 0;
-    public static final int CHANGE_ROTATION = 1 << 1;
-    public static final int CHANGE_FRAME_DELAY = 1 << 2;
-
-    public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION | CHANGE_FRAME_DELAY;
-
-    private final Context mDisplayContext;
-    private final int mId;
-    private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
-    private final Handler mChangeHandler;
-    private Info mInfo;
-
-    private DefaultDisplay(Context context) {
-        DisplayManager dm = context.getSystemService(DisplayManager.class);
-        // Use application context to create display context so that it can have its own Resources.
-        mDisplayContext = context.getApplicationContext().createDisplayContext(
-                dm.getDisplay(DEFAULT_DISPLAY));
-        // Note that the Display object must be obtained from DisplayManager which is associated to
-        // the display context, so the Display is isolated from Activity and Application to provide
-        // the actual state of device that excludes the additional adjustment and override.
-        mInfo = new Info(mDisplayContext);
-        mId = mInfo.id;
-        mChangeHandler = new Handler(this::onChange);
-
-        dm.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
-    }
-
-    @Override
-    public final void onDisplayAdded(int displayId) {  }
-
-    @Override
-    public final void onDisplayRemoved(int displayId) { }
-
-    @Override
-    public final void onDisplayChanged(int displayId) {
-        if (displayId != mId) {
-            return;
-        }
-
-        Info oldInfo = mInfo;
-        Info info = new Info(mDisplayContext);
-
-        int change = 0;
-        if (info.hasDifferentSize(oldInfo)) {
-            change |= CHANGE_SIZE;
-        }
-        if (oldInfo.rotation != info.rotation) {
-            change |= CHANGE_ROTATION;
-        }
-        if (info.singleFrameMs != oldInfo.singleFrameMs) {
-            change |= CHANGE_FRAME_DELAY;
-        }
-
-        if (change != 0) {
-            mInfo = info;
-            mChangeHandler.sendEmptyMessage(change);
-        }
-    }
-
-    public static int getSingleFrameMs(Context context) {
-        return INSTANCE.get(context).getInfo().singleFrameMs;
-    }
-
-    public Info getInfo() {
-        return mInfo;
-    }
-
-    public void addChangeListener(DisplayInfoChangeListener listener) {
-        mListeners.add(listener);
-    }
-
-    public void removeChangeListener(DisplayInfoChangeListener listener) {
-        mListeners.remove(listener);
-    }
-
-    private boolean onChange(Message msg) {
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            mListeners.get(i).onDisplayInfoChanged(mInfo, msg.what);
-        }
-        return true;
-    }
-
-    public static class Info {
-
-        public final int id;
-        public final int rotation;
-        public final int singleFrameMs;
-
-        public final Point realSize;
-        public final Point smallestSize;
-        public final Point largestSize;
-
-        public final DisplayMetrics metrics;
-
-        @VisibleForTesting
-        public Info(int id, int rotation, int singleFrameMs, Point realSize, Point smallestSize,
-                Point largestSize, DisplayMetrics metrics) {
-            this.id = id;
-            this.rotation = rotation;
-            this.singleFrameMs = singleFrameMs;
-            this.realSize = realSize;
-            this.smallestSize = smallestSize;
-            this.largestSize = largestSize;
-            this.metrics = metrics;
-        }
-
-        private Info(Context context) {
-            this(context, context.getSystemService(DisplayManager.class)
-                    .getDisplay(DEFAULT_DISPLAY));
-        }
-
-        public Info(Context context, Display display) {
-            id = display.getDisplayId();
-            rotation = display.getRotation();
-
-            float refreshRate = display.getRefreshRate();
-            singleFrameMs = refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
-
-            realSize = new Point();
-            smallestSize = new Point();
-            largestSize = new Point();
-            display.getRealSize(realSize);
-            display.getCurrentSizeRange(smallestSize, largestSize);
-
-            metrics = context.getResources().getDisplayMetrics();
-        }
-
-        private boolean hasDifferentSize(Info info) {
-            if (!realSize.equals(info.realSize)
-                    && !realSize.equals(info.realSize.y, info.realSize.x)) {
-                Log.d(TAG, String.format("Display size changed from %s to %s",
-                        info.realSize, realSize));
-                return true;
-            }
-
-            if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) {
-                Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
-                        smallestSize, largestSize, info.smallestSize, info.largestSize));
-                return true;
-            }
-
-            return false;
-        }
-    }
-
-    /**
-     * Interface for listening for display changes
-     */
-    public interface DisplayInfoChangeListener {
-
-        void onDisplayInfoChanged(Info info, int flags);
-    }
-}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
new file mode 100644
index 0000000..e2c0a32
--- /dev/null
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static com.android.launcher3.Utilities.dpiFromPx;
+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.WindowManagerCompat.MIN_TABLET_WIDTH;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Build;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowMetrics;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.uioverrides.ApiWrapper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Utility class to cache properties of default display to avoid a system RPC on every call.
+ */
+@SuppressLint("NewApi")
+public class DisplayController implements DisplayListener, ComponentCallbacks {
+
+    private static final String TAG = "DisplayController";
+
+    public static final MainThreadInitializedObject<DisplayController> INSTANCE =
+            new MainThreadInitializedObject<>(DisplayController::new);
+
+    public static final int CHANGE_ACTIVE_SCREEN = 1 << 0;
+    public static final int CHANGE_ROTATION = 1 << 1;
+    public static final int CHANGE_FRAME_DELAY = 1 << 2;
+    public static final int CHANGE_DENSITY = 1 << 3;
+    public static final int CHANGE_SUPPORTED_BOUNDS = 1 << 4;
+
+    public static final int CHANGE_ALL = CHANGE_ACTIVE_SCREEN | CHANGE_ROTATION
+            | CHANGE_FRAME_DELAY | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
+
+    private final Context mContext;
+    private final DisplayManager mDM;
+
+    // Null for SDK < S
+    private final Context mWindowContext;
+
+    private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
+    private Info mInfo;
+
+    private DisplayController(Context context) {
+        mContext = context;
+        mDM = context.getSystemService(DisplayManager.class);
+
+        Display display = mDM.getDisplay(DEFAULT_DISPLAY);
+        if (Utilities.ATLEAST_S) {
+            mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
+            mWindowContext.registerComponentCallbacks(this);
+        } else {
+            mWindowContext = null;
+            SimpleBroadcastReceiver configChangeReceiver =
+                    new SimpleBroadcastReceiver(this::onConfigChanged);
+            mContext.registerReceiver(configChangeReceiver,
+                    new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+        }
+
+        // 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) {
+                Point size = new Point();
+                d.getRealSize(size);
+                extraInternalDisplays.add(new PortraitSize(size.x, size.y));
+            }
+        }
+        mInfo = new Info(getDisplayInfoContext(display), display, extraInternalDisplays);
+        mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+    }
+
+    @Override
+    public final void onDisplayAdded(int displayId) { }
+
+    @Override
+    public final void onDisplayRemoved(int displayId) { }
+
+    @WorkerThread
+    @Override
+    public final void onDisplayChanged(int displayId) {
+        if (displayId != DEFAULT_DISPLAY) {
+            return;
+        }
+        Display display = mDM.getDisplay(DEFAULT_DISPLAY);
+        if (display == null) {
+            return;
+        }
+        if (Utilities.ATLEAST_S) {
+            // Only check for refresh rate. Everything else comes from component callbacks
+            if (getSingleFrameMs(display) == mInfo.singleFrameMs) {
+                return;
+            }
+        }
+        handleInfoChange(display);
+    }
+
+    public static int getSingleFrameMs(Context context) {
+        return INSTANCE.get(context).getInfo().singleFrameMs;
+    }
+
+    /**
+     * Interface for listening for display changes
+     */
+    public interface DisplayInfoChangeListener {
+
+        /**
+         * Invoked when display info has changed.
+         * @param context updated context associated with the display.
+         * @param info updated display information.
+         * @param flags bitmask indicating type of change.
+         */
+        void onDisplayInfoChanged(Context context, Info info, int flags);
+    }
+
+    /**
+     * Only used for pre-S
+     */
+    private void onConfigChanged(Intent intent) {
+        Configuration config = mContext.getResources().getConfiguration();
+        if (mInfo.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) {
+            Log.d(TAG, "Configuration changed, notifying listeners");
+            Display display = mDM.getDisplay(DEFAULT_DISPLAY);
+            if (display != null) {
+                handleInfoChange(display);
+            }
+        }
+    }
+
+    @UiThread
+    @Override
+    @TargetApi(Build.VERSION_CODES.S)
+    public final void onConfigurationChanged(Configuration config) {
+        Display display = mWindowContext.getDisplay();
+        if (config.densityDpi != mInfo.densityDpi
+                || config.fontScale != mInfo.fontScale
+                || display.getRotation() != mInfo.rotation
+                || !mInfo.mScreenSizeDp.equals(
+                        new PortraitSize(config.screenHeightDp, config.screenWidthDp))) {
+            handleInfoChange(display);
+        }
+    }
+
+    @Override
+    public final void onLowMemory() { }
+
+    public void addChangeListener(DisplayInfoChangeListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void removeChangeListener(DisplayInfoChangeListener listener) {
+        mListeners.remove(listener);
+    }
+
+    public Info getInfo() {
+        return mInfo;
+    }
+
+    private Context getDisplayInfoContext(Display display) {
+        return Utilities.ATLEAST_S ? mWindowContext : mContext.createDisplayContext(display);
+    }
+
+    @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);
+        int change = 0;
+        if (!newInfo.mScreenSizeDp.equals(oldInfo.mScreenSizeDp)) {
+            change |= CHANGE_ACTIVE_SCREEN;
+        }
+        if (newInfo.rotation != oldInfo.rotation) {
+            change |= CHANGE_ROTATION;
+        }
+        if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
+            change |= CHANGE_FRAME_DELAY;
+        }
+        if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
+            change |= CHANGE_DENSITY;
+        }
+        if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
+            change |= CHANGE_SUPPORTED_BOUNDS;
+        }
+
+        if (change != 0) {
+            mInfo = newInfo;
+            final int flags = change;
+            MAIN_EXECUTOR.execute(() -> notifyChange(displayContext, flags));
+        }
+    }
+
+    private void notifyChange(Context context, int flags) {
+        for (int i = mListeners.size() - 1; i >= 0; i--) {
+            mListeners.get(i).onDisplayInfoChanged(context, mInfo, flags);
+        }
+    }
+
+    public static class Info {
+
+        public final int id;
+        public final int singleFrameMs;
+
+        // Configuration properties
+        public final int rotation;
+        public final float fontScale;
+        public final int densityDpi;
+
+        private final PortraitSize mScreenSizeDp;
+        private final Set<PortraitSize> mAllSizes;
+
+        public final Point currentSize;
+
+        public final Set<WindowBounds> supportedBounds = new ArraySet<>();
+
+        public Info(Context context, Display display) {
+            this(context, display, Collections.emptySet());
+        }
+
+        private Info(Context context, Display display, Set<PortraitSize> extraDisplaysSizes) {
+            id = display.getDisplayId();
+
+            rotation = display.getRotation();
+
+            Configuration config = context.getResources().getConfiguration();
+            fontScale = config.fontScale;
+            densityDpi = config.densityDpi;
+            mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
+
+            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);
+
+                int portraitWidth = Math.min(currentSize.x, currentSize.y);
+                int portraitHeight = Math.max(currentSize.x, currentSize.y);
+
+                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)));
+            }
+        }
+
+        /**
+         * Returns true if the bounds represent a tablet
+         */
+        public boolean isTablet(WindowBounds bounds) {
+            return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()),
+                    densityDpi) >= MIN_TABLET_WIDTH;
+        }
+    }
+
+    /**
+     * Utility class to hold a size information in an orientation independent way
+     */
+    public static class PortraitSize {
+        public final int width, height;
+
+        public PortraitSize(int w, int h) {
+            width = Math.min(w, h);
+            height = Math.max(w, h);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            PortraitSize that = (PortraitSize) o;
+            return width == that.width && height == that.height;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(width, height);
+        }
+    }
+
+    private static int getSingleFrameMs(Display display) {
+        float refreshRate = display.getRefreshRate();
+        return refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
+    }
+}
diff --git a/src/com/android/launcher3/util/EdgeEffectCompat.java b/src/com/android/launcher3/util/EdgeEffectCompat.java
new file mode 100644
index 0000000..491582b
--- /dev/null
+++ b/src/com/android/launcher3/util/EdgeEffectCompat.java
@@ -0,0 +1,46 @@
+/*
+ * 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.widget.EdgeEffect;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Extension of {@link EdgeEffect} to allow backwards compatibility
+ */
+public class EdgeEffectCompat extends EdgeEffect {
+
+    public EdgeEffectCompat(Context context) {
+        super(context);
+    }
+
+    @Override
+    public float getDistance() {
+        return Utilities.ATLEAST_S ? super.getDistance() : 0;
+    }
+
+    @Override
+    public float onPullDistance(float deltaDistance, float displacement) {
+        if (Utilities.ATLEAST_S) {
+            return super.onPullDistance(deltaDistance, displacement);
+        } else {
+            onPull(deltaDistance, displacement);
+            return deltaDistance;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index 0a32734..6329540 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -20,26 +20,25 @@
 import android.os.Process;
 
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Various different executors used in Launcher
  */
 public class Executors {
 
-    // These values are same as that in {@link AsyncTask}.
-    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
-    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
-    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+    private static final int POOL_SIZE =
+            Math.max(Runtime.getRuntime().availableProcessors(), 2);
     private static final int KEEP_ALIVE = 1;
 
     /**
      * An {@link ThreadPoolExecutor} to be used with async task with no limit on the queue size.
      */
     public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
-            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
-            TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+            POOL_SIZE, POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
 
     /**
      * Returns the executor for running tasks on the main thread.
@@ -51,7 +50,8 @@
      * A background executor for using time sensitive actions where user is waiting for response.
      */
     public static final LooperExecutor UI_HELPER_EXECUTOR =
-            new LooperExecutor(createAndStartNewForegroundLooper("UiThreadHelper"));
+            new LooperExecutor(
+                    createAndStartNewLooper("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND));
 
     /**
      * Utility method to get a started handler thread statically
@@ -70,17 +70,33 @@
     }
 
     /**
-     * Similar to {@link #createAndStartNewLooper(String)}, but starts the thread with
-     * foreground priority.
-     * Think before using
-     */
-    public static Looper createAndStartNewForegroundLooper(String name) {
-        return createAndStartNewLooper(name, Process.THREAD_PRIORITY_FOREGROUND);
-    }
-
-    /**
      * Executor used for running Launcher model related tasks (eg loading icons or updated db)
      */
     public static final LooperExecutor MODEL_EXECUTOR =
             new LooperExecutor(createAndStartNewLooper("launcher-loader"));
+
+    /**
+     * A simple ThreadFactory to set the thread name and priority when used with executors.
+     */
+    public static class SimpleThreadFactory implements ThreadFactory {
+
+        private final int mPriority;
+        private final String mNamePrefix;
+
+        private final AtomicInteger mCount = new AtomicInteger(0);
+
+        public SimpleThreadFactory(String namePrefix, int priority) {
+            mNamePrefix = namePrefix;
+            mPriority = priority;
+        }
+
+        @Override
+        public Thread newThread(Runnable runnable) {
+            Thread t = new Thread(() -> {
+                Process.setThreadPriority(mPriority);
+                runnable.run();
+            }, mNamePrefix + mCount.incrementAndGet());
+            return t;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index 9d0ad22..c9aa51c 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -70,9 +70,6 @@
 
         mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY);
 
-        // Don't highlight the icon as it's animating
-        mDragObject.dragView.setColor(0);
-
         final int duration = mDuration + DRAG_END_DELAY;
         final long startTime = AnimationUtils.currentAnimationTimeMillis();
 
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
deleted file mode 100644
index 4f4cccd..0000000
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ /dev/null
@@ -1,553 +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.
- */
-
-package com.android.launcher3.util;
-
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.Arrays;
-
-/**
- * Calculates the next item that a {@link KeyEvent} should change the focus to.
- *<p>
- * Note, this utility class calculates everything regards to icon index and its (x,y) coordinates.
- * Currently supports:
- * <ul>
- *  <li> full matrix of cells that are 1x1
- *  <li> sparse matrix of cells that are 1x1
- *     [ 1][  ][ 2][  ]
- *     [  ][  ][ 3][  ]
- *     [  ][ 4][  ][  ]
- *     [  ][ 5][ 6][ 7]
- * </ul>
- * *<p>
- * For testing, one can use a BT keyboard, or use following adb command.
- * ex. $ adb shell input keyevent 20 // KEYCODE_DPAD_LEFT
- */
-public class FocusLogic {
-
-    private static final String TAG = "FocusLogic";
-    private static final boolean DEBUG = false;
-
-    /** Item and page index related constant used by {@link #handleKeyEvent}. */
-    public static final int NOOP = -1;
-
-    public static final int PREVIOUS_PAGE_RIGHT_COLUMN  = -2;
-    public static final int PREVIOUS_PAGE_FIRST_ITEM    = -3;
-    public static final int PREVIOUS_PAGE_LAST_ITEM     = -4;
-    public static final int PREVIOUS_PAGE_LEFT_COLUMN   = -5;
-
-    public static final int CURRENT_PAGE_FIRST_ITEM     = -6;
-    public static final int CURRENT_PAGE_LAST_ITEM      = -7;
-
-    public static final int NEXT_PAGE_FIRST_ITEM        = -8;
-    public static final int NEXT_PAGE_LEFT_COLUMN       = -9;
-    public static final int NEXT_PAGE_RIGHT_COLUMN      = -10;
-
-    public static final int ALL_APPS_COLUMN = -11;
-
-    // Matrix related constant.
-    public static final int EMPTY = -1;
-    public static final int PIVOT = 100;
-
-    /**
-     * Returns true only if this utility class handles the key code.
-     */
-    public static boolean shouldConsume(int keyCode) {
-        return (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
-                keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
-                keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END ||
-                keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN);
-    }
-
-    public static int handleKeyEvent(int keyCode, int [][] map, int iconIdx, int pageIndex,
-            int pageCount, boolean isRtl) {
-
-        int cntX = map == null ? -1 : map.length;
-        int cntY = map == null ? -1 : map[0].length;
-
-        if (DEBUG) {
-            Log.v(TAG, String.format(
-                    "handleKeyEvent START: cntX=%d, cntY=%d, iconIdx=%d, pageIdx=%d, pageCnt=%d",
-                    cntX, cntY, iconIdx, pageIndex, pageCount));
-        }
-
-        int newIndex = NOOP;
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/, isRtl);
-                if (!isRtl && newIndex == NOOP && pageIndex > 0) {
-                    newIndex = PREVIOUS_PAGE_RIGHT_COLUMN;
-                } else if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
-                    newIndex = NEXT_PAGE_RIGHT_COLUMN;
-                }
-                break;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/, isRtl);
-                if (!isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
-                    newIndex = NEXT_PAGE_LEFT_COLUMN;
-                } else if (isRtl && newIndex == NOOP && pageIndex > 0) {
-                    newIndex = PREVIOUS_PAGE_LEFT_COLUMN;
-                }
-                break;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                newIndex = handleDpadVertical(iconIdx, cntX, cntY, map, 1  /*increment*/);
-                break;
-            case KeyEvent.KEYCODE_DPAD_UP:
-                newIndex = handleDpadVertical(iconIdx, cntX, cntY, map, -1  /*increment*/);
-                break;
-            case KeyEvent.KEYCODE_MOVE_HOME:
-                newIndex = handleMoveHome();
-                break;
-            case KeyEvent.KEYCODE_MOVE_END:
-                newIndex = handleMoveEnd();
-                break;
-            case KeyEvent.KEYCODE_PAGE_DOWN:
-                newIndex = handlePageDown(pageIndex, pageCount);
-                break;
-            case KeyEvent.KEYCODE_PAGE_UP:
-                newIndex = handlePageUp(pageIndex);
-                break;
-            default:
-                break;
-        }
-
-        if (DEBUG) {
-            Log.v(TAG, String.format("handleKeyEvent FINISH: index [%d -> %s]",
-                    iconIdx, getStringIndex(newIndex)));
-        }
-        return newIndex;
-    }
-
-    /**
-     * Returns a matrix of size (m x n) that has been initialized with {@link #EMPTY}.
-     *
-     * @param m                 number of columns in the matrix
-     * @param n                 number of rows in the matrix
-     */
-    // TODO: get rid of dynamic matrix creation.
-    private static int[][] createFullMatrix(int m, int n) {
-        int[][] matrix = new int [m][n];
-
-        for (int i=0; i < m;i++) {
-            Arrays.fill(matrix[i], EMPTY);
-        }
-        return matrix;
-    }
-
-    /**
-     * Returns a matrix of size same as the {@link CellLayout} dimension that is initialized with the
-     * index of the child view.
-     */
-    // TODO: get rid of the dynamic matrix creation
-    public static int[][] createSparseMatrix(CellLayout layout) {
-        ShortcutAndWidgetContainer parent = layout.getShortcutsAndWidgets();
-        final int m = layout.getCountX();
-        final int n = layout.getCountY();
-        final boolean invert = parent.invertLayoutHorizontally();
-
-        int[][] matrix = createFullMatrix(m, n);
-
-        // Iterate thru the children.
-        for (int i = 0; i < parent.getChildCount(); i++ ) {
-            View cell = parent.getChildAt(i);
-            if (!cell.isFocusable()) {
-                continue;
-            }
-            int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
-            int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
-            int x = invert ? (m - cx - 1) : cx;
-            if (x < m && cy < n) { // check if view fits into matrix, else skip
-                matrix[x][cy] = i;
-            }
-        }
-        if (DEBUG) {
-            printMatrix(matrix);
-        }
-        return matrix;
-    }
-
-    /**
-     * Creates a sparse matrix that merges the icon and hotseat view group using the cell layout.
-     * The size of the returning matrix is [icon column count x (icon + hotseat row count)]
-     * in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)]
-     */
-    // TODO: get rid of the dynamic matrix creation
-    public static int[][] createSparseMatrixWithHotseat(
-            CellLayout iconLayout, CellLayout hotseatLayout, DeviceProfile dp) {
-
-        ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
-        ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
-
-        boolean isHotseatHorizontal = !dp.isVerticalBarLayout();
-
-        int m, n;
-        if (isHotseatHorizontal) {
-            m = hotseatLayout.getCountX();
-            n = iconLayout.getCountY() + hotseatLayout.getCountY();
-        } else {
-            m = iconLayout.getCountX() + hotseatLayout.getCountX();
-            n = hotseatLayout.getCountY();
-        }
-        int[][] matrix = createFullMatrix(m, n);
-        // Iterate through the children of the workspace.
-        for (int i = 0; i < iconParent.getChildCount(); i++) {
-            View cell = iconParent.getChildAt(i);
-            if (!cell.isFocusable()) {
-                continue;
-            }
-            int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
-            int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
-            matrix[cx][cy] = i;
-        }
-
-        // Iterate thru the children of the hotseat.
-        for (int i = hotseatParent.getChildCount() - 1; i >= 0; i--) {
-            if (isHotseatHorizontal) {
-                int cx = ((CellLayout.LayoutParams)
-                        hotseatParent.getChildAt(i).getLayoutParams()).cellX;
-                matrix[cx][iconLayout.getCountY()] = iconParent.getChildCount() + i;
-            } else {
-                int cy = ((CellLayout.LayoutParams)
-                        hotseatParent.getChildAt(i).getLayoutParams()).cellY;
-                matrix[iconLayout.getCountX()][cy] = iconParent.getChildCount() + i;
-            }
-        }
-        if (DEBUG) {
-            printMatrix(matrix);
-        }
-        return matrix;
-    }
-
-    /**
-     * Creates a sparse matrix that merges the icon of previous/next page and last column of
-     * current page. When left key is triggered on the leftmost column, sparse matrix is created
-     * that combines previous page matrix and an extra column on the right. Likewise, when right
-     * key is triggered on the rightmost column, sparse matrix is created that combines this column
-     * on the 0th column and the next page matrix.
-     *
-     * @param pivotX    x coordinate of the focused item in the current page
-     * @param pivotY    y coordinate of the focused item in the current page
-     */
-    // TODO: get rid of the dynamic matrix creation
-    public static int[][] createSparseMatrixWithPivotColumn(CellLayout iconLayout,
-            int pivotX, int pivotY) {
-
-        ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
-
-        int[][] matrix = createFullMatrix(iconLayout.getCountX() + 1, iconLayout.getCountY());
-
-        // Iterate thru the children of the top parent.
-        for (int i = 0; i < iconParent.getChildCount(); i++) {
-            View cell = iconParent.getChildAt(i);
-            if (!cell.isFocusable()) {
-                continue;
-            }
-            int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
-            int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
-            if (pivotX < 0) {
-                matrix[cx - pivotX][cy] = i;
-            } else {
-                matrix[cx][cy] = i;
-            }
-        }
-
-        if (pivotX < 0) {
-            matrix[0][pivotY] = PIVOT;
-        } else {
-            matrix[pivotX][pivotY] = PIVOT;
-        }
-        if (DEBUG) {
-            printMatrix(matrix);
-        }
-        return matrix;
-    }
-
-    //
-    // key event handling methods.
-    //
-
-    /**
-     * Calculates icon that has is closest to the horizontal axis in reference to the cur icon.
-     *
-     * Example of the check order for KEYCODE_DPAD_RIGHT:
-     * [  ][  ][13][14][15]
-     * [  ][ 6][ 8][10][12]
-     * [ X][ 1][ 2][ 3][ 4]
-     * [  ][ 5][ 7][ 9][11]
-     */
-    // TODO: add unit tests to verify all permutation.
-    private static int handleDpadHorizontal(int iconIdx, int cntX, int cntY,
-            int[][] matrix, int increment, boolean isRtl) {
-        if(matrix == null) {
-            throw new IllegalStateException("Dpad navigation requires a matrix.");
-        }
-        int newIconIndex = NOOP;
-
-        int xPos = -1;
-        int yPos = -1;
-        // Figure out the location of the icon.
-        for (int i = 0; i < cntX; i++) {
-            for (int j = 0; j < cntY; j++) {
-                if (matrix[i][j] == iconIdx) {
-                    xPos = i;
-                    yPos = j;
-                }
-            }
-        }
-        if (DEBUG) {
-            Log.v(TAG, String.format("\thandleDpadHorizontal: \t[x, y]=[%d, %d] iconIndex=%d",
-                    xPos, yPos, iconIdx));
-        }
-
-        // Rule1: check first in the horizontal direction
-        for (int x = xPos + increment; 0 <= x && x < cntX; x += increment) {
-            if ((newIconIndex = inspectMatrix(x, yPos, cntX, cntY, matrix)) != NOOP
-                    && newIconIndex != ALL_APPS_COLUMN) {
-                return newIconIndex;
-            }
-        }
-
-        // Rule2: check (x1-n, yPos + increment),   (x1-n, yPos - increment)
-        //              (x2-n, yPos + 2*increment), (x2-n, yPos - 2*increment)
-        int nextYPos1;
-        int nextYPos2;
-        boolean haveCrossedAllAppsColumn1 = false;
-        boolean haveCrossedAllAppsColumn2 = false;
-        int x = -1;
-        for (int coeff = 1; coeff < cntY; coeff++) {
-            nextYPos1 = yPos + coeff * increment;
-            nextYPos2 = yPos - coeff * increment;
-            x = xPos + increment * coeff;
-            if (inspectMatrix(x, nextYPos1, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
-                haveCrossedAllAppsColumn1 = true;
-            }
-            if (inspectMatrix(x, nextYPos2, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
-                haveCrossedAllAppsColumn2 = true;
-            }
-            for (; 0 <= x && x < cntX; x += increment) {
-                int offset1 = haveCrossedAllAppsColumn1 && x < cntX - 1 ? increment : 0;
-                newIconIndex = inspectMatrix(x, nextYPos1 + offset1, cntX, cntY, matrix);
-                if (newIconIndex != NOOP) {
-                    return newIconIndex;
-                }
-                int offset2 = haveCrossedAllAppsColumn2 && x < cntX - 1 ? -increment : 0;
-                newIconIndex = inspectMatrix(x, nextYPos2 + offset2, cntX, cntY, matrix);
-                if (newIconIndex != NOOP) {
-                    return newIconIndex;
-                }
-            }
-        }
-
-        // Rule3: if switching between pages, do a brute-force search to find an item that was
-        //        missed by rules 1 and 2 (such as when going from a bottom right icon to top left)
-        if (iconIdx == PIVOT) {
-            if (isRtl) {
-                return increment < 0 ? NEXT_PAGE_FIRST_ITEM : PREVIOUS_PAGE_LAST_ITEM;
-            }
-            return increment < 0 ? PREVIOUS_PAGE_LAST_ITEM : NEXT_PAGE_FIRST_ITEM;
-        }
-        return newIconIndex;
-    }
-
-    /**
-     * Calculates icon that is closest to the vertical axis in reference to the current icon.
-     *
-     * Example of the check order for KEYCODE_DPAD_DOWN:
-     * [  ][  ][  ][ X][  ][  ][  ]
-     * [  ][  ][ 5][ 1][ 4][  ][  ]
-     * [  ][10][ 7][ 2][ 6][ 9][  ]
-     * [14][12][ 9][ 3][ 8][11][13]
-     */
-    // TODO: add unit tests to verify all permutation.
-    private static int handleDpadVertical(int iconIndex, int cntX, int cntY,
-            int [][] matrix, int increment) {
-        int newIconIndex = NOOP;
-        if(matrix == null) {
-            throw new IllegalStateException("Dpad navigation requires a matrix.");
-        }
-
-        int xPos = -1;
-        int yPos = -1;
-        // Figure out the location of the icon.
-        for (int i = 0; i< cntX; i++) {
-            for (int j = 0; j < cntY; j++) {
-                if (matrix[i][j] == iconIndex) {
-                    xPos = i;
-                    yPos = j;
-                }
-            }
-        }
-
-        if (DEBUG) {
-            Log.v(TAG, String.format("\thandleDpadVertical: \t[x, y]=[%d, %d] iconIndex=%d",
-                    xPos, yPos, iconIndex));
-        }
-
-        // Rule1: check first in the dpad direction
-        for (int y = yPos + increment; 0 <= y && y <cntY && 0 <= y; y += increment) {
-            if ((newIconIndex = inspectMatrix(xPos, y, cntX, cntY, matrix)) != NOOP
-                    && newIconIndex != ALL_APPS_COLUMN) {
-                return newIconIndex;
-            }
-        }
-
-        // Rule2: check (xPos + increment, y_(1-n)),   (xPos - increment, y_(1-n))
-        //              (xPos + 2*increment, y_(2-n))), (xPos - 2*increment, y_(2-n))
-        int nextXPos1;
-        int nextXPos2;
-        boolean haveCrossedAllAppsColumn1 = false;
-        boolean haveCrossedAllAppsColumn2 = false;
-        int y = -1;
-        for (int coeff = 1; coeff < cntX; coeff++) {
-            nextXPos1 = xPos + coeff * increment;
-            nextXPos2 = xPos - coeff * increment;
-            y = yPos + increment * coeff;
-            if (inspectMatrix(nextXPos1, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
-                haveCrossedAllAppsColumn1 = true;
-            }
-            if (inspectMatrix(nextXPos2, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
-                haveCrossedAllAppsColumn2 = true;
-            }
-            for (; 0 <= y && y < cntY; y = y + increment) {
-                int offset1 = haveCrossedAllAppsColumn1 && y < cntY - 1 ? increment : 0;
-                newIconIndex = inspectMatrix(nextXPos1 + offset1, y, cntX, cntY, matrix);
-                if (newIconIndex != NOOP) {
-                    return newIconIndex;
-                }
-                int offset2 = haveCrossedAllAppsColumn2 && y < cntY - 1 ? -increment : 0;
-                newIconIndex = inspectMatrix(nextXPos2 + offset2, y, cntX, cntY, matrix);
-                if (newIconIndex != NOOP) {
-                    return newIconIndex;
-                }
-            }
-        }
-        return newIconIndex;
-    }
-
-    private static int handleMoveHome() {
-        return CURRENT_PAGE_FIRST_ITEM;
-    }
-
-    private static int handleMoveEnd() {
-        return CURRENT_PAGE_LAST_ITEM;
-    }
-
-    private static int handlePageDown(int pageIndex, int pageCount) {
-        if (pageIndex < pageCount -1) {
-            return NEXT_PAGE_FIRST_ITEM;
-        }
-        return CURRENT_PAGE_LAST_ITEM;
-    }
-
-    private static int handlePageUp(int pageIndex) {
-        if (pageIndex > 0) {
-            return PREVIOUS_PAGE_FIRST_ITEM;
-        } else {
-            return CURRENT_PAGE_FIRST_ITEM;
-        }
-    }
-
-    //
-    // Helper methods.
-    //
-
-    private static boolean isValid(int xPos, int yPos, int countX, int countY) {
-        return (0 <= xPos && xPos < countX && 0 <= yPos && yPos < countY);
-    }
-
-    private static int inspectMatrix(int x, int y, int cntX, int cntY, int[][] matrix) {
-        int newIconIndex = NOOP;
-        if (isValid(x, y, cntX, cntY)) {
-            if (matrix[x][y] != -1) {
-                newIconIndex = matrix[x][y];
-                if (DEBUG) {
-                    Log.v(TAG, String.format("\t\tinspect: \t[x, y]=[%d, %d] %d",
-                            x, y, matrix[x][y]));
-                }
-                return newIconIndex;
-            }
-        }
-        return newIconIndex;
-    }
-
-    /**
-     * Only used for debugging.
-     */
-    private static String getStringIndex(int index) {
-        switch(index) {
-            case NOOP: return "NOOP";
-            case PREVIOUS_PAGE_FIRST_ITEM:  return "PREVIOUS_PAGE_FIRST";
-            case PREVIOUS_PAGE_LAST_ITEM:   return "PREVIOUS_PAGE_LAST";
-            case PREVIOUS_PAGE_RIGHT_COLUMN:return "PREVIOUS_PAGE_RIGHT_COLUMN";
-            case CURRENT_PAGE_FIRST_ITEM:   return "CURRENT_PAGE_FIRST";
-            case CURRENT_PAGE_LAST_ITEM:    return "CURRENT_PAGE_LAST";
-            case NEXT_PAGE_FIRST_ITEM:      return "NEXT_PAGE_FIRST";
-            case NEXT_PAGE_LEFT_COLUMN:     return "NEXT_PAGE_LEFT_COLUMN";
-            case ALL_APPS_COLUMN:           return "ALL_APPS_COLUMN";
-            default:
-                return Integer.toString(index);
-        }
-    }
-
-    /**
-     * Only used for debugging.
-     */
-    private static void printMatrix(int[][] matrix) {
-        Log.v(TAG, "\tprintMap:");
-        int m = matrix.length;
-        int n = matrix[0].length;
-
-        for (int j=0; j < n; j++) {
-            String colY = "\t\t";
-            for (int i=0; i < m; i++) {
-                colY +=  String.format("%3d",matrix[i][j]);
-            }
-            Log.v(TAG, colY);
-        }
-    }
-
-    /**
-     * @param edgeColumn the column of the new icon. either {@link #NEXT_PAGE_LEFT_COLUMN} or
-     * {@link #NEXT_PAGE_RIGHT_COLUMN}
-     * @return the view adjacent to {@param oldView} in the {@param nextPage} of the folder.
-     */
-    public static View getAdjacentChildInNextFolderPage(
-            ShortcutAndWidgetContainer nextPage, View oldView, int edgeColumn) {
-        final int newRow = ((CellLayout.LayoutParams) oldView.getLayoutParams()).cellY;
-
-        int column = (edgeColumn == NEXT_PAGE_LEFT_COLUMN) ^ nextPage.invertLayoutHorizontally()
-                ? 0 : (((CellLayout) nextPage.getParent()).getCountX() - 1);
-
-        for (; column >= 0; column--) {
-            for (int row = newRow; row >= 0; row--) {
-                View newView = nextPage.getChildAt(column, row);
-                if (newView != null) {
-                    return newView;
-                }
-            }
-        }
-        return null;
-    }
-}
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index fcb96d7..1cec0ec 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -16,20 +16,19 @@
 
 package com.android.launcher3.util;
 
-import android.content.Context;
+import android.os.FileUtils;
 import android.util.Log;
 
+import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.UUID;
 
 /**
  * Supports various IO utility functions
@@ -52,6 +51,9 @@
     }
 
     public static long copy(InputStream from, OutputStream to) throws IOException {
+        if (Utilities.ATLEAST_Q) {
+            return FileUtils.copy(from, to);
+        }
         byte[] buf = new byte[BUF_SIZE];
         long total = 0;
         int r;
@@ -62,25 +64,6 @@
         return total;
     }
 
-    /**
-     * Utility method to debug binary data
-     */
-    public static String createTempFile(Context context, byte[] data) {
-        if (!FeatureFlags.IS_STUDIO_BUILD) {
-            throw new IllegalStateException("Method only allowed in development mode");
-        }
-
-        String name = UUID.randomUUID().toString();
-        File file = new File(context.getCacheDir(), name);
-        try (FileOutputStream fo = new FileOutputStream(file)) {
-            fo.write(data);
-            fo.flush();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-        return file.getAbsolutePath();
-    }
-
     public static void closeSilently(Closeable c) {
         if (c != null) {
             try {
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 4d5405d..354609d 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -20,13 +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.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
 import java.util.HashSet;
+import java.util.Set;
 
 /**
  * A utility class to check for {@link ItemInfo}
@@ -36,34 +34,15 @@
     boolean matches(ItemInfo info, ComponentName cn);
 
     /**
-     * Filters {@param infos} to those satisfying the {@link #matches(ItemInfo, ComponentName)}.
+     * Returns true if the itemInfo matches this check
      */
-    default HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) {
-        HashSet<ItemInfo> filtered = new HashSet<>();
-        for (ItemInfo i : infos) {
-            if (i instanceof WorkspaceItemInfo) {
-                WorkspaceItemInfo info = (WorkspaceItemInfo) i;
-                ComponentName cn = info.getTargetComponent();
-                if (cn != null && matches(info, cn)) {
-                    filtered.add(info);
-                }
-            } else if (i instanceof FolderInfo) {
-                FolderInfo info = (FolderInfo) i;
-                for (WorkspaceItemInfo s : info.contents) {
-                    ComponentName cn = s.getTargetComponent();
-                    if (cn != null && matches(s, cn)) {
-                        filtered.add(s);
-                    }
-                }
-            } else if (i instanceof LauncherAppWidgetInfo) {
-                LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
-                ComponentName cn = info.providerName;
-                if (cn != null && matches(info, cn)) {
-                    filtered.add(info);
-                }
-            }
+    default boolean matchesInfo(ItemInfo info) {
+        if (info != null) {
+            ComponentName cn = info.getTargetComponent();
+            return cn != null && matches(info, cn);
+        } else {
+            return false;
         }
-        return filtered;
     }
 
     /**
@@ -81,11 +60,10 @@
     }
 
     /**
-     * Returns a new matcher which returns the opposite boolean value of the provided
-     * {@param matcher}.
+     * Returns a new matcher with returns the opposite value of this.
      */
-    static ItemInfoMatcher not(ItemInfoMatcher matcher) {
-        return (info, cn) -> !matcher.matches(info, cn);
+    default ItemInfoMatcher negate() {
+        return (info, cn) -> !matches(info, cn);
     }
 
     static ItemInfoMatcher ofUser(UserHandle user) {
@@ -96,16 +74,19 @@
         return (info, cn) -> components.contains(cn) && info.user.equals(user);
     }
 
-    static ItemInfoMatcher ofPackages(HashSet<String> packageNames, UserHandle user) {
+    static ItemInfoMatcher ofPackages(Set<String> packageNames, UserHandle user) {
         return (info, cn) -> packageNames.contains(cn.getPackageName()) && info.user.equals(user);
     }
 
-    static ItemInfoMatcher ofShortcutKeys(HashSet<ShortcutKey> keys) {
+    static ItemInfoMatcher ofShortcutKeys(Set<ShortcutKey> keys) {
         return  (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
                         keys.contains(ShortcutKey.fromItemInfo(info));
     }
 
-    static ItemInfoMatcher ofItemIds(IntSparseArrayMap<Boolean> ids, Boolean matchDefault) {
-        return (info, cn) -> ids.get(info.id, matchDefault);
+    /**
+     * Returns a matcher for items with provided ids
+     */
+    static ItemInfoMatcher ofItemIds(IntSet ids) {
+        return (info, cn) -> ids.contains(info.id);
     }
 }
diff --git a/src/com/android/launcher3/util/MultiHashMap.java b/src/com/android/launcher3/util/MultiHashMap.java
deleted file mode 100644
index b7275c1..0000000
--- a/src/com/android/launcher3/util/MultiHashMap.java
+++ /dev/null
@@ -1,52 +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.util;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * A utility map from keys to an ArrayList of values.
- */
-public class MultiHashMap<K, V> extends HashMap<K, ArrayList<V>> {
-
-    public MultiHashMap() { }
-
-    public MultiHashMap(int size) {
-        super(size);
-    }
-
-    public void addToList(K key, V value) {
-        ArrayList<V> list = get(key);
-        if (list == null) {
-            list = new ArrayList<>();
-            list.add(value);
-            put(key, list);
-        } else {
-            list.add(value);
-        }
-    }
-
-    @Override
-    public MultiHashMap<K, V> clone() {
-        MultiHashMap<K, V> map = new MultiHashMap<>(size());
-        for (Entry<K, ArrayList<V>> entry : entrySet()) {
-            map.put(entry.getKey(), new ArrayList<V>(entry.getValue()));
-        }
-        return map;
-    }
-}
diff --git a/src/com/android/launcher3/util/ObjectWrapper.java b/src/com/android/launcher3/util/ObjectWrapper.java
index e5b4707..a715821 100644
--- a/src/com/android/launcher3/util/ObjectWrapper.java
+++ b/src/com/android/launcher3/util/ObjectWrapper.java
@@ -42,4 +42,11 @@
     public static IBinder wrap(Object obj) {
         return new ObjectWrapper<>(obj);
     }
+
+    public static <T> T unwrap(IBinder binder) {
+        if (binder instanceof ObjectWrapper) {
+            return ((ObjectWrapper<T>) binder).get();
+        }
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index d4e074c..40bc9c3 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -33,42 +33,43 @@
 public class OnboardingPrefs<T extends Launcher> {
 
     public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
-    public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
     public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
-    public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
-    public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
     public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
     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";
 
     /**
      * Events that either have happened or have not (booleans).
      */
     @StringDef(value = {
             HOME_BOUNCE_SEEN,
-            SHELF_BOUNCE_SEEN
+            HOTSEAT_LONGPRESS_TIP_SEEN,
+            SEARCH_EDU_SEEN
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface EventBoolKey {}
+    public @interface EventBoolKey {
+    }
 
     /**
      * Events that occur multiple times, which we count up to a max defined in {@link #MAX_COUNTS}.
      */
     @StringDef(value = {
             HOME_BOUNCE_COUNT,
-            SHELF_BOUNCE_COUNT,
-            ALL_APPS_COUNT,
-            HOTSEAT_DISCOVERY_TIP_COUNT
+            HOTSEAT_DISCOVERY_TIP_COUNT,
+            SEARCH_SNACKBAR_COUNT
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface EventCountKey {}
+    public @interface EventCountKey {
+    }
 
     private static final Map<String, Integer> MAX_COUNTS;
+
     static {
-        Map<String, Integer> maxCounts = new ArrayMap<>(3);
+        Map<String, Integer> maxCounts = new ArrayMap<>(4);
         maxCounts.put(HOME_BOUNCE_COUNT, 3);
-        maxCounts.put(SHELF_BOUNCE_COUNT, 3);
-        maxCounts.put(ALL_APPS_COUNT, 5);
         maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
+        maxCounts.put(SEARCH_SNACKBAR_COUNT, 3);
         MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
     }
 
@@ -108,6 +109,7 @@
 
     /**
      * Add 1 to the given event count, if we haven't already reached the max count.
+     *
      * @return Whether we have now reached the max count.
      */
     public boolean incrementEventCount(@EventCountKey String eventKey) {
diff --git a/src/com/android/launcher3/util/OverScroller.java b/src/com/android/launcher3/util/OverScroller.java
deleted file mode 100644
index 87e6986..0000000
--- a/src/com/android/launcher3/util/OverScroller.java
+++ /dev/null
@@ -1,867 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.anim.Interpolators.SCROLL;
-
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.hardware.SensorManager;
-import android.util.Log;
-import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import com.android.launcher3.R;
-import com.android.systemui.plugins.ResourceProvider;
-
-/**
- * Based on {@link android.widget.OverScroller} supporting only 1-d scrolling and with more
- * customization options.
- */
-public class OverScroller {
-    private int mMode;
-
-    private final SplineOverScroller mScroller;
-
-    private TimeInterpolator mInterpolator;
-
-    private final boolean mFlywheel;
-
-    private static final int DEFAULT_DURATION = 250;
-    private static final int SCROLL_MODE = 0;
-    private static final int FLING_MODE = 1;
-
-    /**
-     * Creates an OverScroller with a viscous fluid scroll interpolator and flywheel.
-     * @param context
-     */
-    public OverScroller(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Creates an OverScroller with flywheel enabled.
-     * @param context The context of this application.
-     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
-     * be used.
-     */
-    public OverScroller(Context context, Interpolator interpolator) {
-        this(context, interpolator, true);
-    }
-
-    /**
-     * Creates an OverScroller.
-     * @param context The context of this application.
-     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
-     * be used.
-     * @param flywheel If true, successive fling motions will keep on increasing scroll speed.
-     */
-    public OverScroller(Context context, Interpolator interpolator, boolean flywheel) {
-        if (interpolator == null) {
-            mInterpolator = SCROLL;
-        } else {
-            mInterpolator = interpolator;
-        }
-        mFlywheel = flywheel;
-        mScroller = new SplineOverScroller(context);
-    }
-
-    public void setInterpolator(TimeInterpolator interpolator) {
-        if (interpolator == null) {
-            mInterpolator = SCROLL;
-        } else {
-            mInterpolator = interpolator;
-        }
-    }
-
-    /**
-     * The amount of friction applied to flings. The default value
-     * is {@link ViewConfiguration#getScrollFriction}.
-     *
-     * @param friction A scalar dimension-less value representing the coefficient of
-     *         friction.
-     */
-    public final void setFriction(float friction) {
-        mScroller.setFriction(friction);
-    }
-
-    /**
-     *
-     * Returns whether the scroller has finished scrolling.
-     *
-     * @return True if the scroller has finished scrolling, false otherwise.
-     */
-    public final boolean isFinished() {
-        return mScroller.mFinished;
-    }
-
-    /**
-     * Force the finished field to a particular value. Contrary to
-     * {@link #abortAnimation()}, forcing the animation to finished
-     * does NOT cause the scroller to move to the final x and y
-     * position.
-     *
-     * @param finished The new finished value.
-     */
-    public final void forceFinished(boolean finished) {
-        mScroller.mFinished = finished;
-    }
-
-    /**
-     * Returns the current offset in the scroll.
-     *
-     * @return The new offset as an absolute distance from the origin.
-     */
-    public final int getCurrPos() {
-        return mScroller.mCurrentPosition;
-    }
-
-    /**
-     * Returns the absolute value of the current velocity.
-     *
-     * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
-     */
-    public float getCurrVelocity() {
-        return mScroller.mCurrVelocity;
-    }
-
-    /**
-     * Returns the start offset in the scroll.
-     *
-     * @return The start offset as an absolute distance from the origin.
-     */
-    public final int getStartPos() {
-        return mScroller.mStart;
-    }
-
-    /**
-     * Returns where the scroll will end. Valid only for "fling" scrolls.
-     *
-     * @return The final offset as an absolute distance from the origin.
-     */
-    public final int getFinalPos() {
-        return mScroller.mFinal;
-    }
-
-    /**
-     * Returns how long the scroll event will take, in milliseconds.
-     *
-     * Note that if mScroller.mState == SPRING, this duration is ignored, so can only
-     * serve as an estimate for how long the spring-controlled scroll will take.
-     *
-     * @return The duration of the scroll in milliseconds.
-     */
-    public final int getDuration() {
-        return mScroller.mDuration;
-    }
-
-    /**
-     * Extend the scroll animation. This allows a running animation to scroll
-     * further and longer, when used with {@link #setFinalPos(int)}.
-     *
-     * @param extend Additional time to scroll in milliseconds.
-     * @see #setFinalPos(int)
-     */
-    public void extendDuration(int extend) {
-        mScroller.extendDuration(extend);
-    }
-
-    /**
-     * Sets the final position for this scroller.
-     *
-     * @param newPos The new offset as an absolute distance from the origin.
-     * @see #extendDuration(int)
-     */
-    public void setFinalPos(int newPos) {
-        mScroller.setFinalPosition(newPos);
-    }
-
-    /**
-     * Call this when you want to know the new location. If it returns true, the
-     * animation is not yet finished.
-     */
-    public boolean computeScrollOffset() {
-        if (isFinished()) {
-            return false;
-        }
-
-        switch (mMode) {
-            case SCROLL_MODE:
-                if (isSpringing()) {
-                    return true;
-                }
-                long time = AnimationUtils.currentAnimationTimeMillis();
-                // Any scroller can be used for time, since they were started
-                // together in scroll mode. We use X here.
-                final long elapsedTime = time - mScroller.mStartTime;
-
-                final int duration = mScroller.mDuration;
-                if (elapsedTime < duration) {
-                    final float q = mInterpolator.getInterpolation(elapsedTime / (float) duration);
-                    mScroller.updateScroll(q);
-                } else {
-                    abortAnimation();
-                }
-                break;
-
-            case FLING_MODE:
-                if (!mScroller.mFinished) {
-                    if (!mScroller.update()) {
-                        if (!mScroller.continueWhenFinished()) {
-                            mScroller.finish();
-                        }
-                    }
-                }
-
-                break;
-        }
-
-        return true;
-    }
-
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     * The scroll will use the default value of 250 milliseconds for the
-     * duration.
-     *
-     * @param start Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param delta Distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     */
-    public void startScroll(int start, int delta) {
-        startScroll(start, delta, DEFAULT_DURATION);
-    }
-
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     *
-     * @param start Starting scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param delta Distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param duration Duration of the scroll in milliseconds.
-     */
-    public void startScroll(int start, int delta, int duration) {
-        mMode = SCROLL_MODE;
-        mScroller.startScroll(start, delta, duration);
-    }
-
-    /**
-     * Start scrolling using a spring by providing a starting point and the distance to travel.
-     *
-     * @param start Starting scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param delta Distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param duration Duration of the scroll in milliseconds.
-     * @param velocity The starting velocity for the spring in px per ms.
-     */
-    public void startScrollSpring(int start, int delta, int duration, float velocity) {
-        mMode = SCROLL_MODE;
-        mScroller.mState = mScroller.SPRING;
-        mScroller.startScroll(start, delta, duration, velocity);
-    }
-
-    /**
-     * Call this when you want to 'spring back' into a valid coordinate range.
-     *
-     * @param start Starting X coordinate
-     * @param min Minimum valid X value
-     * @param max Maximum valid X value
-     * @return true if a springback was initiated, false if startX and startY were
-     *          already within the valid range.
-     */
-    public boolean springBack(int start, int min, int max) {
-        mMode = FLING_MODE;
-        return mScroller.springback(start, min, max);
-    }
-
-    public void fling(int start, int velocity, int min, int max) {
-        fling(start, velocity, min, max, 0);
-    }
-
-    /**
-     * Start scrolling based on a fling gesture. The distance traveled will
-     * depend on the initial velocity of the fling.
-     *  @param start Starting point of the scroll (X)
-     * @param velocity Initial velocity of the fling (X) measured in pixels per
-     *            second.
-     * @param min Minimum X value. The scroller will not scroll past this point
- *            unless overX > 0. If overfling is allowed, it will use minX as
- *            a springback boundary.
-     * @param max Maximum X value. The scroller will not scroll past this point
-*            unless overX > 0. If overfling is allowed, it will use maxX as
-*            a springback boundary.
-     * @param over Overfling range. If > 0, horizontal overfling in either
-*            direction will be possible.
-     */
-    public void fling(int start, int velocity, int min, int max, int over) {
-        // Continue a scroll or fling in progress
-        if (mFlywheel && !isFinished()) {
-            float oldVelocityX = mScroller.mCurrVelocity;
-            if (Math.signum(velocity) == Math.signum(oldVelocityX)) {
-                velocity += oldVelocityX;
-            }
-        }
-
-        mMode = FLING_MODE;
-        mScroller.fling(start, velocity, min, max, over);
-    }
-
-    /**
-     * Notify the scroller that we've reached a horizontal boundary.
-     * Normally the information to handle this will already be known
-     * when the animation is started, such as in a call to one of the
-     * fling functions. However there are cases where this cannot be known
-     * in advance. This function will transition the current motion and
-     * animate from startX to finalX as appropriate.
-     *  @param start Starting/current X position
-     * @param finalPos Desired final X position
-     * @param over Magnitude of overscroll allowed. This should be the maximum
-     */
-    public void notifyEdgeReached(int start, int finalPos, int over) {
-        mScroller.notifyEdgeReached(start, finalPos, over);
-    }
-
-    /**
-     * Returns whether the current Scroller is currently returning to a valid position.
-     * Valid bounds were provided by the
-     * {@link #fling(int, int, int, int, int)} method.
-     *
-     * One should check this value before calling
-     * {@link #startScroll(int, int)} as the interpolation currently in progress
-     * to restore a valid position will then be stopped. The caller has to take into account
-     * the fact that the started scroll will start from an overscrolled position.
-     *
-     * @return true when the current position is overscrolled and in the process of
-     *         interpolating back to a valid value.
-     */
-    public boolean isOverScrolled() {
-        return (!mScroller.mFinished && mScroller.mState != SplineOverScroller.SPLINE);
-    }
-
-    /**
-     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
-     * aborting the animating causes the scroller to move to the final x and y
-     * positions.
-     *
-     * @see #forceFinished(boolean)
-     */
-    public void abortAnimation() {
-        mScroller.finish();
-    }
-
-    /**
-     * Returns the time elapsed since the beginning of the scrolling.
-     *
-     * @return The elapsed time in milliseconds.
-     *
-     * @hide
-     */
-    public int timePassed() {
-        final long time = AnimationUtils.currentAnimationTimeMillis();
-        return (int) (time - mScroller.mStartTime);
-    }
-
-    public boolean isSpringing() {
-        return mScroller.mState == SplineOverScroller.SPRING && !isFinished();
-    }
-
-    static class SplineOverScroller {
-        // Initial position
-        private int mStart;
-
-        // Current position
-        private int mCurrentPosition;
-
-        // Final position
-        private int mFinal;
-
-        // Initial velocity
-        private int mVelocity;
-
-        // Current velocity
-        private float mCurrVelocity;
-
-        // Constant current deceleration
-        private float mDeceleration;
-
-        // Animation starting time, in system milliseconds
-        private long mStartTime;
-
-        // Animation duration, in milliseconds
-        private int mDuration;
-
-        // Duration to complete spline component of animation
-        private int mSplineDuration;
-
-        // Distance to travel along spline animation
-        private int mSplineDistance;
-
-        // Whether the animation is currently in progress
-        private boolean mFinished;
-
-        // The allowed overshot distance before boundary is reached.
-        private int mOver;
-
-        // Fling friction
-        private float mFlingFriction = ViewConfiguration.getScrollFriction();
-
-        // Current state of the animation.
-        private int mState = SPLINE;
-
-        private Context mContext;
-        private SpringAnimation mSpring;
-
-        // Constant gravity value, used in the deceleration phase.
-        private static final float GRAVITY = 2000.0f;
-
-        // A context-specific coefficient adjusted to physical values.
-        private float mPhysicalCoeff;
-
-        private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
-        private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1)
-        private static final float START_TENSION = 0.5f;
-        private static final float END_TENSION = 1.0f;
-        private static final float P1 = START_TENSION * INFLEXION;
-        private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION);
-
-        private static final int NB_SAMPLES = 100;
-        private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1];
-        private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1];
-
-        private static final int SPLINE = 0;
-        private static final int CUBIC = 1;
-        private static final int BALLISTIC = 2;
-        private static final int SPRING = 3;
-
-        private static final FloatPropertyCompat<SplineOverScroller> SPRING_PROPERTY =
-                new FloatPropertyCompat<SplineOverScroller>("splineOverScrollerSpring") {
-                    @Override
-                    public float getValue(SplineOverScroller scroller) {
-                        return scroller.mCurrentPosition;
-                    }
-
-                    @Override
-                    public void setValue(SplineOverScroller scroller, float value) {
-                        scroller.mCurrentPosition = (int) value;
-                    }
-                };
-
-        static {
-            float x_min = 0.0f;
-            float y_min = 0.0f;
-            for (int i = 0; i < NB_SAMPLES; i++) {
-                final float alpha = (float) i / NB_SAMPLES;
-
-                float x_max = 1.0f;
-                float x, tx, coef;
-                while (true) {
-                    x = x_min + (x_max - x_min) / 2.0f;
-                    coef = 3.0f * x * (1.0f - x);
-                    tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x;
-                    if (Math.abs(tx - alpha) < 1E-5) break;
-                    if (tx > alpha) x_max = x;
-                    else x_min = x;
-                }
-                SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x;
-
-                float y_max = 1.0f;
-                float y, dy;
-                while (true) {
-                    y = y_min + (y_max - y_min) / 2.0f;
-                    coef = 3.0f * y * (1.0f - y);
-                    dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y;
-                    if (Math.abs(dy - alpha) < 1E-5) break;
-                    if (dy > alpha) y_max = y;
-                    else y_min = y;
-                }
-                SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y;
-            }
-            SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
-        }
-
-        void setFriction(float friction) {
-            mFlingFriction = friction;
-        }
-
-        SplineOverScroller(Context context) {
-            mContext = context;
-            mFinished = true;
-            final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
-            mPhysicalCoeff = SensorManager.GRAVITY_EARTH // g (m/s^2)
-                    * 39.37f // inch/meter
-                    * ppi
-                    * 0.84f; // look and feel tuning
-        }
-
-        void updateScroll(float q) {
-            if (mState == SPRING) {
-                return;
-            }
-            mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
-        }
-
-        /*
-         * Get a signed deceleration that will reduce the velocity.
-         */
-        static private float getDeceleration(int velocity) {
-            return velocity > 0 ? -GRAVITY : GRAVITY;
-        }
-
-        /*
-         * Modifies mDuration to the duration it takes to get from start to newFinal using the
-         * spline interpolation. The previous duration was needed to get to oldFinal.
-         */
-        private void adjustDuration(int start, int oldFinal, int newFinal) {
-            final int oldDistance = oldFinal - start;
-            final int newDistance = newFinal - start;
-            final float x = Math.abs((float) newDistance / oldDistance);
-            final int index = (int) (NB_SAMPLES * x);
-            if (index < NB_SAMPLES) {
-                final float x_inf = (float) index / NB_SAMPLES;
-                final float x_sup = (float) (index + 1) / NB_SAMPLES;
-                final float t_inf = SPLINE_TIME[index];
-                final float t_sup = SPLINE_TIME[index + 1];
-                final float timeCoef = t_inf + (x - x_inf) / (x_sup - x_inf) * (t_sup - t_inf);
-                mDuration *= timeCoef;
-            }
-        }
-
-        void startScroll(int start, int distance, int duration) {
-            startScroll(start, distance, duration, 0);
-        }
-
-        void startScroll(int start, int distance, int duration, float velocity) {
-            mFinished = false;
-
-            mCurrentPosition = mStart = start;
-            mFinal = start + distance;
-
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mDuration = duration;
-
-            if (mSpring != null) {
-                mSpring.cancel();
-            }
-
-            if (mState == SPRING) {
-                mSpring = new SpringAnimation(this, SPRING_PROPERTY);
-
-                ResourceProvider rp = DynamicResource.provider(mContext);
-                float stiffness = rp.getFloat(R.dimen.horizontal_spring_stiffness);
-                float damping = rp.getFloat(R.dimen.horizontal_spring_damping_ratio);
-                mSpring.setSpring(new SpringForce(mFinal)
-                        .setStiffness(stiffness)
-                        .setDampingRatio(damping));
-                mSpring.setStartVelocity(velocity);
-                mSpring.animateToFinalPosition(mFinal);
-                mSpring.addEndListener((animation, canceled, value, velocity1) -> {
-                    mSpring = null;
-                    finish();
-                    mState = SPLINE;
-                });
-            }
-            // Unused
-            mDeceleration = 0.0f;
-            mVelocity = 0;
-        }
-
-        void finish() {
-            if (mSpring != null && mSpring.isRunning()) mSpring.cancel();
-
-            mCurrentPosition = mFinal;
-            // Not reset since WebView relies on this value for fast fling.
-            // TODO: restore when WebView uses the fast fling implemented in this class.
-            // mCurrVelocity = 0.0f;
-            mFinished = true;
-        }
-
-        void setFinalPosition(int position) {
-            mFinal = position;
-            if (mState == SPRING && mSpring != null) {
-                mSpring.animateToFinalPosition(mFinal);
-            }
-            mSplineDistance = mFinal - mStart;
-            mFinished = false;
-        }
-
-        void extendDuration(int extend) {
-            final long time = AnimationUtils.currentAnimationTimeMillis();
-            final int elapsedTime = (int) (time - mStartTime);
-            mDuration  = mSplineDuration = elapsedTime + extend;
-            mFinished = false;
-        }
-
-        boolean springback(int start, int min, int max) {
-            mFinished = true;
-
-            mCurrentPosition = mStart = mFinal = start;
-            mVelocity = 0;
-
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mDuration = 0;
-
-            if (start < min) {
-                startSpringback(start, min, 0);
-            } else if (start > max) {
-                startSpringback(start, max, 0);
-            }
-
-            return !mFinished;
-        }
-
-        private void startSpringback(int start, int end, int velocity) {
-            // mStartTime has been set
-            mFinished = false;
-            mState = CUBIC;
-            mCurrentPosition = mStart = start;
-            mFinal = end;
-            final int delta = start - end;
-            mDeceleration = getDeceleration(delta);
-            // TODO take velocity into account
-            mVelocity = -delta; // only sign is used
-            mOver = Math.abs(delta);
-            mDuration = (int) (1000.0 * Math.sqrt(-2.0 * delta / mDeceleration));
-        }
-
-        void fling(int start, int velocity, int min, int max, int over) {
-            mOver = over;
-            mFinished = false;
-            mCurrVelocity = mVelocity = velocity;
-            mDuration = mSplineDuration = 0;
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mCurrentPosition = mStart = start;
-
-            if (start > max || start < min) {
-                startAfterEdge(start, min, max, velocity);
-                return;
-            }
-
-            mState = SPLINE;
-            double totalDistance = 0.0;
-
-            if (velocity != 0) {
-                mDuration = mSplineDuration = getSplineFlingDuration(velocity);
-                totalDistance = getSplineFlingDistance(velocity);
-            }
-
-            mSplineDistance = (int) (totalDistance * Math.signum(velocity));
-            mFinal = start + mSplineDistance;
-
-            // Clamp to a valid final position
-            if (mFinal < min) {
-                adjustDuration(mStart, mFinal, min);
-                mFinal = min;
-            }
-
-            if (mFinal > max) {
-                adjustDuration(mStart, mFinal, max);
-                mFinal = max;
-            }
-        }
-
-        private double getSplineDeceleration(int velocity) {
-            return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
-        }
-
-        private double getSplineFlingDistance(int velocity) {
-            final double l = getSplineDeceleration(velocity);
-            final double decelMinusOne = DECELERATION_RATE - 1.0;
-            return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
-        }
-
-        /* Returns the duration, expressed in milliseconds */
-        private int getSplineFlingDuration(int velocity) {
-            final double l = getSplineDeceleration(velocity);
-            final double decelMinusOne = DECELERATION_RATE - 1.0;
-            return (int) (1000.0 * Math.exp(l / decelMinusOne));
-        }
-
-        private void fitOnBounceCurve(int start, int end, int velocity) {
-            // Simulate a bounce that started from edge
-            final float durationToApex = - velocity / mDeceleration;
-            // The float cast below is necessary to avoid integer overflow.
-            final float velocitySquared = (float) velocity * velocity;
-            final float distanceToApex = velocitySquared / 2.0f / Math.abs(mDeceleration);
-            final float distanceToEdge = Math.abs(end - start);
-            final float totalDuration = (float) Math.sqrt(
-                    2.0 * (distanceToApex + distanceToEdge) / Math.abs(mDeceleration));
-            mStartTime -= (int) (1000.0f * (totalDuration - durationToApex));
-            mCurrentPosition = mStart = end;
-            mVelocity = (int) (- mDeceleration * totalDuration);
-        }
-
-        private void startBounceAfterEdge(int start, int end, int velocity) {
-            mDeceleration = getDeceleration(velocity == 0 ? start - end : velocity);
-            fitOnBounceCurve(start, end, velocity);
-            onEdgeReached();
-        }
-
-        private void startAfterEdge(int start, int min, int max, int velocity) {
-            if (start > min && start < max) {
-                Log.e("OverScroller", "startAfterEdge called from a valid position");
-                mFinished = true;
-                return;
-            }
-            final boolean positive = start > max;
-            final int edge = positive ? max : min;
-            final int overDistance = start - edge;
-            boolean keepIncreasing = overDistance * velocity >= 0;
-            if (keepIncreasing) {
-                // Will result in a bounce or a to_boundary depending on velocity.
-                startBounceAfterEdge(start, edge, velocity);
-            } else {
-                final double totalDistance = getSplineFlingDistance(velocity);
-                if (totalDistance > Math.abs(overDistance)) {
-                    fling(start, velocity, positive ? min : start, positive ? start : max, mOver);
-                } else {
-                    startSpringback(start, edge, velocity);
-                }
-            }
-        }
-
-        void notifyEdgeReached(int start, int end, int over) {
-            // mState is used to detect successive notifications
-            if (mState == SPLINE) {
-                mOver = over;
-                mStartTime = AnimationUtils.currentAnimationTimeMillis();
-                // We were in fling/scroll mode before: current velocity is such that distance to
-                // edge is increasing. This ensures that startAfterEdge will not start a new fling.
-                startAfterEdge(start, end, end, (int) mCurrVelocity);
-            }
-        }
-
-        private void onEdgeReached() {
-            // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
-            // The float cast below is necessary to avoid integer overflow.
-            final float velocitySquared = (float) mVelocity * mVelocity;
-            float distance = velocitySquared / (2.0f * Math.abs(mDeceleration));
-            final float sign = Math.signum(mVelocity);
-
-            if (distance > mOver) {
-                // Default deceleration is not sufficient to slow us down before boundary
-                mDeceleration = - sign * velocitySquared / (2.0f * mOver);
-                distance = mOver;
-            }
-
-            mOver = (int) distance;
-            mState = BALLISTIC;
-            mFinal = mStart + (int) (mVelocity > 0 ? distance : -distance);
-            mDuration = - (int) (1000.0f * mVelocity / mDeceleration);
-        }
-
-        boolean continueWhenFinished() {
-            switch (mState) {
-                case SPLINE:
-                    // Duration from start to null velocity
-                    if (mDuration < mSplineDuration) {
-                        // If the animation was clamped, we reached the edge
-                        mCurrentPosition = mStart = mFinal;
-                        // TODO Better compute speed when edge was reached
-                        mVelocity = (int) mCurrVelocity;
-                        mDeceleration = getDeceleration(mVelocity);
-                        mStartTime += mDuration;
-                        onEdgeReached();
-                    } else {
-                        // Normal stop, no need to continue
-                        return false;
-                    }
-                    break;
-                case BALLISTIC:
-                    mStartTime += mDuration;
-                    startSpringback(mFinal, mStart, 0);
-                    break;
-                case CUBIC:
-                    return false;
-            }
-
-            update();
-            return true;
-        }
-
-        /*
-         * Update the current position and velocity for current time. Returns
-         * true if update has been done and false if animation duration has been
-         * reached.
-         */
-        boolean update() {
-            if (mState == SPRING) {
-                return mFinished;
-            }
-
-            final long time = AnimationUtils.currentAnimationTimeMillis();
-            final long currentTime = time - mStartTime;
-
-            if (currentTime == 0) {
-                // Skip work but report that we're still going if we have a nonzero duration.
-                return mDuration > 0;
-            }
-            if (currentTime > mDuration) {
-                return false;
-            }
-
-            double distance = 0.0;
-            switch (mState) {
-                case SPLINE: {
-                    final float t = (float) currentTime / mSplineDuration;
-                    final int index = (int) (NB_SAMPLES * t);
-                    float distanceCoef = 1.f;
-                    float velocityCoef = 0.f;
-                    if (index < NB_SAMPLES) {
-                        final float t_inf = (float) index / NB_SAMPLES;
-                        final float t_sup = (float) (index + 1) / NB_SAMPLES;
-                        final float d_inf = SPLINE_POSITION[index];
-                        final float d_sup = SPLINE_POSITION[index + 1];
-                        velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
-                        distanceCoef = d_inf + (t - t_inf) * velocityCoef;
-                    }
-
-                    distance = distanceCoef * mSplineDistance;
-                    mCurrVelocity = velocityCoef * mSplineDistance / mSplineDuration * 1000.0f;
-                    break;
-                }
-
-                case BALLISTIC: {
-                    final float t = currentTime / 1000.0f;
-                    mCurrVelocity = mVelocity + mDeceleration * t;
-                    distance = mVelocity * t + mDeceleration * t * t / 2.0f;
-                    break;
-                }
-
-                case CUBIC: {
-                    final float t = (float) (currentTime) / mDuration;
-                    final float t2 = t * t;
-                    final float sign = Math.signum(mVelocity);
-                    distance = sign * mOver * (3.0f * t2 - 2.0f * t * t2);
-                    mCurrVelocity = sign * mOver * 6.0f * (- t + t2);
-                    break;
-                }
-            }
-
-            mCurrentPosition = mStart + (int) Math.round(distance);
-
-            return true;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/OverlayEdgeEffect.java b/src/com/android/launcher3/util/OverlayEdgeEffect.java
new file mode 100644
index 0000000..8d455c6
--- /dev/null
+++ b/src/com/android/launcher3/util/OverlayEdgeEffect.java
@@ -0,0 +1,83 @@
+/*
+ * 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.graphics.Canvas;
+import android.widget.EdgeEffect;
+
+import com.android.launcher3.Utilities;
+import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
+
+/**
+ * Extension of {@link EdgeEffect} which shows the Launcher overlay
+ */
+public class OverlayEdgeEffect extends EdgeEffectCompat {
+
+    private final LauncherOverlay mOverlay;
+    private final boolean mIsRtl;
+
+    private float mDistance;
+    private boolean mIsScrolling;
+
+    public OverlayEdgeEffect(Context context, LauncherOverlay overlay) {
+        super(context);
+        mOverlay = overlay;
+        mIsRtl = Utilities.isRtl(context.getResources());
+
+    }
+
+    @Override
+    public float getDistance() {
+        return mDistance;
+    }
+
+    public float onPullDistance(float deltaDistance, float displacement) {
+        mDistance = Math.max(0f, deltaDistance + mDistance);
+        if (!mIsScrolling) {
+            mOverlay.onScrollInteractionBegin();
+            mIsScrolling = true;
+        }
+        mOverlay.onScrollChange(mDistance, mIsRtl);
+        return mDistance > 0 ? deltaDistance : 0;
+    }
+
+    @Override
+    public void onAbsorb(int velocity) { }
+
+    @Override
+    public boolean isFinished() {
+        return mDistance <= 0;
+    }
+
+    @Override
+    public void onRelease() {
+        if (mIsScrolling) {
+            mDistance = 0;
+            mOverlay.onScrollInteractionEnd();
+            mIsScrolling = false;
+        }
+    }
+
+    @Override
+    public boolean draw(Canvas canvas) {
+        return false;
+    }
+
+    public void finish() {
+        mDistance = 0;
+    }
+}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 86f3431..08ec591 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -37,7 +37,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PatternMatcher;
-import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
@@ -49,8 +48,8 @@
 import com.android.launcher3.Utilities;
 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.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 
 import java.net.URISyntaxException;
@@ -93,44 +92,28 @@
     }
 
     /**
+     * Returns whether the target app is installed for a given user
+     */
+    public boolean isAppInstalled(String packageName, UserHandle user) {
+        ApplicationInfo info = getApplicationInfo(packageName, user, 0);
+        return info != null;
+    }
+
+    /**
      * Returns the application info for the provided package or null
      */
     public ApplicationInfo getApplicationInfo(String packageName, UserHandle user, int flags) {
-        if (Utilities.ATLEAST_OREO) {
-            try {
-                ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
-                return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
-                        ? null : info;
-            } catch (PackageManager.NameNotFoundException e) {
-                return null;
-            }
-        } else {
-            final boolean isPrimaryUser = Process.myUserHandle().equals(user);
-            if (!isPrimaryUser && (flags == 0)) {
-                // We are looking for an installed app on a secondary profile. Prior to O, the only
-                // entry point for work profiles is through the LauncherActivity.
-                List<LauncherActivityInfo> activityList =
-                        mLauncherApps.getActivityList(packageName, user);
-                return activityList.size() > 0 ? activityList.get(0).getApplicationInfo() : null;
-            }
-            try {
-                ApplicationInfo info = mPm.getApplicationInfo(packageName, flags);
-                // There is no way to check if the app is installed for managed profile. But for
-                // primary profile, we can still have this check.
-                if (isPrimaryUser && ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0)
-                        || !info.enabled) {
-                    return null;
-                }
-                return info;
-            } catch (PackageManager.NameNotFoundException e) {
-                // Package not found
-                return null;
-            }
+        try {
+            ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
+            return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
+                    ? null : info;
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
         }
     }
 
     public boolean isSafeMode() {
-        return mContext.getPackageManager().isSafeMode();
+        return mPm.isSafeMode();
     }
 
     public Intent getAppLaunchIntent(String pkg, UserHandle user) {
@@ -226,9 +209,12 @@
      * Starts the details activity for {@code info}
      */
     public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
-        if (info instanceof PromiseAppInfo) {
-            PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
-            mContext.startActivity(promiseAppInfo.getMarketIntent(mContext));
+        if (info instanceof ItemInfoWithIcon
+                && (((ItemInfoWithIcon) info).runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+            ItemInfoWithIcon appInfo = (ItemInfoWithIcon) info;
+            mContext.startActivity(new PackageManagerHelper(mContext)
+                    .getMarketIntent(appInfo.getTargetComponent().getPackageName()));
             return;
         }
         ComponentName componentName = null;
@@ -345,4 +331,12 @@
         }
         return false;
     }
+
+    /** Returns the incremental download progress for the given shortcut's app. */
+    public static int getLoadingProgress(LauncherActivityInfo info) {
+        if (Utilities.ATLEAST_S) {
+            return (int) (100 * info.getLoadingProgress());
+        }
+        return 100;
+    }
 }
diff --git a/src/com/android/launcher3/util/PendingRequestArgs.java b/src/com/android/launcher3/util/PendingRequestArgs.java
index 9b8c6a6..77c8c0c 100644
--- a/src/com/android/launcher3/util/PendingRequestArgs.java
+++ b/src/com/android/launcher3/util/PendingRequestArgs.java
@@ -20,7 +20,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 
 /**
@@ -34,11 +36,13 @@
     private static final int TYPE_APP_WIDGET = 2;
 
     private final int mArg1;
+    private final int mArg2;
     private final int mObjectType;
     private final Parcelable mObject;
 
     public PendingRequestArgs(ItemInfo info) {
         mArg1 = 0;
+        mArg2 = 0;
         mObjectType = TYPE_NONE;
         mObject = null;
 
@@ -46,7 +50,12 @@
     }
 
     private PendingRequestArgs(int arg1, int objectType, Parcelable object) {
+        this(arg1, 0, objectType, object);
+    }
+
+    private PendingRequestArgs(int arg1, int arg2, int objectType, Parcelable object) {
         mArg1 = arg1;
+        mArg2 = arg2;
         mObjectType = objectType;
         mObject = object;
     }
@@ -56,6 +65,7 @@
         user = parcel.readParcelable(null);
 
         mArg1 = parcel.readInt();
+        mArg2 = parcel.readInt();
         mObjectType = parcel.readInt();
         mObject = parcel.readParcelable(getClass().getClassLoader());
     }
@@ -73,6 +83,7 @@
         dest.writeParcelable(user, flags);
 
         dest.writeInt(mArg1);
+        dest.writeInt(mArg2);
         dest.writeInt(mObjectType);
         dest.writeParcelable(mObject, flags);
     }
@@ -85,6 +96,10 @@
         return mObjectType == TYPE_APP_WIDGET ? mArg1 : 0;
     }
 
+    public int getWidgetSourceContainer() {
+        return mObjectType == TYPE_APP_WIDGET ? mArg2 : Favorites.CONTAINER_UNKNOWN;
+    }
+
     public Intent getPendingIntent() {
         return mObjectType == TYPE_INTENT ? (Intent) mObject : null;
     }
@@ -95,8 +110,13 @@
 
     public static PendingRequestArgs forWidgetInfo(
             int appWidgetId, WidgetAddFlowHandler widgetHandler, ItemInfo info) {
+        int sourceContainer = Favorites.CONTAINER_UNKNOWN;
+        if (info instanceof PendingAddWidgetInfo) {
+            sourceContainer = ((PendingAddWidgetInfo) info).sourceContainer;
+        }
         PendingRequestArgs args =
-                new PendingRequestArgs(appWidgetId, TYPE_APP_WIDGET, widgetHandler);
+                new PendingRequestArgs(
+                        appWidgetId, sourceContainer, TYPE_APP_WIDGET, widgetHandler);
         args.copyFrom(info);
         return args;
     }
diff --git a/src/com/android/launcher3/util/PersistedItemArray.java b/src/com/android/launcher3/util/PersistedItemArray.java
new file mode 100644
index 0000000..7ff2abb
--- /dev/null
+++ b/src/com/android/launcher3/util/PersistedItemArray.java
@@ -0,0 +1,184 @@
+/*
+ * 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 android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Xml;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.AutoInstallsLayout;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.pm.UserCache;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.LongFunction;
+
+/**
+ * Utility class to read/write a list of {@link com.android.launcher3.model.data.ItemInfo} on disk.
+ * This class is not thread safe, the caller should ensure proper threading
+ */
+public class PersistedItemArray<T extends ItemInfo> {
+
+    private static final String TAG = "PersistedItemArray";
+
+    private static final String TAG_ROOT = "items";
+    private static final String TAG_ENTRY = "entry";
+
+    private final String mFileName;
+
+    public PersistedItemArray(String fileName) {
+        mFileName = fileName + ".xml";
+    }
+
+    /**
+     * Writes the provided list of items on the disk
+     */
+    @WorkerThread
+    public void write(Context context, List<T> items) {
+        AtomicFile file = getFile(context);
+        FileOutputStream fos;
+        try {
+            fos = file.startWrite();
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to persist items in " + mFileName, e);
+            return;
+        }
+
+        UserCache userCache = UserCache.INSTANCE.get(context);
+
+        try {
+            XmlSerializer out = Xml.newSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+            out.startTag(null, TAG_ROOT);
+            for (T item : items) {
+                Intent intent = item.getIntent();
+                if (intent == null) {
+                    continue;
+                }
+
+                out.startTag(null, TAG_ENTRY);
+                out.attribute(null, Favorites.ITEM_TYPE, Integer.toString(item.itemType));
+                out.attribute(null, Favorites.PROFILE_ID,
+                        Long.toString(userCache.getSerialNumberForUser(item.user)));
+                out.attribute(null, Favorites.INTENT, intent.toUri(0));
+                out.endTag(null, TAG_ENTRY);
+            }
+            out.endTag(null, TAG_ROOT);
+            out.endDocument();
+        } catch (IOException e) {
+            file.failWrite(fos);
+            Log.e(TAG, "Unable to persist items in " + mFileName, e);
+            return;
+        }
+
+        file.finishWrite(fos);
+    }
+
+    /**
+     * Reads the items from the disk
+     */
+    @WorkerThread
+    public List<T> read(Context context, ItemFactory<T> factory) {
+        return read(context, factory, UserCache.INSTANCE.get(context)::getUserForSerialNumber);
+    }
+
+    /**
+     * Reads the items from the disk
+     * @param userFn method to provide user handle for a given user serial
+     */
+    @WorkerThread
+    public List<T> read(Context context, ItemFactory<T> factory, LongFunction<UserHandle> userFn) {
+        List<T> result = new ArrayList<>();
+        try (FileInputStream fis = getFile(context).openRead()) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(new InputStreamReader(fis, StandardCharsets.UTF_8));
+
+            AutoInstallsLayout.beginDocument(parser, TAG_ROOT);
+            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_ENTRY.equals(parser.getName())) {
+                    continue;
+                }
+                try {
+                    int itemType = Integer.parseInt(
+                            parser.getAttributeValue(null, Favorites.ITEM_TYPE));
+                    UserHandle user = userFn.apply(Long.parseLong(
+                            parser.getAttributeValue(null, Favorites.PROFILE_ID)));
+                    Intent intent = Intent.parseUri(
+                            parser.getAttributeValue(null, Favorites.INTENT), 0);
+
+                    if (user != null && intent != null) {
+                        T item = factory.createInfo(itemType, user, intent);
+                        if (item != null) {
+                            result.add(item);
+                        }
+                    }
+                } catch (Exception e) {
+                    // Ignore this entry
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // Ignore
+        } catch (IOException | XmlPullParserException e) {
+            Log.e(TAG, "Unable to read items in " + mFileName, e);
+            return Collections.emptyList();
+        }
+        return result;
+    }
+
+    /**
+     * Returns the underlying file used for persisting data
+     */
+    public AtomicFile getFile(Context context) {
+        return new AtomicFile(context.getFileStreamPath(mFileName));
+    }
+
+    /**
+     * Interface to create an ItemInfo during parsing
+     */
+    public interface ItemFactory<T extends ItemInfo> {
+
+        /**
+         * Returns an item info or null in which case the entry is ignored
+         */
+        @Nullable
+        T createInfo(int itemType, UserHandle user, Intent intent);
+    }
+}
diff --git a/src/com/android/launcher3/util/RunnableList.java b/src/com/android/launcher3/util/RunnableList.java
new file mode 100644
index 0000000..644537b
--- /dev/null
+++ b/src/com/android/launcher3/util/RunnableList.java
@@ -0,0 +1,67 @@
+/*
+ * 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.util.ArrayList;
+
+/**
+ * Utility class to hold a list of runnable
+ */
+public class RunnableList {
+
+    private ArrayList<Runnable> mList = null;
+    private boolean mDestroyed = false;
+
+    /**
+     * Ads a runnable to this list
+     */
+    public void add(Runnable runnable) {
+        if (runnable == null) {
+            return;
+        }
+        if (mDestroyed) {
+            runnable.run();
+            return;
+        }
+        if (mList == null) {
+            mList = new ArrayList<>();
+        }
+        mList.add(runnable);
+    }
+
+    /**
+     * Destroys the list, executing any pending callbacks. All new callbacks are
+     * immediately executed
+     */
+    public void executeAllAndDestroy() {
+        mDestroyed = true;
+        executeAllAndClear();
+    }
+
+    /**
+     * Executes all previously added runnable and clears the list
+     */
+    public void executeAllAndClear() {
+        if (mList != null) {
+            ArrayList<Runnable> list = mList;
+            mList = null;
+            int count = list.size();
+            for (int i = 0; i < count; i++) {
+                list.get(i).run();
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/SecureSettingsObserver.java b/src/com/android/launcher3/util/SecureSettingsObserver.java
deleted file mode 100644
index 48aa02b..0000000
--- a/src/com/android/launcher3/util/SecureSettingsObserver.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.Settings;
-
-/**
- * Utility class to listen for secure settings changes
- */
-public class SecureSettingsObserver extends ContentObserver {
-
-    /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
-    public static final String NOTIFICATION_BADGING = "notification_badging";
-
-    private final ContentResolver mResolver;
-    private final String mKeySetting;
-    private final int mDefaultValue;
-    private final OnChangeListener mOnChangeListener;
-
-    public SecureSettingsObserver(ContentResolver resolver, OnChangeListener listener,
-            String keySetting, int defaultValue) {
-        super(new Handler());
-
-        mResolver = resolver;
-        mOnChangeListener = listener;
-        mKeySetting = keySetting;
-        mDefaultValue = defaultValue;
-    }
-
-    @Override
-    public void onChange(boolean selfChange) {
-        mOnChangeListener.onSettingsChanged(getValue());
-    }
-
-    public boolean getValue() {
-        return Settings.Secure.getInt(mResolver, mKeySetting, mDefaultValue) == 1;
-    }
-
-    public void register() {
-        mResolver.registerContentObserver(Settings.Secure.getUriFor(mKeySetting), false, this);
-    }
-
-    public ContentResolver getResolver() {
-        return mResolver;
-    }
-
-    public void dispatchOnChange() {
-        onChange(true);
-    }
-
-    public void unregister() {
-        mResolver.unregisterContentObserver(this);
-    }
-
-    public interface OnChangeListener {
-        void onSettingsChanged(boolean isEnabled);
-    }
-
-    public static SecureSettingsObserver newNotificationSettingsObserver(Context context,
-            OnChangeListener listener) {
-        return new SecureSettingsObserver(
-                context.getContentResolver(), listener, NOTIFICATION_BADGING, 1);
-    }
-}
diff --git a/src/com/android/launcher3/util/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java
new file mode 100644
index 0000000..10611c7
--- /dev/null
+++ b/src/com/android/launcher3/util/SettingsCache.java
@@ -0,0 +1,175 @@
+/*
+ * 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 android.provider.Settings.System.ACCELEROMETER_ROTATION;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * ContentObserver over Settings keys that also has a caching layer.
+ * Consumers can register for callbacks via {@link #register(Uri, OnChangeListener)} and
+ * {@link #unregister(Uri, OnChangeListener)} methods.
+ *
+ * This can be used as a normal cache without any listeners as well via the
+ * {@link #getValue(Uri, int)} and {@link #onChange)} to update (and subsequently call
+ * get)
+ *
+ * The cache will be invalidated/updated through the normal
+ * {@link ContentObserver#onChange(boolean)} calls
+ *
+ * Cache will also be updated if a key queried is missing (even if it has no listeners registered).
+ */
+public class SettingsCache extends ContentObserver {
+
+    /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
+    public static final Uri NOTIFICATION_BADGING_URI =
+            Settings.Secure.getUriFor("notification_badging");
+    /** Hidden field Settings.Secure.ONE_HANDED_MODE_ENABLED */
+    public static final String ONE_HANDED_ENABLED = "one_handed_mode_enabled";
+    /** Hidden field Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED */
+    public static final String ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
+            "swipe_bottom_to_notification_enabled";
+    public static final Uri ROTATION_SETTING_URI =
+            Settings.System.getUriFor(ACCELEROMETER_ROTATION);
+
+    private static final String SYSTEM_URI_PREFIX = Settings.System.CONTENT_URI.toString();
+
+    /**
+     * Caches the last seen value for registered keys.
+     */
+    private Map<Uri, Boolean> mKeyCache = new ConcurrentHashMap<>();
+    private final Map<Uri, CopyOnWriteArrayList<OnChangeListener>> mListenerMap = new HashMap<>();
+    protected final ContentResolver mResolver;
+
+
+    /**
+     * Singleton instance
+     */
+    public static MainThreadInitializedObject<SettingsCache> INSTANCE =
+            new MainThreadInitializedObject<>(SettingsCache::new);
+
+    private SettingsCache(Context context) {
+        super(new Handler());
+        mResolver = context.getContentResolver();
+    }
+
+    @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
+        boolean newVal = updateValue(uri, 1 /* Effectively Unused */);
+        if (!mListenerMap.containsKey(uri)) {
+            return;
+        }
+
+        for (OnChangeListener listener : mListenerMap.get(uri)) {
+            listener.onSettingsChanged(newVal);
+        }
+    }
+
+    /**
+     * Returns the value for this classes key from the cache. If not in cache, will call
+     * {@link #updateValue(Uri, int)} to fetch.
+     */
+    public boolean getValue(Uri keySetting) {
+        return getValue(keySetting, 1);
+    }
+
+    /**
+     * Returns the value for this classes key from the cache. If not in cache, will call
+     * {@link #updateValue(Uri, int)} to fetch.
+     */
+    public boolean getValue(Uri keySetting, int defaultValue) {
+        if (mKeyCache.containsKey(keySetting)) {
+            return mKeyCache.get(keySetting);
+        } else {
+            return updateValue(keySetting, defaultValue);
+        }
+    }
+
+    /**
+     * Does not de-dupe if you add same listeners for the same key multiple times.
+     * Unregister once complete using {@link #unregister(Uri, OnChangeListener)}
+     */
+    public void register(Uri uri, OnChangeListener changeListener) {
+        if (mListenerMap.containsKey(uri)) {
+            mListenerMap.get(uri).add(changeListener);
+        } else {
+            CopyOnWriteArrayList<OnChangeListener> l = new CopyOnWriteArrayList<>();
+            l.add(changeListener);
+            mListenerMap.put(uri, l);
+            mResolver.registerContentObserver(uri, false, this);
+        }
+    }
+
+    private boolean updateValue(Uri keyUri, int defaultValue) {
+        String key = keyUri.getLastPathSegment();
+        boolean newVal;
+        if (keyUri.toString().startsWith(SYSTEM_URI_PREFIX)) {
+            newVal = Settings.System.getInt(mResolver, key, defaultValue) == 1;
+        } else { // SETTING_SECURE
+            newVal = Settings.Secure.getInt(mResolver, key, defaultValue) == 1;
+        }
+
+        mKeyCache.put(keyUri, newVal);
+        return newVal;
+    }
+
+    /**
+     * Call to stop receiving updates on the given {@param listener}.
+     * This Uri/Listener pair must correspond to the same pair called with for
+     * {@link #register(Uri, OnChangeListener)}
+     */
+    public void unregister(Uri uri, OnChangeListener listener) {
+        List<OnChangeListener> listenersToRemoveFrom = mListenerMap.get(uri);
+        if (!listenersToRemoveFrom.contains(listener)) {
+            return;
+        }
+
+        listenersToRemoveFrom.remove(listener);
+        if (listenersToRemoveFrom.isEmpty()) {
+            mListenerMap.remove(uri);
+        }
+    }
+
+    /**
+     * Don't use this. Ever.
+     * @param keyCache Cache to replace {@link #mKeyCache}
+     */
+    @VisibleForTesting
+    void setKeyCache(Map<Uri, Boolean> keyCache) {
+        mKeyCache = keyCache;
+    }
+
+    public interface OnChangeListener {
+        void onSettingsChanged(boolean isEnabled);
+    }
+}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
new file mode 100644
index 0000000..573c8bd
--- /dev/null
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -0,0 +1,85 @@
+/*
+ * 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.util;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+public final class SplitConfigurationOptions {
+
+    ///////////////////////////////////
+    // Taken from
+    // frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+    /**
+     * Stage position isn't specified normally meaning to use what ever it is currently set to.
+     */
+    public static final int STAGE_POSITION_UNDEFINED = -1;
+    /**
+     * Specifies that a stage is positioned at the top half of the screen if
+     * in portrait mode or at the left half of the screen if in landscape mode.
+     */
+    public static final int STAGE_POSITION_TOP_OR_LEFT = 0;
+
+    /**
+     * Specifies that a stage is positioned at the bottom half of the screen if
+     * in portrait mode or at the right half of the screen if in landscape mode.
+     */
+    public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
+
+    @Retention(SOURCE)
+    @IntDef({STAGE_POSITION_UNDEFINED, STAGE_POSITION_TOP_OR_LEFT, STAGE_POSITION_BOTTOM_OR_RIGHT})
+    public @interface StagePosition {}
+
+    /**
+     * Stage type isn't specified normally meaning to use what ever the default is.
+     * E.g. exit split-screen and launch the app in fullscreen.
+     */
+    public static final int STAGE_TYPE_UNDEFINED = -1;
+    /**
+     * The main stage type.
+     */
+    public static final int STAGE_TYPE_MAIN = 0;
+
+    /**
+     * The side stage type.
+     */
+    public static final int STAGE_TYPE_SIDE = 1;
+
+    @IntDef({STAGE_TYPE_UNDEFINED, STAGE_TYPE_MAIN, STAGE_TYPE_SIDE})
+    public @interface StageType {}
+    ///////////////////////////////////
+
+    public static class SplitPositionOption {
+        public final int mIconResId;
+        public final int mTextResId;
+        @StagePosition
+        public final int mStagePosition;
+
+        @StageType
+        public final int mStageType;
+
+        public SplitPositionOption(int iconResId, int textResId, int stagePosition, int stageType) {
+            mIconResId = iconResId;
+            mTextResId = textResId;
+            mStagePosition = stagePosition;
+            mStageType = stageType;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 275c024..630df7e 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -19,8 +19,6 @@
 import android.view.View;
 import android.view.Window;
 
-import com.android.launcher3.Utilities;
-
 import java.util.Arrays;
 
 /**
@@ -32,7 +30,8 @@
     public static final int UI_STATE_BASE_WINDOW = 0;
     public static final int UI_STATE_SCRIM_VIEW = 1;
     public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
-    public static final int UI_STATE_OVERVIEW = 3;
+    public static final int UI_STATE_FULLSCREEN_TASK = 3;
+    public static final int UI_STATE_ALLAPPS = 4;
 
     public static final int FLAG_LIGHT_NAV = 1 << 0;
     public static final int FLAG_DARK_NAV = 1 << 1;
@@ -40,7 +39,7 @@
     public static final int FLAG_DARK_STATUS = 1 << 3;
 
     private final Window mWindow;
-    private final int[] mStates = new int[4];
+    private final int[] mStates = new int[5];
 
     public SystemUiController(Window window) {
         mWindow = window;
@@ -77,12 +76,10 @@
     }
 
     private int getSysUiVisibilityFlags(int stateFlag, int currentVisibility) {
-        if (Utilities.ATLEAST_OREO) {
-            if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
-                currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-            } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
-                currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
-            }
+        if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
+            currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+        } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
+            currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
         }
 
         if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index b74686f..53a584d 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -16,8 +16,12 @@
 
 package com.android.launcher3.util;
 
+import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_TEXT;
+import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_THEME;
+
+import android.app.WallpaperColors;
+import android.app.WallpaperManager;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.ColorMatrix;
@@ -28,33 +32,50 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.GraphicsUtils;
 
 /**
  * Various utility methods associated with theming.
  */
+@SuppressWarnings("NewApi")
 public class Themes {
 
-    public static int getActivityThemeRes(Context context) {
-        WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.INSTANCE.get(context);
-        boolean darkTheme;
-        if (Utilities.ATLEAST_Q) {
-            Configuration configuration = context.getResources().getConfiguration();
-            int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
-            darkTheme = nightMode == Configuration.UI_MODE_NIGHT_YES;
-        } else {
-            darkTheme = wallpaperColorInfo.isDark();
-        }
+    public static final String KEY_THEMED_ICONS = "themed_icons";
 
-        if (darkTheme) {
-            return wallpaperColorInfo.supportsDarkText() ?
-                    R.style.AppTheme_Dark_DarkText : wallpaperColorInfo.isMainColorDark() ?
-                            R.style.AppTheme_Dark_DarkMainColor : R.style.AppTheme_Dark;
+    public static int getActivityThemeRes(Context context) {
+        final int colorHints;
+        if (Utilities.ATLEAST_P) {
+            WallpaperColors colors = context.getSystemService(WallpaperManager.class)
+                    .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
+            colorHints = colors == null ? 0 : colors.getColorHints();
         } else {
-            return wallpaperColorInfo.supportsDarkText() ?
-                    R.style.AppTheme_DarkText : wallpaperColorInfo.isMainColorDark() ?
-                            R.style.AppTheme_DarkMainColor : R.style.AppTheme;
+            colorHints = 0;
         }
+        return getActivityThemeRes(context, colorHints);
+    }
+
+    public static int getActivityThemeRes(Context context, int wallpaperColorHints) {
+        boolean supportsDarkText = Utilities.ATLEAST_S
+                && (wallpaperColorHints & HINT_SUPPORTS_DARK_TEXT) != 0;
+        boolean isMainColorDark = Utilities.ATLEAST_S
+                && (wallpaperColorHints & HINT_SUPPORTS_DARK_THEME) != 0;
+
+        if (Utilities.isDarkTheme(context)) {
+            return supportsDarkText ? R.style.AppTheme_Dark_DarkText
+                    : isMainColorDark ? R.style.AppTheme_Dark_DarkMainColor : R.style.AppTheme_Dark;
+        } else {
+            return supportsDarkText ? R.style.AppTheme_DarkText
+                    : isMainColorDark ? R.style.AppTheme_DarkMainColor : R.style.AppTheme;
+        }
+    }
+
+    /**
+     * Returns true if workspace icon theming is enabled
+     */
+    public static boolean isThemedIconEnabled(Context context) {
+        return FeatureFlags.ENABLE_THEMED_ICONS.get()
+                && Utilities.getPrefs(context).getBoolean(KEY_THEMED_ICONS, false);
     }
 
     public static String getDefaultBodyFont(Context context) {
@@ -81,11 +102,18 @@
         return getAttrColor(context, android.R.attr.colorAccent);
     }
 
+    /** Returns the background color attribute. */
+    public static int getColorBackground(Context context) {
+        return getAttrColor(context, android.R.attr.colorBackground);
+    }
+
+    /** Returns the floating background color attribute. */
+    public static int getColorBackgroundFloating(Context context) {
+        return getAttrColor(context, android.R.attr.colorBackgroundFloating);
+    }
+
     public static int getAttrColor(Context context, int attr) {
-        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
-        int colorAccent = ta.getColor(0, 0);
-        ta.recycle();
-        return colorAccent;
+        return GraphicsUtils.getAttrColor(context, attr);
     }
 
     public static boolean getAttrBoolean(Context context, int attr) {
@@ -110,23 +138,6 @@
     }
 
     /**
-     * Returns the alpha corresponding to the theme attribute {@param attr}, in the range [0, 255].
-     */
-    public static int getAlpha(Context context, int attr) {
-        return (int) (255 * getFloat(context, attr, 0) + 0.5f);
-    }
-
-    /**
-     * Returns the alpha corresponding to the theme attribute {@param attr}
-     */
-    public static float getFloat(Context context, int attr, float defValue) {
-        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
-        float value = ta.getFloat(0, defValue);
-        ta.recycle();
-        return value;
-    }
-
-    /**
      * Scales a color matrix such that, when applied to color R G B A, it produces R' G' B' A' where
      * R' = r * R
      * G' = g * G
diff --git a/src/com/android/launcher3/util/TouchController.java b/src/com/android/launcher3/util/TouchController.java
index fc1d819..9c397c0 100644
--- a/src/com/android/launcher3/util/TouchController.java
+++ b/src/com/android/launcher3/util/TouchController.java
@@ -32,5 +32,10 @@
      */
     boolean onControllerInterceptTouchEvent(MotionEvent ev);
 
+    /**
+     * Called when one handed mode state changed
+     */
+    default void onOneHandedModeStateChanged(boolean activated) { }
+
     default void dump(String prefix, PrintWriter writer) { }
 }
diff --git a/src/com/android/launcher3/util/TranslateEdgeEffect.java b/src/com/android/launcher3/util/TranslateEdgeEffect.java
new file mode 100644
index 0000000..8fdc8df
--- /dev/null
+++ b/src/com/android/launcher3/util/TranslateEdgeEffect.java
@@ -0,0 +1,50 @@
+/*
+ * 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.graphics.Canvas;
+import android.graphics.RenderNode;
+import android.widget.EdgeEffect;
+
+/**
+ * Extension of {@link EdgeEffect} which translates the content instead of the default
+ * platform implementation
+ */
+@SuppressWarnings("NewApi")
+public class TranslateEdgeEffect extends EdgeEffectCompat {
+
+    private final RenderNode mNode;
+
+    public TranslateEdgeEffect(Context context) {
+        super(context);
+        mNode = new RenderNode("TranslateEdgeEffect");
+    }
+
+    @Override
+    public boolean draw(Canvas canvas) {
+        return false;
+    }
+
+    public boolean getTranslationShift(float[] out) {
+        Canvas c = mNode.beginRecording(1, 1);
+        boolean result = super.draw(c);
+        mNode.endRecording();
+
+        out[0] = getDistance();
+        return result;
+    }
+}
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index 0498052..0f40179 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -15,15 +15,22 @@
  */
 package com.android.launcher3.util;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 
+import com.android.launcher3.Launcher;
+import com.android.launcher3.views.ActivityContext;
+
 /**
  * Utility class for offloading some class from UI thread
  */
@@ -36,9 +43,30 @@
     private static final int MSG_HIDE_KEYBOARD = 1;
     private static final int MSG_SET_ORIENTATION = 2;
     private static final int MSG_RUN_COMMAND = 3;
+    private static final String STATS_LOGGER_KEY = "STATS_LOGGER_KEY";
 
-    public static void hideKeyboardAsync(Context context, IBinder token) {
-        Message.obtain(HANDLER.get(context), MSG_HIDE_KEYBOARD, token).sendToTarget();
+    @SuppressLint("NewApi")
+    public static void hideKeyboardAsync(ActivityContext activityContext, IBinder token) {
+        View root = activityContext.getDragLayer();
+
+        // Since the launcher context cannot be accessed directly from callback, adding secondary
+        // message to log keyboard close event asynchronously.
+        Bundle mHideKeyboardLoggerMsg = new Bundle();
+        mHideKeyboardLoggerMsg.putParcelable(
+                STATS_LOGGER_KEY,
+                Message.obtain(
+                        HANDLER.get(root.getContext()),
+                        () -> Launcher.cast(activityContext)
+                                .getStatsLogManager()
+                                .logger()
+                                .log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED)
+                )
+        );
+
+        Message mHideKeyboardMsg = Message.obtain(HANDLER.get(root.getContext()), MSG_HIDE_KEYBOARD,
+                token);
+        mHideKeyboardMsg.setData(mHideKeyboardLoggerMsg);
+        mHideKeyboardMsg.sendToTarget();
     }
 
     public static void setOrientationAsync(Activity activity, int orientation) {
@@ -69,7 +97,11 @@
         public boolean handleMessage(Message message) {
             switch (message.what) {
                 case MSG_HIDE_KEYBOARD:
-                    mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0);
+                    if (mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0)) {
+                        // log keyboard close event only when keyboard is actually closed
+                        ((Message) message.getData().getParcelable(STATS_LOGGER_KEY))
+                                .sendToTarget();
+                    }
                     return true;
                 case MSG_SET_ORIENTATION:
                     ((Activity) message.obj).setRequestedOrientation(message.arg1);
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/src/com/android/launcher3/util/VelocityUtils.java
similarity index 66%
copy from src/com/android/launcher3/util/SafeCloseable.java
copy to src/com/android/launcher3/util/VelocityUtils.java
index ba8ee04..d5962ed 100644
--- a/src/com/android/launcher3/util/SafeCloseable.java
+++ b/src/com/android/launcher3/util/VelocityUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -13,14 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.launcher3.util;
 
 /**
- * Extension of closeable which does not throw an exception
+ * Contains some constants and functions to standardize velocity usage.
  */
-public interface SafeCloseable extends AutoCloseable {
+public class VelocityUtils {
 
-    @Override
-    void close();
+    /**
+     * Unit to pass to {@link android.view.VelocityTracker#computeCurrentVelocity(int)}.
+     */
+    public static final int PX_PER_MS = 1;
+
 }
diff --git a/src/com/android/launcher3/util/ViewPool.java b/src/com/android/launcher3/util/ViewPool.java
index 5b33f18..e413d7f 100644
--- a/src/com/android/launcher3/util/ViewPool.java
+++ b/src/com/android/launcher3/util/ViewPool.java
@@ -58,7 +58,7 @@
         Preconditions.assertUIThread();
         Handler handler = new Handler();
 
-        // LayoutInflater is not thread save as it maintains a global variable 'mConstructorArgs'.
+        // LayoutInflater is not thread safe as it maintains a global variable 'mConstructorArgs'.
         // Create a different copy to use on the background thread.
         LayoutInflater inflater = mInflater.cloneInContext(mInflater.getContext());
 
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index 2ad80cf..b8554e4 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -145,14 +145,17 @@
         msg.sendToTarget();
     }
 
-    private void updateOffset() {
-        int numPagesForWallpaperParallax;
+    /** Returns the number of pages used for the wallpaper parallax. */
+    public int getNumPagesForWallpaperParallax() {
         if (mWallpaperIsLiveWallpaper) {
-            numPagesForWallpaperParallax = mNumScreens;
+            return mNumScreens;
         } else {
-            numPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens);
+            return Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens);
         }
-        Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, numPagesForWallpaperParallax, 0,
+    }
+
+    private void updateOffset() {
+        Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, getNumPagesForWallpaperParallax(), 0,
                 mWindowToken).sendToTarget();
     }
 
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
index 3c2fb62..c92770e 100644
--- a/src/com/android/launcher3/util/WindowBounds.java
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -15,11 +15,16 @@
  */
 package com.android.launcher3.util;
 
+import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.WindowInsets.Type;
+import android.view.WindowMetrics;
 
 import androidx.annotation.Nullable;
 
+import java.util.Objects;
+
 /**
  * Utility class to hold information about window position and layout
  */
@@ -36,6 +41,18 @@
                 bounds.height() - insets.top - insets.bottom);
     }
 
+    public WindowBounds(int width, int height, int availableWidth, int availableHeight) {
+        this.bounds = new Rect(0, 0, width, height);
+        this.availableSize = new Point(availableWidth, availableHeight);
+        // We don't care about insets in this case
+        this.insets = new Rect(0, 0, width - availableWidth, height - availableHeight);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(bounds, insets);
+    }
+
     @Override
     public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof WindowBounds)) {
@@ -44,4 +61,30 @@
         WindowBounds other = (WindowBounds) obj;
         return other.bounds.equals(bounds) && other.insets.equals(insets);
     }
+
+    @Override
+    public String toString() {
+        return "WindowBounds{"
+                + "bounds=" + bounds
+                + ", insets=" + insets
+                + ", availableSize=" + availableSize
+                + '}';
+    }
+
+    /**
+     * Returns true if the device is in landscape orientation
+     */
+    public final boolean isLandscape() {
+        return availableSize.x > availableSize.y;
+    }
+
+    /**
+     * Returns the bounds corresponding to the provided WindowMetrics
+     */
+    @SuppressWarnings("NewApi")
+    public static WindowBounds fromWindowMetrics(WindowMetrics wm) {
+        Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
+        return new WindowBounds(wm.getBounds(),
+                new Rect(insets.left, insets.top, insets.right, insets.bottom));
+    }
 }
diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java
new file mode 100644
index 0000000..38a63de
--- /dev/null
+++ b/src/com/android/launcher3/util/WindowManagerCompat.java
@@ -0,0 +1,107 @@
+/*
+ * 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.ResourceUtils.INVALID_RESOURCE_HANDLE;
+import static com.android.launcher3.Utilities.dpiFromPx;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.util.DisplayController.PortraitSize;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Utility class to simulate window manager APIs until proper APIs are available
+ */
+@TargetApi(Build.VERSION_CODES.S)
+public class WindowManagerCompat {
+
+    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
+     * @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)
+                .getMaximumWindowMetrics().getWindowInsets();
+        boolean hasNavbar = ResourceUtils.getIntegerByName(
+                "config_navBarInteractionMode",
+                windowContext.getResources(),
+                INVALID_RESOURCE_HANDLE) != 0;
+
+        WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(metrics);
+
+        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()));
+        }
+        return result;
+    }
+
+    private static int getSystemResource(Context context, String key, int swDp) {
+        int resourceId = context.getResources().getIdentifier(key, "dimen", "android");
+        if (resourceId > 0) {
+            Configuration conf = new Configuration();
+            conf.smallestScreenWidthDp = swDp;
+            return context.createConfigurationContext(conf)
+                    .getResources().getDimensionPixelSize(resourceId);
+        }
+        return 0;
+    }
+}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 11c1029..92ca8a1 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -31,17 +31,21 @@
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.touch.BaseSwipeDetector;
 import com.android.launcher3.touch.SingleAxisSwipeDetector;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
- * Extension of AbstractFloatingView with common methods for sliding in from bottom
+ * Extension of {@link AbstractFloatingView} with common methods for sliding in from bottom.
+ *
+ * @param <T> Type of ActivityContext inflating this view.
  */
-public abstract class AbstractSlideInView extends AbstractFloatingView
-        implements SingleAxisSwipeDetector.Listener {
+public abstract class AbstractSlideInView<T extends Context & ActivityContext>
+        extends AbstractFloatingView implements SingleAxisSwipeDetector.Listener {
 
     protected static final Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
             new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
@@ -59,22 +63,24 @@
     protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
     protected static final float TRANSLATION_SHIFT_OPENED = 0f;
 
-    protected final Launcher mLauncher;
+    protected final T mActivityContext;
+
     protected final SingleAxisSwipeDetector mSwipeDetector;
     protected final ObjectAnimator mOpenCloseAnimator;
 
     protected View mContent;
-    private final View mColorScrim;
+    protected final View mColorScrim;
     protected Interpolator mScrollInterpolator;
 
     // range [0, 1], 0=> completely open, 1=> completely closed
     protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
 
     protected boolean mNoIntercept;
+    protected List<OnCloseListener> mOnCloseListeners = new ArrayList<>();
 
     public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mLauncher = Launcher.getLauncher(context);
+        mActivityContext = ActivityContext.lookupContext(context);
 
         mScrollInterpolator = Interpolators.SCROLL_CUBIC;
         mSwipeDetector = new SingleAxisSwipeDetector(context, this,
@@ -88,8 +94,8 @@
                 announceAccessibilityChanges();
             }
         });
-        int scrimColor = getScrimColor(mLauncher);
-        mColorScrim = scrimColor != -1 ? createColorScrim(mLauncher, scrimColor) : null;
+        int scrimColor = getScrimColor(context);
+        mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
     }
 
     protected void attachToContainer() {
@@ -120,8 +126,8 @@
             return false;
         }
 
-        int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
-                SingleAxisSwipeDetector.DIRECTION_NEGATIVE : 0;
+        int directionsToDetectScroll = mSwipeDetector.isIdleState()
+                ? SingleAxisSwipeDetector.DIRECTION_NEGATIVE : 0;
         mSwipeDetector.setDetectableScrollConditions(
                 directionsToDetectScroll, false);
         mSwipeDetector.onTouchEvent(ev);
@@ -176,6 +182,11 @@
         }
     }
 
+    /** Registers an {@link OnCloseListener}. */
+    public void addOnCloseListener(OnCloseListener listener) {
+        mOnCloseListeners.add(listener);
+    }
+
     protected void handleClose(boolean animate, long defaultDuration) {
         if (!mIsOpen) {
             return;
@@ -210,14 +221,14 @@
         if (mColorScrim != null) {
             getPopupContainer().removeView(mColorScrim);
         }
+        mOnCloseListeners.forEach(OnCloseListener::onSlideInViewClosed);
     }
 
     protected BaseDragLayer getPopupContainer() {
-        return mLauncher.getDragLayer();
+        return mActivityContext.getDragLayer();
     }
 
-
-    protected static View createColorScrim(Context context, int bgColor) {
+    protected View createColorScrim(Context context, int bgColor) {
         View view = new View(context);
         view.forceHasOverlappingRendering(false);
         view.setBackgroundColor(bgColor);
@@ -228,4 +239,15 @@
 
         return view;
     }
+
+    /**
+     * Interface to report that the {@link AbstractSlideInView} has closed.
+     */
+    public interface OnCloseListener {
+
+        /**
+         * Called when {@link AbstractSlideInView} closes.
+         */
+        void onSlideInViewClosed();
+    }
 }
diff --git a/src/com/android/launcher3/views/AccessibilityActionsView.java b/src/com/android/launcher3/views/AccessibilityActionsView.java
new file mode 100644
index 0000000..1d136c3
--- /dev/null
+++ b/src/com/android/launcher3/views/AccessibilityActionsView.java
@@ -0,0 +1,97 @@
+/*
+ * 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.views;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.views.OptionsPopupView.OptionItem;
+
+/**
+ * Placeholder view to expose additional Launcher actions via accessibility actions
+ */
+public class AccessibilityActionsView extends View implements StateListener<LauncherState> {
+
+    public AccessibilityActionsView(Context context) {
+        this(context, null);
+    }
+
+    public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        Launcher.getLauncher(context).getStateManager().addStateListener(this);
+        setWillNotDraw(true);
+    }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        setImportantForAccessibility(finalState == NORMAL
+                ? IMPORTANT_FOR_ACCESSIBILITY_YES : IMPORTANT_FOR_ACCESSIBILITY_NO);
+    }
+
+    @Override
+    public AccessibilityNodeInfo createAccessibilityNodeInfo() {
+        AccessibilityNodeInfo info = super.createAccessibilityNodeInfo();
+        Launcher l = Launcher.getLauncher(getContext());
+        info.addAction(new AccessibilityAction(
+                R.string.all_apps_button_label, l.getText(R.string.all_apps_button_label)));
+        for (OptionItem item : OptionsPopupView.getOptions(l)) {
+            info.addAction(new AccessibilityAction(item.labelRes, item.label));
+        }
+        return info;
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
+        Launcher l = Launcher.getLauncher(getContext());
+        if (action == R.string.all_apps_button_label) {
+            l.getStateManager().goToState(ALL_APPS);
+            return true;
+        }
+        for (OptionItem item : OptionsPopupView.getOptions(l)) {
+            if (item.labelRes == action) {
+                if (item.eventId.getId() > 0) {
+                    l.getStatsLogManager().logger().log(item.eventId);
+                }
+                if (item.clickListener.onLongClick(this)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index ae459e1..646b669 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -17,12 +17,15 @@
 
 import android.content.Context;
 import android.content.ContextWrapper;
-import android.view.ContextThemeWrapper;
+import android.graphics.Rect;
+import android.view.LayoutInflater;
 import android.view.View.AccessibilityDelegate;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.ViewCache;
 
 /**
  * An interface to be used along with a context for various activities in Launcher. This allows a
@@ -49,6 +52,35 @@
         return null;
     }
 
+    default Rect getFolderBoundingBox() {
+        return getDeviceProfile().getAbsoluteOpenFolderBounds();
+    }
+
+    /**
+     * After calling {@link #getFolderBoundingBox()}, we calculate a (left, top) position for a
+     * Folder of size width x height to be within those bounds. However, the chosen position may
+     * not be visually ideal (e.g. uncanny valley of centeredness), so here's a chance to update it.
+     * @param inOutPosition A 2-size array where the first element is the left position of the open
+     *     folder and the second element is the top position. Should be updated in place if desired.
+     * @param bounds The bounds that the open folder should fit inside.
+     * @param width The width of the open folder.
+     * @param height The height of the open folder.
+     */
+    default void updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height) {
+    }
+
+    /**
+     * Returns a LayoutInflater that is cloned in this Context, so that Views inflated by it will
+     * have the same Context. (i.e. {@link #lookupContext(Context)} will find this ActivityContext.)
+     */
+    default LayoutInflater getLayoutInflater() {
+        if (this instanceof Context) {
+            Context context = (Context) this;
+            return LayoutInflater.from(context).cloneInContext(context);
+        }
+        return null;
+    }
+
     /**
      * The root view to support drag-and-drop and popup support.
      */
@@ -56,10 +88,24 @@
 
     DeviceProfile getDeviceProfile();
 
-    static ActivityContext lookupContext(Context context) {
+    default ViewCache getViewCache() {
+        return new ViewCache();
+    }
+
+    /**
+     * Controller for supporting item drag-and-drop
+     */
+    default <T extends DragController> T getDragController() {
+        return null;
+    }
+
+    /**
+     * Returns the ActivityContext associated with the given Context.
+     */
+    static <T extends Context & ActivityContext> T lookupContext(Context context) {
         if (context instanceof ActivityContext) {
-            return (ActivityContext) context;
-        } else if (context instanceof ContextThemeWrapper) {
+            return (T) context;
+        } else if (context instanceof ContextWrapper) {
             return lookupContext(((ContextWrapper) context).getBaseContext());
         } else {
             throw new IllegalArgumentException("Cannot find ActivityContext in parent tree");
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index b4a6b14..e449a4b 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -17,11 +17,13 @@
 package com.android.launcher3.views;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.CornerPathEffect;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.Handler;
-import android.util.TypedValue;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
@@ -29,6 +31,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.Px;
 import androidx.core.content.ContextCompat;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -43,6 +47,7 @@
  */
 public class ArrowTipView extends AbstractFloatingView {
 
+    private static final String TAG = ArrowTipView.class.getSimpleName();
     private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
     private static final long SHOW_DELAY_MS = 200;
     private static final long SHOW_DURATION_MS = 300;
@@ -50,11 +55,20 @@
 
     protected final BaseDraggingActivity mActivity;
     private final Handler mHandler = new Handler();
+    private final int mArrowWidth;
+    private boolean mIsPointingUp;
     private Runnable mOnClosed;
+    private View mArrowView;
 
     public ArrowTipView(Context context) {
+        this(context, false);
+    }
+
+    public ArrowTipView(Context context, boolean isPointingUp) {
         super(context, null, 0);
         mActivity = BaseDraggingActivity.fromContext(context);
+        mIsPointingUp = isPointingUp;
+        mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width);
         init(context);
     }
 
@@ -62,6 +76,9 @@
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             close(true);
+            if (mActivity.getDragLayer().isEventOverView(this, ev)) {
+                return true;
+            }
         }
         return false;
     }
@@ -87,10 +104,6 @@
     }
 
     @Override
-    public void logActionCommand(int command) {
-    }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_ON_BOARD_POPUP) != 0;
     }
@@ -98,27 +111,9 @@
     private void init(Context context) {
         inflate(context, R.layout.arrow_toast, this);
         setOrientation(LinearLayout.VERTICAL);
-        View dismissButton = findViewById(R.id.dismiss);
-        dismissButton.setOnClickListener(view -> {
-            handleClose(true);
-        });
 
-        View arrowView = findViewById(R.id.arrow);
-        ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
-        ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
-                arrowLp.width, arrowLp.height, false));
-        Paint arrowPaint = arrowDrawable.getPaint();
-        TypedValue typedValue = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
-        arrowPaint.setColor(ContextCompat.getColor(getContext(), typedValue.resourceId));
-        // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
-        arrowPaint.setPathEffect(new CornerPathEffect(
-                context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
-        arrowView.setBackground(arrowDrawable);
-
-        mIsOpen = true;
-
-        mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
+        mArrowView = findViewById(R.id.arrow);
+        updateArrowTipInView();
     }
 
     /**
@@ -134,7 +129,7 @@
      * @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) {
@@ -144,19 +139,25 @@
 
         DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
         params.gravity = gravity;
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) findViewById(
-                R.id.arrow).getLayoutParams();
+        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);
+            lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart - mArrowWidth);
         } else if (gravity == Gravity.START) {
-            lp.setMarginStart(arrowMarginStart);
+            lp.setMarginStart(arrowMarginStart - mArrowWidth / 2);
         }
         requestLayout();
 
         params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
         params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
-        post(() -> setY(top - getHeight()));
+        post(() -> setY(top - (mIsPointingUp ? 0 : getHeight())));
+
+        mIsOpen = true;
+        mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
         setAlpha(0);
         animate()
                 .alpha(1f)
@@ -169,10 +170,159 @@
     }
 
     /**
+     * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it
+     * cannot fit on screen in the requested orientation.
+     *
+     * @param text The text to be shown in the tooltip.
+     * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the
+     *                    center of tooltip unless the tooltip goes beyond screen margin.
+     * @param yCoord The Y coordinate of the pointed tip end of the tooltip.
+     * @return The tool tip view. {@code null} if the tip can not be shown.
+     */
+    @Nullable public ArrowTipView showAtLocation(String text, @Px int arrowXCoord, @Px int yCoord) {
+        return showAtLocation(
+                text,
+                arrowXCoord,
+                /* yCoordDownPointingTip= */ yCoord,
+                /* yCoordUpPointingTip= */ yCoord);
+    }
+
+    /**
+     * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it
+     * cannot fit on screen in the requested orientation.
+     *
+     * @param text The text to be shown in the tooltip.
+     * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the
+     *                    center of tooltip unless the tooltip goes beyond screen margin.
+     * @param rect The coordinates of the view which requests the tooltip to be shown.
+     * @param margin The margin between {@param rect} and the tooltip.
+     * @return The tool tip view. {@code null} if the tip can not be shown.
+     */
+    @Nullable public ArrowTipView showAroundRect(
+            String text, @Px int arrowXCoord, Rect rect, @Px int margin) {
+        return showAtLocation(
+                text,
+                arrowXCoord,
+                /* yCoordDownPointingTip= */ rect.top - margin,
+                /* yCoordUpPointingTip= */ rect.bottom + margin);
+    }
+
+    /**
+     * Show the ArrowTipView (tooltip) custom aligned. The tooltip is vertically flipped if it
+     * cannot fit on screen in the requested orientation.
+     *
+     * @param text The text to be shown in the tooltip.
+     * @param arrowXCoord The X coordinate for the arrow on the tooltip. The arrow is usually in the
+     *                    center of tooltip unless the tooltip goes beyond screen margin.
+     * @param yCoordDownPointingTip The Y coordinate of the pointed tip end of the tooltip when the
+     *                              tooltip is placed pointing downwards.
+     * @param yCoordUpPointingTip The Y coordinate of the pointed tip end of the tooltip when the
+     *                            tooltip is placed pointing upwards.
+     * @return The tool tip view. {@code null} if the tip can not be shown.
+     */
+    @Nullable private ArrowTipView showAtLocation(String text, @Px int arrowXCoord,
+            @Px int yCoordDownPointingTip, @Px int yCoordUpPointingTip) {
+        ViewGroup parent = mActivity.getDragLayer();
+        @Px int parentViewWidth = parent.getWidth();
+        @Px int parentViewHeight = parent.getHeight();
+        @Px int maxTextViewWidth = getContext().getResources()
+                .getDimensionPixelSize(R.dimen.widget_picker_education_tip_max_width);
+        @Px int minViewMargin = getContext().getResources()
+                .getDimensionPixelSize(R.dimen.widget_picker_education_tip_min_margin);
+        if (parentViewWidth < maxTextViewWidth + 2 * minViewMargin) {
+            Log.w(TAG, "Cannot display tip on a small screen of size: " + parentViewWidth);
+            return null;
+        }
+
+        TextView textView = findViewById(R.id.text);
+        textView.setText(text);
+        textView.setMaxWidth(maxTextViewWidth);
+        parent.addView(this);
+        requestLayout();
+
+        post(() -> {
+            // Adjust the tooltip horizontally.
+            float halfWidth = getWidth() / 2f;
+            float xCoord;
+            if (arrowXCoord - halfWidth < minViewMargin) {
+                // If the tooltip is estimated to go beyond the left margin, place its start just at
+                // the left margin.
+                xCoord = minViewMargin;
+            } else if (arrowXCoord + halfWidth > parentViewWidth - minViewMargin) {
+                // If the tooltip is estimated to go beyond the right margin, place it such that its
+                // end is just at the right margin.
+                xCoord = parentViewWidth - minViewMargin - getWidth();
+            } else {
+                // Place the tooltip such that its center is at arrowXCoord.
+                xCoord = arrowXCoord - halfWidth;
+            }
+            setX(xCoord);
+
+            // Adjust the tooltip vertically.
+            @Px int viewHeight = getHeight();
+            if (mIsPointingUp
+                    ? (yCoordUpPointingTip + viewHeight > parentViewHeight)
+                    : (yCoordDownPointingTip - viewHeight < 0)) {
+                // Flip the view if it exceeds the vertical bounds of screen.
+                mIsPointingUp = !mIsPointingUp;
+                updateArrowTipInView();
+            }
+            // Place the tooltip such that its top is at yCoordUpPointingTip if arrow is displayed
+            // pointing upwards, otherwise place it such that its bottom is at
+            // yCoordDownPointingTip.
+            setY(mIsPointingUp ? yCoordUpPointingTip : yCoordDownPointingTip - viewHeight);
+
+            // Adjust the arrow's relative position on tooltip to make sure the actual position of
+            // arrow's pointed tip is always at arrowXCoord.
+            mArrowView.setX(arrowXCoord - xCoord - mArrowView.getWidth() / 2f);
+            requestLayout();
+        });
+
+        mIsOpen = true;
+        mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
+        setAlpha(0);
+        animate()
+                .alpha(1f)
+                .withLayer()
+                .setStartDelay(SHOW_DELAY_MS)
+                .setDuration(SHOW_DURATION_MS)
+                .setInterpolator(Interpolators.DEACCEL)
+                .start();
+        return this;
+    }
+
+    private void updateArrowTipInView() {
+        ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams();
+        ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+                arrowLp.width, arrowLp.height, mIsPointingUp));
+        Paint arrowPaint = arrowDrawable.getPaint();
+        @Px int arrowTipRadius = getContext().getResources()
+                .getDimensionPixelSize(R.dimen.arrow_toast_corner_radius);
+        arrowPaint.setColor(ContextCompat.getColor(getContext(), R.color.arrow_tip_view_bg));
+        arrowPaint.setPathEffect(new CornerPathEffect(arrowTipRadius));
+        mArrowView.setBackground(arrowDrawable);
+        // Add negative margin so that the rounded corners on base of arrow are not visible.
+        removeView(mArrowView);
+        if (mIsPointingUp) {
+            addView(mArrowView, 0);
+            ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, 0, 0, -1 * arrowTipRadius);
+        } else {
+            addView(mArrowView, 1);
+            ((ViewGroup.MarginLayoutParams) arrowLp).setMargins(0, -1 * arrowTipRadius, 0, 0);
+        }
+    }
+
+    /**
      * Register a callback fired when toast is hidden
      */
     public ArrowTipView setOnClosedCallback(Runnable runnable) {
         mOnClosed = runnable;
         return this;
     }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        close(/* animate= */ false);
+    }
 }
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 357eeb8..01c0b56 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -20,20 +20,16 @@
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_UP;
 
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 
 import android.annotation.TargetApi;
-import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Property;
 import android.view.MotionEvent;
 import android.view.View;
@@ -43,16 +39,11 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.launcher3.util.SimpleBroadcastReceiver;
 import com.android.launcher3.util.TouchController;
 
 import java.io.PrintWriter;
@@ -119,9 +110,6 @@
     protected final T mActivity;
     private final MultiValueAlpha mMultiValueAlpha;
     private final WallpaperManager mWallpaperManager;
-    private final SimpleBroadcastReceiver mWallpaperChangeReceiver =
-            new SimpleBroadcastReceiver(this::onWallpaperChanged);
-    private final String[] mWallpapersWithoutSysuiScrims;
 
     // All the touch controllers for the view
     protected TouchController[] mControllers;
@@ -132,15 +120,11 @@
 
     private TouchCompleteListener mTouchCompleteListener;
 
-    protected boolean mAllowSysuiScrims = true;
-
     public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
         super(context, attrs);
         mActivity = (T) ActivityContext.lookupContext(context);
         mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
         mWallpaperManager = context.getSystemService(WallpaperManager.class);
-        mWallpapersWithoutSysuiScrims = getResources().getStringArray(
-                R.array.live_wallpapers_remove_sysui_scrims);
     }
 
     /**
@@ -191,12 +175,6 @@
     }
 
     private TouchController findControllerToHandleTouch(MotionEvent ev) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "findControllerToHandleTouch ev=" + ev
-                    + ", isEventInLauncher=" + isEventInLauncher(ev)
-                    + ", topOpenView=" + AbstractFloatingView.getTopOpenView(mActivity));
-        }
-
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
         if (topView != null
                 && (isEventInLauncher(ev) || topView.canInterceptEventsInSystemGestureRegion())
@@ -214,15 +192,19 @@
 
     protected boolean findActiveController(MotionEvent ev) {
         mActiveController = null;
-        if ((mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION
-                | TOUCH_DISPATCHING_FROM_PROXY)) == 0) {
-            // Only look for controllers if we are not dispatching from gesture area and proxy is
-            // not active
+        if (canFindActiveController()) {
             mActiveController = findControllerToHandleTouch(ev);
         }
         return mActiveController != null;
     }
 
+    protected boolean canFindActiveController() {
+        // Only look for controllers if we are not dispatching from gesture area and proxy is
+        // not active
+        return (mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION
+                | TOUCH_DISPATCHING_FROM_PROXY)) == 0;
+    }
+
     @Override
     public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
         // Shortcuts can appear above folder
@@ -463,12 +445,6 @@
     }
 
     @Override
-    public boolean dispatchUnhandledMove(View focused, int direction) {
-        // Consume the unhandled move if a container is open, to avoid switching pages underneath.
-        return AbstractFloatingView.getTopOpenView(mActivity) != null;
-    }
-
-    @Override
     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
         View topView = AbstractFloatingView.getTopOpenView(mActivity);
         if (topView != null) {
@@ -572,46 +548,4 @@
         }
         return super.dispatchApplyWindowInsets(insets);
     }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mWallpaperChangeReceiver.register(mActivity, Intent.ACTION_WALLPAPER_CHANGED);
-        onWallpaperChanged(null);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mActivity.unregisterReceiver(mWallpaperChangeReceiver);
-    }
-
-    private void onWallpaperChanged(Intent unusedBroadcastIntent) {
-        WallpaperInfo newWallpaperInfo = mWallpaperManager.getWallpaperInfo();
-        boolean oldAllowSysuiScrims = mAllowSysuiScrims;
-        mAllowSysuiScrims = computeAllowSysuiScrims(newWallpaperInfo);
-        if (mAllowSysuiScrims != oldAllowSysuiScrims) {
-            // Reapply insets so scrim can be removed or re-added if necessary.
-            setInsets(mInsets);
-        }
-    }
-
-    /**
-     * Determines whether we can scrim the status bar and nav bar for the given wallpaper by
-     * checking against a list of live wallpapers that we don't show the scrims on.
-     */
-    private boolean computeAllowSysuiScrims(@Nullable WallpaperInfo newWallpaperInfo) {
-        if (newWallpaperInfo == null) {
-            // Static wallpapers need scrim unless determined otherwise by wallpaperColors.
-            return true;
-        }
-        ComponentName newWallpaper = newWallpaperInfo.getComponent();
-        for (String wallpaperWithoutScrim : mWallpapersWithoutSysuiScrims) {
-            if (newWallpaper.equals(ComponentName.unflattenFromString(wallpaperWithoutScrim))) {
-                // New wallpaper does not need a scrim.
-                return false;
-            }
-        }
-        return true;
-    }
 }
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/src/com/android/launcher3/views/BubbleTextHolder.java
similarity index 66%
copy from src/com/android/launcher3/util/SafeCloseable.java
copy to src/com/android/launcher3/views/BubbleTextHolder.java
index ba8ee04..47d3563 100644
--- a/src/com/android/launcher3/util/SafeCloseable.java
+++ b/src/com/android/launcher3/views/BubbleTextHolder.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,14 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.launcher3.views;
 
-package com.android.launcher3.util;
+import com.android.launcher3.BubbleTextView;
 
 /**
- * Extension of closeable which does not throw an exception
+ * Views that contain {@link BubbleTextView} should implement this interface.
  */
-public interface SafeCloseable extends AutoCloseable {
-
-    @Override
-    void close();
+public interface BubbleTextHolder {
+    BubbleTextView getBubbleText();
 }
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index fab0bd4..a66b3f9 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -15,10 +15,13 @@
  */
 package com.android.launcher3.views;
 
+import static com.android.launcher3.Utilities.boundToRange;
 import static com.android.launcher3.Utilities.mapToRange;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
 
+import static java.lang.Math.max;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -143,10 +146,9 @@
     /**
      * Update the icon UI to match the provided parameters during an animation frame
      */
-    public void update(RectF rect, float progress, float shapeProgressStart,
-            float cornerRadius, boolean isOpening, View container,
-            DeviceProfile dp, boolean isVerticalBarLayout) {
-
+    public void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
+            int fgIconAlpha, boolean isOpening, View container, DeviceProfile dp,
+            boolean isVerticalBarLayout) {
         MarginLayoutParams lp = (MarginLayoutParams) container.getLayoutParams();
 
         float dX = mIsRtl
@@ -161,7 +163,12 @@
         float scaleY = rect.height() / minSize;
         float scale = Math.max(1f, Math.min(scaleX, scaleY));
 
-        update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
+        if (Float.isNaN(scale)) {
+            // Views are no longer laid out, do not update.
+            return;
+        }
+
+        update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha, isOpening, scale,
                 minSize, lp, isVerticalBarLayout, dp);
 
         container.setPivotX(0);
@@ -173,8 +180,8 @@
     }
 
     private void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
-            boolean isOpening, float scale, float minSize, MarginLayoutParams parentLp,
-            boolean isVerticalBarLayout, DeviceProfile dp) {
+            int fgIconAlpha, boolean isOpening, float scale, float minSize,
+            MarginLayoutParams parentLp, boolean isVerticalBarLayout, DeviceProfile dp) {
         float dX = mIsRtl
                 ? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
                 : rect.left - parentLp.getMarginStart();
@@ -182,9 +189,9 @@
 
         // shapeRevealProgress = 1 when progress = shapeProgressStart + SHAPE_PROGRESS_DURATION
         float toMax = isOpening ? 1 / SHAPE_PROGRESS_DURATION : 1f;
-        float shapeRevealProgress = Utilities.boundToRange(mapToRange(
-                Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax,
-                LINEAR), 0, 1);
+
+        float shapeRevealProgress = boundToRange(mapToRange(max(shapeProgressStart, progress),
+                shapeProgressStart, 1f, 0, toMax, LINEAR), 0, 1);
 
         if (isVerticalBarLayout) {
             mOutline.right = (int) (rect.width() / scale);
@@ -226,6 +233,8 @@
                 sTmpRect.offset(diffX, diffY);
                 mForeground.setBounds(sTmpRect);
             } else {
+                mForeground.setAlpha(fgIconAlpha);
+
                 // Spring the foreground relative to the icon's movement within the DragLayer.
                 int diffX = (int) (dX / dp.availableWidthPx * FG_TRANS_X_FACTOR);
                 int diffY = (int) (dY / dp.availableHeightPx * FG_TRANS_Y_FACTOR);
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
index d89e7f8..a309e6e 100644
--- a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -60,7 +60,7 @@
 
         // We enhance the shadow by drawing the shadow twice
         getPaint().setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0,
-                setColorAlphaBound(mShadowInfo.ambientShadowColor, alpha));
+                getTextShadowColor(mShadowInfo.ambientShadowColor, alpha));
 
         drawWithoutDot(canvas);
         canvas.save();
@@ -68,8 +68,11 @@
                 getScrollX() + getWidth(),
                 getScrollY() + getHeight());
 
-        getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f, mShadowInfo.keyShadowOffset,
-                setColorAlphaBound(mShadowInfo.keyShadowColor, alpha));
+        getPaint().setShadowLayer(
+                mShadowInfo.keyShadowBlur,
+                mShadowInfo.keyShadowOffsetX,
+                mShadowInfo.keyShadowOffsetY,
+                getTextShadowColor(mShadowInfo.keyShadowColor, alpha));
         drawWithoutDot(canvas);
         canvas.restore();
 
@@ -81,7 +84,8 @@
         public final int ambientShadowColor;
 
         public final float keyShadowBlur;
-        public final float keyShadowOffset;
+        public final float keyShadowOffsetX;
+        public final float keyShadowOffsetY;
         public final int keyShadowColor;
 
         public ShadowInfo(Context c, AttributeSet attrs, int defStyle) {
@@ -89,11 +93,13 @@
             TypedArray a = c.obtainStyledAttributes(
                     attrs, R.styleable.ShadowInfo, defStyle, 0);
 
-            ambientShadowBlur = a.getDimension(R.styleable.ShadowInfo_ambientShadowBlur, 0);
+            ambientShadowBlur = a.getDimensionPixelSize(
+                    R.styleable.ShadowInfo_ambientShadowBlur, 0);
             ambientShadowColor = a.getColor(R.styleable.ShadowInfo_ambientShadowColor, 0);
 
-            keyShadowBlur = a.getDimension(R.styleable.ShadowInfo_keyShadowBlur, 0);
-            keyShadowOffset = a.getDimension(R.styleable.ShadowInfo_keyShadowOffset, 0);
+            keyShadowBlur = a.getDimensionPixelSize(R.styleable.ShadowInfo_keyShadowBlur, 0);
+            keyShadowOffsetX = a.getDimensionPixelSize(R.styleable.ShadowInfo_keyShadowOffsetX, 0);
+            keyShadowOffsetY = a.getDimensionPixelSize(R.styleable.ShadowInfo_keyShadowOffsetY, 0);
             keyShadowColor = a.getColor(R.styleable.ShadowInfo_keyShadowColor, 0);
             a.recycle();
         }
@@ -105,17 +111,26 @@
             if (textAlpha == 0 || (keyShadowAlpha == 0 && ambientShadowAlpha == 0)) {
                 textView.getPaint().clearShadowLayer();
                 return true;
-            } else if (ambientShadowAlpha > 0) {
+            } else if (ambientShadowAlpha > 0 && keyShadowAlpha == 0) {
                 textView.getPaint().setShadowLayer(ambientShadowBlur, 0, 0,
-                        setColorAlphaBound(ambientShadowColor, textAlpha));
+                        getTextShadowColor(ambientShadowColor, textAlpha));
                 return true;
-            } else if (keyShadowAlpha > 0) {
-                textView.getPaint().setShadowLayer(keyShadowBlur, 0.0f, keyShadowOffset,
-                        setColorAlphaBound(keyShadowColor, textAlpha));
+            } else if (keyShadowAlpha > 0 && ambientShadowAlpha == 0) {
+                textView.getPaint().setShadowLayer(
+                        keyShadowBlur,
+                        keyShadowOffsetX,
+                        keyShadowOffsetY,
+                        getTextShadowColor(keyShadowColor, textAlpha));
                 return true;
             } else {
                 return false;
             }
         }
     }
+
+    // Multiplies the alpha of shadowColor by textAlpha.
+    private static int getTextShadowColor(int shadowColor, int textAlpha) {
+        return setColorAlphaBound(shadowColor,
+                Math.round(Color.alpha(shadowColor) * textAlpha / 255f));
+    }
 }
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 8186dfa..872adec 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.views;
 
-import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.Utilities.getBadge;
 import static com.android.launcher3.Utilities.getFullDrawable;
 import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -23,9 +22,6 @@
 import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -55,18 +51,20 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.testing.TestProtocol;
 
 /**
  * A view that is created to look like another view with the purpose of creating fluid animations.
  */
 @TargetApi(Build.VERSION_CODES.Q)
 public class FloatingIconView extends FrameLayout implements
-        Animator.AnimatorListener, OnGlobalLayoutListener {
+        Animator.AnimatorListener, OnGlobalLayoutListener, FloatingView {
 
     private static final String TAG = FloatingIconView.class.getSimpleName();
 
@@ -74,7 +72,6 @@
     private static @Nullable IconLoadResult sIconLoadResult;
 
     public static final float SHAPE_PROGRESS_DURATION = 0.10f;
-    private static final int FADE_DURATION_MS = 200;
     private static final RectF sTmpRectF = new RectF();
     private static final Object[] sTmpObjArray = new Object[1];
 
@@ -89,6 +86,9 @@
 
     private IconLoadResult mIconLoadResult;
 
+    // Draw the drawable of the BubbleTextView behind ClipIconView to reveal the built in shadow.
+    private View mBtvDrawable;
+
     private ClipIconView mClipIconView;
     private @Nullable Drawable mBadge;
 
@@ -98,10 +98,11 @@
 
     private final Rect mFinalDrawableBounds = new Rect();
 
-    private AnimatorSet mFadeAnimatorSet;
     private ListenerView mListenerView;
     private Runnable mFastFinishRunnable;
 
+    private float mIconOffsetY;
+
     public FloatingIconView(Context context) {
         this(context, null);
     }
@@ -116,6 +117,8 @@
         mIsRtl = Utilities.isRtl(getResources());
         mListenerView = new ListenerView(context, attrs);
         mClipIconView = new ClipIconView(context, attrs);
+        mBtvDrawable = new ImageView(context, attrs);
+        addView(mBtvDrawable);
         addView(mClipIconView);
         setWillNotDraw(false);
     }
@@ -136,16 +139,18 @@
 
     /**
      * Positions this view to match the size and location of {@param rect}.
-     * @param alpha The alpha to set this view.
+     * @param alpha The alpha[0, 1] of the entire floating view.
+     * @param fgIconAlpha The alpha[0-255] of the foreground layer of the icon (if applicable).
      * @param progress A value from [0, 1] that represents the animation progress.
      * @param shapeProgressStart The progress value at which to start the shape reveal.
      * @param cornerRadius The corner radius of {@param rect}.
+     * @param isOpening True if view is used for app open animation, false for app close animation.
      */
-    public void update(RectF rect, float alpha, float progress, float shapeProgressStart,
-            float cornerRadius, boolean isOpening) {
+    public void update(float alpha, int fgIconAlpha, RectF rect, float progress,
+            float shapeProgressStart, float cornerRadius, boolean isOpening) {
         setAlpha(alpha);
-        mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening,
-                this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
+        mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha,
+                isOpening, this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
     }
 
     @Override
@@ -176,6 +181,7 @@
         setLayoutParams(lp);
 
         mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
+        mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
     }
 
     private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
@@ -209,8 +215,8 @@
     public static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
             RectF outRect, Rect outViewBounds) {
         boolean ignoreTransform = !isOpening;
-        if (v instanceof DeepShortcutView) {
-            v = ((DeepShortcutView) v).getBubbleText();
+        if (v instanceof BubbleTextHolder) {
+            v = ((BubbleTextHolder) v).getBubbleText();
             ignoreTransform = false;
         } else if (v.getParent() instanceof DeepShortcutView) {
             v = ((DeepShortcutView) v.getParent()).getIconView();
@@ -228,15 +234,8 @@
             outViewBounds.set(0, 0, v.getWidth(), v.getHeight());
         }
 
-        float[] points = new float[] {outViewBounds.left, outViewBounds.top, outViewBounds.right,
-                outViewBounds.bottom};
-        Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points,
-                false, ignoreTransform);
-        outRect.set(
-                Math.min(points[0], points[2]),
-                Math.min(points[1], points[3]),
-                Math.max(points[0], points[2]),
-                Math.max(points[1], points[3]));
+        Utilities.getBoundsForViewInDragLayer(launcher.getDragLayer(), v, outViewBounds,
+                ignoreTransform, null /** recycle */, outRect);
     }
 
     /**
@@ -252,14 +251,12 @@
     @WorkerThread
     @SuppressWarnings("WrongThread")
     private static void getIconResult(Launcher l, View originalView, ItemInfo info, RectF pos,
-            IconLoadResult iconLoadResult) {
-        Drawable drawable = null;
-        Drawable badge = null;
+            Drawable btvIcon, IconLoadResult iconLoadResult) {
+        Drawable drawable;
         boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
-                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
                 && !info.isDisabled(); // Use original icon for disabled icons.
-        Drawable btvIcon = originalView instanceof BubbleTextView
-                ? ((BubbleTextView) originalView).getIcon() : null;
+
+        Drawable badge = null;
         if (info instanceof SystemShortcut) {
             if (originalView instanceof ImageView) {
                 drawable = ((ImageView) originalView).getDrawable();
@@ -268,6 +265,9 @@
             } else {
                 drawable = originalView.getBackground();
             }
+        } else if (btvIcon instanceof PreloadIconDrawable) {
+            // Force the progress bar to display.
+            drawable = btvIcon;
         } else {
             int width = (int) pos.width();
             int height = (int) pos.height();
@@ -293,6 +293,8 @@
         drawable = drawable == null ? null : drawable.getConstantState().newDrawable();
         int iconOffset = getOffsetForIconBounds(l, drawable, pos);
         synchronized (iconLoadResult) {
+            iconLoadResult.btvDrawable = btvIcon == null || drawable == btvIcon
+                    ? null : btvIcon.getConstantState().newDrawable();
             iconLoadResult.drawable = drawable;
             iconLoadResult.badge = badge;
             iconLoadResult.iconOffset = iconOffset;
@@ -312,7 +314,8 @@
      * @param iconOffset The amount of offset needed to match this view with the original view.
      */
     @UiThread
-    private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge, int iconOffset) {
+    private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge,
+            @Nullable Drawable btvIcon, int iconOffset) {
         final InsettableFrameLayout.LayoutParams lp =
                 (InsettableFrameLayout.LayoutParams) getLayoutParams();
         mBadge = badge;
@@ -343,10 +346,21 @@
                 mBadge.setBounds(0, 0, clipViewOgWidth, clipViewOgHeight);
             }
         }
+
+        if (!mIsOpening && btvIcon != null) {
+            mBtvDrawable.setBackground(btvIcon);
+        }
         invalidate();
     }
 
     /**
+     * Returns true if the icon is different from main app icon
+     */
+    public boolean isDifferentFromAppIcon() {
+        return mIconLoadResult == null ? false : mIconLoadResult.isThemed;
+    }
+
+    /**
      * Checks if the icon result is loaded. If true, we set the icon immediately. Else, we add a
      * callback to set the icon once the icon result is loaded.
      */
@@ -361,7 +375,8 @@
         synchronized (mIconLoadResult) {
             if (mIconLoadResult.isIconLoaded) {
                 setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
-                        mIconLoadResult.iconOffset);
+                        mIconLoadResult.btvDrawable, mIconLoadResult.iconOffset);
+                setVisibility(VISIBLE);
                 setIconAndDotVisible(originalView, false);
             } else {
                 mIconLoadResult.onIconLoaded = () -> {
@@ -370,7 +385,7 @@
                     }
 
                     setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
-                            mIconLoadResult.iconOffset);
+                            mIconLoadResult.btvDrawable, mIconLoadResult.iconOffset);
 
                     setVisibility(VISIBLE);
                     setIconAndDotVisible(originalView, false);
@@ -383,8 +398,7 @@
     @WorkerThread
     @SuppressWarnings("WrongThread")
     private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O
-                || !(drawable instanceof AdaptiveIconDrawable)
+        if (!(drawable instanceof AdaptiveIconDrawable)
                 || (drawable instanceof FolderAdaptiveIcon)) {
             return 0;
         }
@@ -423,6 +437,7 @@
         mFastFinishRunnable = runnable;
     }
 
+    @Override
     public void fastFinish() {
         if (mFastFinishRunnable != null) {
             mFastFinishRunnable.run();
@@ -436,10 +451,6 @@
             mEndRunnable.run();
             mEndRunnable = null;
         }
-        if (mFadeAnimatorSet != null) {
-            mFadeAnimatorSet.end();
-            mFadeAnimatorSet = null;
-        }
     }
 
     @Override
@@ -460,10 +471,16 @@
     public void onAnimationRepeat(Animator animator) {}
 
     @Override
+    public void setPositionOffsetY(float y) {
+        mIconOffsetY = y;
+        onGlobalLayout();
+    }
+
+    @Override
     public void onGlobalLayout() {
-        if (mOriginalIcon.isAttachedToWindow() && mPositionOut != null) {
-            getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening,
-                    sTmpRectF);
+        if (mOriginalIcon != null && mOriginalIcon.isAttachedToWindow() && mPositionOut != null) {
+            getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening, sTmpRectF);
+            sTmpRectF.offset(0, mIconOffsetY);
             if (!sTmpRectF.equals(mPositionOut)) {
                 updatePosition(sTmpRectF, (InsettableFrameLayout.LayoutParams) getLayoutParams());
                 if (mOnTargetChangeRunnable != null) {
@@ -482,12 +499,28 @@
      */
     @UiThread
     public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) {
-        IconLoadResult result = new IconLoadResult(info);
-        MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {
-            RectF position = new RectF();
-            getLocationBoundsForView(l, v, isOpening, position);
-            getIconResult(l, v, info, position, result);
-        });
+        RectF position = new RectF();
+        getLocationBoundsForView(l, v, isOpening, position);
+
+        final FastBitmapDrawable btvIcon;
+        if (v instanceof BubbleTextView) {
+            BubbleTextView btv = (BubbleTextView) v;
+            if (info instanceof ItemInfoWithIcon
+                    && (((ItemInfoWithIcon) info).runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+                btvIcon = btv.makePreloadIcon();
+            } else {
+                btvIcon = btv.getIcon();
+            }
+        } else {
+            btvIcon = null;
+        }
+
+        IconLoadResult result = new IconLoadResult(info,
+                btvIcon == null ? false : btvIcon.isThemed());
+
+        MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() ->
+                getIconResult(l, v, info, position, btvIcon, result));
 
         sIconLoadResult = result;
         return result;
@@ -533,11 +566,6 @@
         view.setVisibility(INVISIBLE);
         parent.addView(view);
         dragLayer.addView(view.mListenerView);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "getFloatingIconView. listenerView "
-                    + "added to dragLayer. listenerView=" + view.mListenerView + ", fiv=" + view,
-                    new Exception());
-        }
         view.mListenerView.setListener(view::fastFinish);
 
         view.mEndRunnable = () -> {
@@ -548,8 +576,11 @@
                     setIconAndDotVisible(originalView, true);
                     view.finish(dragLayer);
                 } else {
-                    view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
-                    view.mFadeAnimatorSet.start();
+                    originalView.setVisibility(VISIBLE);
+                    if (originalView instanceof IconLabelDotView) {
+                        setIconAndDotVisible(originalView, true);
+                    }
+                    view.finish(dragLayer);
                 }
             } else {
                 view.finish(dragLayer);
@@ -566,54 +597,9 @@
         return view;
     }
 
-    private AnimatorSet createFadeAnimation(View originalView, DragLayer dragLayer) {
-        AnimatorSet fade = new AnimatorSet();
-        fade.setDuration(FADE_DURATION_MS);
-        fade.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                originalView.setVisibility(VISIBLE);
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                finish(dragLayer);
-            }
-        });
-
-        if (originalView instanceof IconLabelDotView) {
-            fade.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    setIconAndDotVisible(originalView, true);
-                }
-            });
-        }
-
-        if (originalView instanceof BubbleTextView) {
-            BubbleTextView btv = (BubbleTextView) originalView;
-            fade.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    btv.setIconVisible(true);
-                    btv.setForceHideDot(true);
-                }
-            });
-            fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255));
-        } else if (!(originalView instanceof FolderIcon)) {
-            fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
-        }
-
-        return fade;
-    }
-
     private void finish(DragLayer dragLayer) {
         ((ViewGroup) dragLayer.getParent()).removeView(this);
         dragLayer.removeView(mListenerView);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "listenerView removed from dragLayer. "
-                    + "listenerView=" + mListenerView + ", fiv=" + this, new Exception());
-        }
         recycle();
         mLauncher.getViewCache().recycleView(R.layout.floating_icon_view, this);
     }
@@ -630,11 +616,7 @@
         mLoadIconSignal = null;
         mEndRunnable = null;
         mFinalDrawableBounds.setEmpty();
-        if (mFadeAnimatorSet != null) {
-            mFadeAnimatorSet.cancel();
-        }
         mPositionOut = null;
-        mFadeAnimatorSet = null;
         mListenerView.setListener(null);
         mOriginalIcon = null;
         mOnTargetChangeRunnable = null;
@@ -642,19 +624,24 @@
         sTmpObjArray[0] = null;
         mIconLoadResult = null;
         mClipIconView.recycle();
+        mBtvDrawable.setBackground(null);
         mFastFinishRunnable = null;
+        mIconOffsetY = 0;
     }
 
     private static class IconLoadResult {
         final ItemInfo itemInfo;
+        final boolean isThemed;
+        Drawable btvDrawable;
         Drawable drawable;
         Drawable badge;
         int iconOffset;
         Runnable onIconLoaded;
         boolean isIconLoaded;
 
-        IconLoadResult(ItemInfo itemInfo) {
+        IconLoadResult(ItemInfo itemInfo, boolean isThemed) {
             this.itemInfo = itemInfo;
+            this.isThemed = isThemed;
         }
     }
 }
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index 040619e..e2e3be7 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -40,7 +40,7 @@
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.Executors;
 
 /**
@@ -97,7 +97,7 @@
 
         // Remove after some time, to avoid flickering
         Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable,
-                DefaultDisplay.INSTANCE.get(mLauncher).getInfo().singleFrameMs);
+                DisplayController.INSTANCE.get(mLauncher).getInfo().singleFrameMs);
     }
 
     private void removeViewFromParent() {
@@ -122,9 +122,6 @@
     }
 
     @Override
-    public void logActionCommand(int command) { }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_ICON_SURFACE) != 0;
     }
@@ -161,7 +158,7 @@
         if (mContract == null) {
             return;
         }
-        View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(
+        View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(-1,
                 mContract.componentName.getPackageName(), mContract.user);
 
         boolean iconChanged = mIcon != icon;
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/src/com/android/launcher3/views/FloatingView.java
similarity index 61%
copy from src/com/android/launcher3/util/SafeCloseable.java
copy to src/com/android/launcher3/views/FloatingView.java
index ba8ee04..ea4fd15 100644
--- a/src/com/android/launcher3/util/SafeCloseable.java
+++ b/src/com/android/launcher3/views/FloatingView.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,14 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package com.android.launcher3.util;
+package com.android.launcher3.views;
 
 /**
- * Extension of closeable which does not throw an exception
+ * Shared interface for floating views.
  */
-public interface SafeCloseable extends AutoCloseable {
+public interface FloatingView {
 
-    @Override
-    void close();
+    /**
+     * Offsets and updates the position of this view by {@param y}.
+     */
+    void setPositionOffsetY(float y);
+
+    /**
+     * Fast finish the animation.
+     */
+    void fastFinish();
 }
diff --git a/src/com/android/launcher3/views/ListenerView.java b/src/com/android/launcher3/views/ListenerView.java
index 3ef778b..b2df0ee 100644
--- a/src/com/android/launcher3/views/ListenerView.java
+++ b/src/com/android/launcher3/views/ListenerView.java
@@ -17,13 +17,11 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.testing.TestProtocol;
 
 /**
  * An invisible AbstractFloatingView that can run a callback when it is being closed.
@@ -38,20 +36,12 @@
     }
 
     public void setListener(Runnable listener) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView setListener lv=" + this
-                    + ", listener=" + listener, new Exception());
-        }
         mCloseListener = listener;
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView onAttachedToWindow lv=" + this,
-                    new Exception());
-        }
         mIsOpen = true;
     }
 
@@ -59,19 +49,10 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mIsOpen = false;
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView onDetachedFromView lv=" + this,
-                    new Exception());
-        }
     }
 
     @Override
     protected void handleClose(boolean animate) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView handeClose lv=" + this
-                    + ", mIsOpen=" + mIsOpen + ", mCloseListener=" + mCloseListener
-                    + ", getParent()=" + getParent(), new Exception());
-        }
         if (mIsOpen) {
             if (mCloseListener != null) {
                 mCloseListener.run();
@@ -85,21 +66,12 @@
     }
 
     @Override
-    public void logActionCommand(int command) {
-        // Users do not interact with FloatingIconView, so there is nothing to log here.
-    }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_LISTENER) != 0;
     }
 
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView touchEvent lv=" + this
-                    + ", ev=" + ev, new Exception());
-        }
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             handleClose(false);
         }
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index d558781..ecdd206 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.views;
 
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
+import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_LAUNCH_SOURCE;
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
@@ -25,6 +26,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
@@ -36,6 +38,7 @@
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.core.content.ContextCompat;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
@@ -48,7 +51,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -61,6 +64,7 @@
 
     private final ArrayMap<View, OptionItem> mItemMap = new ArrayMap<>();
     private RectF mTargetRect;
+    private boolean mShouldAddArrow;
 
     public OptionsPopupView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -85,10 +89,10 @@
         if (item == null) {
             return false;
         }
-        if (item.mEventId.getId() > 0) {
-            mLauncher.getStatsLogManager().logger().log(item.mEventId);
+        if (item.eventId.getId() > 0) {
+            mLauncher.getStatsLogManager().logger().log(item.eventId);
         }
-        if (item.mClickListener.onLongClick(view)) {
+        if (item.clickListener.onLongClick(view)) {
             close(true);
             return true;
         }
@@ -108,13 +112,17 @@
     }
 
     @Override
-    public void logActionCommand(int command) {
-        // TODO:
+    protected boolean isOfType(int type) {
+        return (type & TYPE_OPTIONS_POPUP) != 0;
+    }
+
+    public void setShouldAddArrow(boolean shouldAddArrow) {
+        mShouldAddArrow = shouldAddArrow;
     }
 
     @Override
-    protected boolean isOfType(int type) {
-        return (type & TYPE_OPTIONS_POPUP) != 0;
+    protected boolean shouldAddArrow() {
+        return mShouldAddArrow;
     }
 
     @Override
@@ -122,27 +130,50 @@
         mTargetRect.roundOut(outPos);
     }
 
-    public static void show(Launcher launcher, RectF targetRect, List<OptionItem> items) {
+    public static OptionsPopupView show(
+            Launcher launcher, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow) {
+        return show(launcher, targetRect, items, shouldAddArrow, 0 /* width */);
+    }
+
+    public static OptionsPopupView show(
+            Launcher launcher, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow,
+            int width) {
         OptionsPopupView popup = (OptionsPopupView) launcher.getLayoutInflater()
                 .inflate(R.layout.longpress_options_menu, launcher.getDragLayer(), false);
         popup.mTargetRect = targetRect;
+        popup.setShouldAddArrow(shouldAddArrow);
 
         for (OptionItem item : items) {
             DeepShortcutView view =
                     (DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup);
-            view.getIconView().setBackgroundResource(item.mIconRes);
-            view.getBubbleText().setText(item.mLabelRes);
-            view.setDividerVisibility(View.INVISIBLE);
+            if (width > 0) {
+                view.getLayoutParams().width = width;
+            }
+            view.getIconView().setBackgroundDrawable(item.icon);
+            view.getBubbleText().setText(item.label);
             view.setOnClickListener(popup);
             view.setOnLongClickListener(popup);
             popup.mItemMap.put(view, item);
         }
-        popup.reorderAndShow(popup.getChildCount());
+
+        popup.addPreDrawForColorExtraction(launcher);
+        popup.show();
+        return popup;
+    }
+
+    @Override
+    protected List<View> getChildrenForColorExtraction() {
+        int childCount = getChildCount();
+        ArrayList<View> children = new ArrayList<>(childCount);
+        for (int i = 0; i < childCount; ++i) {
+            children.add(getChildAt(i));
+        }
+        return children;
     }
 
     @VisibleForTesting
     public static ArrowPopup getOptionsPopup(Launcher launcher) {
-        return launcher.findViewById(R.id.deep_shortcuts_container);
+        return launcher.findViewById(R.id.popup_container);
     }
 
     public static void showDefaultOptions(Launcher launcher, float x, float y) {
@@ -152,28 +183,39 @@
             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
+     */
+    public static ArrayList<OptionItem> getOptions(Launcher launcher) {
         ArrayList<OptionItem> options = new ArrayList<>();
+        options.add(new OptionItem(launcher,
+                R.string.settings_button_text,
+                R.drawable.ic_setting,
+                LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
+                OptionsPopupView::startSettings));
+        if (!WidgetsModel.GO_DISABLE_WIDGETS) {
+            options.add(new OptionItem(launcher,
+                    R.string.widget_button_text,
+                    R.drawable.ic_widget,
+                    LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS,
+                    OptionsPopupView::onWidgetsClicked));
+        }
         int resString = Utilities.existsStyleWallpapers(launcher) ?
                 R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
         int resDrawable = Utilities.existsStyleWallpapers(launcher) ?
                 R.drawable.ic_palette : R.drawable.ic_wallpaper;
-        options.add(new OptionItem(resString, resDrawable,
+        options.add(new OptionItem(launcher,
+                resString,
+                resDrawable,
                 IGNORE,
                 OptionsPopupView::startWallpaperPicker));
-        if (!WidgetsModel.GO_DISABLE_WIDGETS) {
-            options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
-                    LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS,
-                    OptionsPopupView::onWidgetsClicked));
-        }
-        options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting,
-                LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
-                OptionsPopupView::startSettings));
-
-        show(launcher, target, options);
+        return options;
     }
 
-    public static boolean onWidgetsClicked(View view) {
+    private static boolean onWidgetsClicked(View view) {
         return openWidgets(Launcher.getLauncher(view.getContext())) != null;
     }
 
@@ -188,7 +230,7 @@
         }
     }
 
-    public static boolean startSettings(View view) {
+    private static boolean startSettings(View view) {
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startSettings");
         Launcher launcher = Launcher.getLauncher(view.getContext());
         launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
@@ -201,7 +243,7 @@
      * Event handler for the wallpaper picker button that appears after a long press
      * on the home screen.
      */
-    public static boolean startWallpaperPicker(View v) {
+    private static boolean startWallpaperPicker(View v) {
         Launcher launcher = Launcher.getLauncher(v.getContext());
         if (!Utilities.isWallpaperAllowed(launcher)) {
             Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
@@ -210,7 +252,8 @@
         Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                 .putExtra(EXTRA_WALLPAPER_OFFSET,
-                        launcher.getWorkspace().getWallpaperOffsetForCenterPage());
+                        launcher.getWorkspace().getWallpaperOffsetForCenterPage())
+                .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher");
         if (!Utilities.existsStyleWallpapers(launcher)) {
             intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
         } else {
@@ -220,30 +263,43 @@
         if (!TextUtils.isEmpty(pickerPackage)) {
             intent.setPackage(pickerPackage);
         }
-        return launcher.startActivitySafely(v, intent, dummyInfo(intent), null);
+        return launcher.startActivitySafely(v, intent, placeholderInfo(intent));
     }
 
-    static WorkspaceItemInfo dummyInfo(Intent intent) {
-        WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
-        dummyInfo.intent = intent;
-        dummyInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-        dummyInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
-        return dummyInfo;
+    static WorkspaceItemInfo placeholderInfo(Intent intent) {
+        WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
+        placeholderInfo.intent = intent;
+        placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+        placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
+        return placeholderInfo;
     }
 
     public static class OptionItem {
 
-        private final int mLabelRes;
-        private final int mIconRes;
-        private final EventEnum mEventId;
-        private final OnLongClickListener mClickListener;
+        // Used to create AccessibilityNodeInfo in AccessibilityActionsView.java.
+        public final int labelRes;
 
-        public OptionItem(int labelRes, int iconRes, EventEnum eventId,
-                OnLongClickListener clickListener) {
-            mLabelRes = labelRes;
-            mIconRes = iconRes;
-            mEventId = eventId;
-            mClickListener = clickListener;
+        public final CharSequence label;
+        public final Drawable icon;
+        public final EventEnum eventId;
+        public final OnLongClickListener clickListener;
+
+        public OptionItem(Context context, int labelRes, int iconRes, EventEnum eventId,
+                          OnLongClickListener clickListener) {
+            this.labelRes = labelRes;
+            this.label = context.getText(labelRes);
+            this.icon = ContextCompat.getDrawable(context, iconRes);
+            this.eventId = eventId;
+            this.clickListener = clickListener;
+        }
+
+        public OptionItem(CharSequence label, Drawable icon, EventEnum eventId,
+                          OnLongClickListener clickListener) {
+            this.labelRes = 0;
+            this.label = label;
+            this.icon = icon;
+            this.eventId = eventId;
+            this.clickListener = clickListener;
         }
     }
 }
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 6a83332..1c2534d 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -16,22 +16,32 @@
 
 package com.android.launcher3.views;
 
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
+import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
+
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Insets;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.Build;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.Property;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.WindowInsets;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.BaseRecyclerView;
@@ -47,8 +57,14 @@
  * The track and scrollbar that shows when you scroll the list.
  */
 public class RecyclerViewFastScroller extends View {
-
+    private static final String TAG = "RecyclerViewFastScroller";
+    private static final boolean DEBUG = false;
+    private static final int FASTSCROLL_THRESHOLD_MILLIS = 40;
     private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
+
+    // Track is very narrow to target and correctly. This is especially the case if a user is
+    // using a hardware case. Even if x is offset by following amount, we consider it to be valid.
+    private static final int SCROLLBAR_LEFT_OFFSET_TOUCH_DELEGATE_DP = 5;
     private static final Rect sTempRect = new Rect();
 
     private static final Property<RecyclerViewFastScroller, Integer> TRACK_WIDTH =
@@ -78,6 +94,7 @@
     /** Keeps the last known scrolling delta/velocity along y-axis. */
     private int mDy = 0;
     private final float mDeltaThreshold;
+    private final float mScrollbarLeftOffsetTouchDelegate;
 
     private final ViewConfiguration mConfig;
 
@@ -97,19 +114,24 @@
     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
     // prevent jumping, this offset is applied as the user scrolls.
     protected int mTouchOffsetY;
     protected int mThumbOffsetY;
+    protected int mRvOffsetY;
 
     // Fast scroller popup
     private TextView mPopupView;
     private boolean mPopupVisible;
     private String mPopupSectionName;
+    private Insets mSystemGestureInsets;
 
     protected BaseRecyclerView mRv;
     private RecyclerView.OnScrollListener mOnScrollListener;
+    @Nullable private OnFastScrollChangeListener mOnFastScrollChangeListener;
 
     private int mDownX;
     private int mDownY;
@@ -144,6 +166,8 @@
 
         mConfig = ViewConfiguration.get(context);
         mDeltaThreshold = res.getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
+        mScrollbarLeftOffsetTouchDelegate = res.getDisplayMetrics().density
+                * SCROLLBAR_LEFT_OFFSET_TOUCH_DELEGATE_DP;
 
         TypedArray ta =
                 context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewFastScroller, defStyleAttr, 0);
@@ -181,11 +205,18 @@
 
     public void setThumbOffsetY(int y) {
         if (mThumbOffsetY == y) {
+            int rvCurrentOffsetY = mRv.getCurrentScrollY();
+            if (mRvOffsetY != rvCurrentOffsetY) {
+                mRvOffsetY = mRv.getCurrentScrollY();
+                notifyScrollChanged();
+            }
             return;
         }
         updatePopupY(y);
         mThumbOffsetY = y;
         invalidate();
+        mRvOffsetY = mRv.getCurrentScrollY();
+        notifyScrollChanged();
     }
 
     public int getThumbOffsetY() {
@@ -219,36 +250,38 @@
     public boolean handleTouchEvent(MotionEvent ev, Point offset) {
         int x = (int) ev.getX() - offset.x;
         int y = (int) ev.getY() - offset.y;
+
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 // Keep track of the down positions
                 mDownX = x;
                 mDownY = mLastY = y;
+                mDownTimeStampMillis = ev.getDownTime();
 
                 if ((Math.abs(mDy) < mDeltaThreshold &&
-                        mRv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
+                        mRv.getScrollState() != SCROLL_STATE_IDLE)) {
                     // now the touch events are being passed to the {@link WidgetCell} until the
                     // touch sequence goes over the touch slop.
                     mRv.stopScroll();
                 }
                 if (isNearThumb(x, y)) {
                     mTouchOffsetY = mDownY - mThumbOffsetY;
-                } else if (mRv.supportsFastScrolling()
-                        && isNearScrollBar(mDownX)) {
-                    calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
-                    updateFastScrollSectionNameAndThumbOffset(y);
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
                 mLastY = y;
+                int absDeltaY = Math.abs(y - mDownY);
+                int absDeltaX = Math.abs(x - mDownX);
 
                 // Check if we should start scrolling, but ignore this fastscroll gesture if we have
                 // exceeded some fixed movement
-                mIgnoreDragGesture |= Math.abs(y - mDownY) > mConfig.getScaledPagingTouchSlop();
-                if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
-                        isNearThumb(mDownX, mLastY) &&
-                        Math.abs(y - mDownY) > mConfig.getScaledTouchSlop()) {
-                    calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+                mIgnoreDragGesture |= absDeltaY > mConfig.getScaledPagingTouchSlop();
+
+                if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling()) {
+                    if ((isNearThumb(mDownX, mLastY) && ev.getEventTime() - mDownTimeStampMillis
+                                    > FASTSCROLL_THRESHOLD_MILLIS)) {
+                        calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+                    }
                 }
                 if (mIsDragging) {
                     updateFastScrollSectionNameAndThumbOffset(y);
@@ -267,6 +300,13 @@
                 }
                 break;
         }
+        if (DEBUG) {
+            Log.d(TAG, (ev.getAction() == MotionEvent.ACTION_DOWN ? "\n" : "")
+                    + "handleTouchEvent " + MotionEvent.actionToString(ev.getAction())
+                    + " (" + x + "," + y + ")" + " isDragging=" + mIsDragging
+                    + " mIgnoreDragGesture=" + mIgnoreDragGesture);
+
+        }
         return mIsDragging;
     }
 
@@ -288,6 +328,7 @@
         if (!sectionName.equals(mPopupSectionName)) {
             mPopupSectionName = sectionName;
             mPopupView.setText(sectionName);
+            performHapticFeedback(CLOCK_TICK);
         }
         animatePopupVisibility(!sectionName.isEmpty());
         mLastTouchY = boundedY;
@@ -314,12 +355,27 @@
         canvas.drawRoundRect(mThumbBounds, r, r, mThumbPaint);
         if (Utilities.ATLEAST_Q) {
             mThumbBounds.roundOut(SYSTEM_GESTURE_EXCLUSION_RECT.get(0));
+            // swiping very close to the thumb area (not just within it's bound)
+            // will also prevent back gesture
             SYSTEM_GESTURE_EXCLUSION_RECT.get(0).offset(mThumbDrawOffset.x, mThumbDrawOffset.y);
+            if (Utilities.ATLEAST_Q && mSystemGestureInsets != null) {
+                SYSTEM_GESTURE_EXCLUSION_RECT.get(0).left =
+                        SYSTEM_GESTURE_EXCLUSION_RECT.get(0).right - mSystemGestureInsets.right;
+            }
             setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
         }
         canvas.restoreToCount(saveCount);
     }
 
+    @Override
+    @RequiresApi(Build.VERSION_CODES.Q)
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        if (Utilities.ATLEAST_Q) {
+            mSystemGestureInsets = insets.getSystemGestureInsets();
+        }
+        return super.onApplyWindowInsets(insets);
+    }
+
     private float getScrollThumbRadius() {
         return mWidth + mThumbPadding + mThumbPadding;
     }
@@ -359,7 +415,8 @@
      * Returns whether the specified x position is near the scroll bar.
      */
     public boolean isNearScrollBar(int x) {
-        return x >= (getWidth() - mMaxWidth) / 2 && x <= (getWidth() + mMaxWidth) / 2;
+        return x >= (getWidth() - mMaxWidth) / 2 - mScrollbarLeftOffsetTouchDelegate
+                && x <= (getWidth() + mMaxWidth) / 2;
     }
 
     private void animatePopupVisibility(boolean visible) {
@@ -371,9 +428,6 @@
     }
 
     private void updatePopupY(int lastTouchY) {
-        if (!mPopupVisible) {
-            return;
-        }
         int height = mPopupView.getHeight();
         // Aligns the rounded corner of the pop up with the top of the thumb.
         float top = mRv.getScrollBarTop() + lastTouchY + (getScrollThumbRadius() / 2f)
@@ -388,7 +442,9 @@
             return false;
         }
         getHitRect(sTempRect);
-        sTempRect.top += mRv.getScrollBarTop();
+        if (mIsRecyclerViewFirstChildInParent) {
+            sTempRect.top += mRv.getScrollBarTop();
+        }
         if (outOffset != null) {
             outOffset.set(sTempRect.left, sTempRect.top);
         }
@@ -401,4 +457,27 @@
         // 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/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 22faf97..1eb79ad 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -15,205 +15,41 @@
  */
 package com.android.launcher3.views;
 
-import static android.content.Context.ACCESSIBILITY_SERVICE;
-import static android.view.MotionEvent.ACTION_DOWN;
-
-import static androidx.core.graphics.ColorUtils.compositeColors;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.Keyframe;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
+import android.graphics.drawable.ColorDrawable;
 import android.util.AttributeSet;
-import android.util.IntProperty;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
 
-import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.BaseActivity;
 import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.uioverrides.WallpaperColorInfo;
-import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.widget.WidgetsFullSheet;
-
-import java.util.List;
+import com.android.launcher3.util.SystemUiController;
 
 /**
  * Simple scrim which draws a flat color
  */
-public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener,
-        AccessibilityStateChangeListener {
+public class ScrimView extends View implements Insettable {
+    private static final float STATUS_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.9f;
 
-    public static final IntProperty<ScrimView> DRAG_HANDLE_ALPHA =
-            new IntProperty<ScrimView>("dragHandleAlpha") {
+    private SystemUiController mSystemUiController;
 
-                @Override
-                public Integer get(ScrimView scrimView) {
-                    return scrimView.mDragHandleAlpha;
-                }
-
-                @Override
-                public void setValue(ScrimView scrimView, int value) {
-                    scrimView.setDragHandleAlpha(value);
-                }
-            };
-    private static final int WALLPAPERS = R.string.wallpaper_button_text;
-    private static final int WIDGETS = R.string.widget_button_text;
-    private static final int SETTINGS = R.string.settings_button_text;
-    private static final int ALPHA_CHANNEL_COUNT = 1;
-
-    private static final long DRAG_HANDLE_BOUNCE_DURATION_MS = 300;
-    // How much to delay before repeating the bounce.
-    private static final long DRAG_HANDLE_BOUNCE_DELAY_MS = 200;
-    // Repeat this many times (i.e. total number of bounces is 1 + this).
-    private static final int DRAG_HANDLE_BOUNCE_REPEAT_COUNT = 2;
-
-    private final Rect mTempRect = new Rect();
-    private final int[] mTempPos = new int[2];
-
-    protected final T mLauncher;
-    private final WallpaperColorInfo mWallpaperColorInfo;
-    private final AccessibilityManager mAM;
-    protected final int mEndScrim;
-    protected final boolean mIsScrimDark;
-
-    private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
-            new StateListener<LauncherState>() {
-        @Override
-        public void onStateTransitionComplete(LauncherState finalState) {
-            setImportantForAccessibility(finalState == ALL_APPS
-                    ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                    : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-        }
-    };
-
-    protected float mMaxScrimAlpha;
-
-    protected float mProgress = 1;
-    protected int mScrimColor;
-
-    protected int mCurrentFlatColor;
-    protected int mEndFlatColor;
-    protected int mEndFlatColorAlpha;
-
-    protected final Point mDragHandleSize;
-    private final int mDragHandleTouchSize;
-    private final int mDragHandlePaddingInVerticalBarLayout;
-    protected float mDragHandleOffset;
-    private final Rect mDragHandleBounds;
-    private final RectF mHitRect = new RectF();
-    private ObjectAnimator mDragHandleAnim;
-
-    private final MultiValueAlpha mMultiValueAlpha;
-
-    private final AccessibilityHelper mAccessibilityHelper;
-    @Nullable
-    protected Drawable mDragHandle;
-
-    private int mDragHandleAlpha = 255;
+    private ScrimDrawingController mDrawingController;
+    private int mBackgroundColor;
+    private boolean mIsVisible = true;
 
     public ScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mLauncher = Launcher.cast(Launcher.getLauncher(context));
-        mWallpaperColorInfo = WallpaperColorInfo.INSTANCE.get(context);
-        mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
-        mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f;
-
-        mMaxScrimAlpha = 0.7f;
-
-        Resources res = context.getResources();
-        mDragHandleSize = new Point(res.getDimensionPixelSize(R.dimen.vertical_drag_handle_width),
-                res.getDimensionPixelSize(R.dimen.vertical_drag_handle_height));
-        mDragHandleBounds = new Rect(0, 0, mDragHandleSize.x, mDragHandleSize.y);
-        mDragHandleTouchSize = res.getDimensionPixelSize(R.dimen.vertical_drag_handle_touch_size);
-        mDragHandlePaddingInVerticalBarLayout = context.getResources()
-                .getDimensionPixelSize(R.dimen.vertical_drag_handle_padding_in_vertical_bar_layout);
-
-        mAccessibilityHelper = createAccessibilityHelper();
-        ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
-
-        mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
         setFocusable(false);
-        mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
-    }
-
-    public AlphaProperty getAlphaProperty(int index) {
-        return mMultiValueAlpha.getProperty(index);
-    }
-
-    @NonNull
-    protected AccessibilityHelper createAccessibilityHelper() {
-        return new AccessibilityHelper();
     }
 
     @Override
     public void setInsets(Rect insets) {
-        updateDragHandleBounds();
-        updateDragHandleVisibility();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        updateDragHandleBounds();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mWallpaperColorInfo.addOnChangeListener(this);
-        onExtractedColorsChanged(mWallpaperColorInfo);
-
-        mAM.addAccessibilityStateChangeListener(this);
-        onAccessibilityStateChanged(mAM.isEnabled());
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mWallpaperColorInfo.removeOnChangeListener(this);
-        mAM.removeAccessibilityStateChangeListener(this);
     }
 
     @Override
@@ -222,324 +58,90 @@
     }
 
     @Override
-    public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
-        mScrimColor = wallpaperColorInfo.getMainColor();
-        mEndFlatColor = compositeColors(mEndScrim, setColorAlphaBound(
-                mScrimColor, Math.round(mMaxScrimAlpha * 255)));
-        mEndFlatColorAlpha = Color.alpha(mEndFlatColor);
-        updateColors();
-        invalidate();
+    protected boolean onSetAlpha(int alpha) {
+        updateSysUiColors();
+        return super.onSetAlpha(alpha);
     }
 
-    public void setProgress(float progress) {
-        if (mProgress != progress) {
-            mProgress = progress;
-            stopDragHandleEducationAnim();
-            updateColors();
-            updateSysUiColors();
-            updateDragHandleAlpha();
-            invalidate();
-        }
+    @Override
+    public void setBackgroundColor(int color) {
+        mBackgroundColor = color;
+        updateSysUiColors();
+        super.setBackgroundColor(color);
     }
 
-    public void reInitUi() { }
-
-    protected void updateColors() {
-        mCurrentFlatColor = mProgress >= 1 ? 0 : setColorAlphaBound(
-                mEndFlatColor, Math.round((1 - mProgress) * mEndFlatColorAlpha));
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+        mIsVisible = isVisible;
     }
 
-    protected void updateSysUiColors() {
-        // Use a light system UI (dark icons) if all apps is behind at least half of the
-        // status bar.
-        boolean forceChange = mProgress <= 0.1f;
-        if (forceChange) {
-            mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
-        } else {
-            mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
-        }
-    }
-
-    protected void updateDragHandleAlpha() {
-        if (mDragHandle != null) {
-            mDragHandle.setAlpha(mDragHandleAlpha);
-        }
-    }
-
-    private void setDragHandleAlpha(int alpha) {
-        if (alpha != mDragHandleAlpha) {
-            mDragHandleAlpha = alpha;
-            if (mDragHandle != null) {
-                mDragHandle.setAlpha(mDragHandleAlpha);
-                invalidate();
-            }
-        }
+    public boolean isFullyOpaque() {
+        return mIsVisible && getAlpha() == 1 && Color.alpha(mBackgroundColor) == 255;
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
-        if (mCurrentFlatColor != 0) {
-            canvas.drawColor(mCurrentFlatColor);
-        }
-        drawDragHandle(canvas);
-    }
-
-    protected void drawDragHandle(Canvas canvas) {
-        if (mDragHandle != null) {
-            canvas.translate(0, -mDragHandleOffset);
-            mDragHandle.draw(canvas);
-            canvas.translate(0, mDragHandleOffset);
+        super.onDraw(canvas);
+        if (mDrawingController != null) {
+            mDrawingController.drawOnScrim(canvas);
         }
     }
 
     @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        boolean superHandledTouch = super.onTouchEvent(event);
-        if (event.getAction() == ACTION_DOWN) {
-            if (!superHandledTouch && mHitRect.contains(event.getX(), event.getY())) {
-                if (startDragHandleEducationAnim()) {
-                    return true;
-                }
-            }
-            stopDragHandleEducationAnim();
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        updateSysUiColors();
+    }
+
+    private void updateSysUiColors() {
+        // Use a light system UI (dark icons) if all apps is behind at least half of the
+        // status bar.
+        final float threshold = STATUS_BAR_COLOR_FORCE_UPDATE_THRESHOLD;
+        boolean forceChange = getVisibility() == VISIBLE
+                && getAlpha() > threshold
+                && (Color.alpha(mBackgroundColor) / 255f) > threshold;
+        if (forceChange) {
+            getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !isScrimDark());
+        } else {
+            getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
         }
-        return superHandledTouch;
+    }
+
+    private SystemUiController getSystemUiController() {
+        if (mSystemUiController == null) {
+            mSystemUiController = BaseActivity.fromContext(getContext()).getSystemUiController();
+        }
+        return mSystemUiController;
+    }
+
+    private boolean isScrimDark() {
+        if (!(getBackground() instanceof ColorDrawable)) {
+            throw new IllegalStateException(
+                    "ScrimView must have a ColorDrawable background, this one has: "
+                            + getBackground());
+        }
+        return ColorUtils.calculateLuminance(
+                ((ColorDrawable) getBackground()).getColor()) < 0.5f;
     }
 
     /**
-     * Animates the drag handle to demonstrate how to get to all apps.
-     * @return Whether the animation was started (false if drag handle is invisible).
+     * Sets drawing controller. Invalidates ScrimView if drawerController has changed.
      */
-    public boolean startDragHandleEducationAnim() {
-        stopDragHandleEducationAnim();
-
-        if (mDragHandle == null || mDragHandle.getAlpha() != 255) {
-            return false;
-        }
-
-        final Drawable drawable = mDragHandle;
-        mDragHandle = null;
-
-        Rect bounds = new Rect(mDragHandleBounds);
-        bounds.offset(0, -(int) mDragHandleOffset);
-        drawable.setBounds(bounds);
-
-        Rect topBounds = new Rect(bounds);
-        topBounds.offset(0, -bounds.height());
-
-        Rect invalidateRegion = new Rect(bounds);
-        invalidateRegion.top = topBounds.top;
-
-        final float progressToReachTop = 0.6f;
-        Keyframe frameTop = Keyframe.ofObject(progressToReachTop, topBounds);
-        frameTop.setInterpolator(DEACCEL);
-        Keyframe frameBot = Keyframe.ofObject(1, bounds);
-        frameBot.setInterpolator(ACCEL_DEACCEL);
-        PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("bounds",
-                Keyframe.ofObject(0, bounds), frameTop, frameBot);
-        holder.setEvaluator(new RectEvaluator());
-
-        mDragHandleAnim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder);
-        long totalBounceDuration = DRAG_HANDLE_BOUNCE_DURATION_MS + DRAG_HANDLE_BOUNCE_DELAY_MS;
-        // The bounce finishes by this progress, the rest of the duration just delays next bounce.
-        float delayStartProgress = 1f - (float) DRAG_HANDLE_BOUNCE_DELAY_MS / totalBounceDuration;
-        mDragHandleAnim.addUpdateListener((v) -> invalidate(invalidateRegion));
-        mDragHandleAnim.setDuration(totalBounceDuration);
-        mDragHandleAnim.setInterpolator(clampToProgress(LINEAR, 0, delayStartProgress));
-        mDragHandleAnim.setRepeatCount(DRAG_HANDLE_BOUNCE_REPEAT_COUNT);
-        getOverlay().add(drawable);
-
-        mDragHandleAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mDragHandleAnim = null;
-                getOverlay().remove(drawable);
-                updateDragHandleVisibility(drawable);
-            }
-        });
-        mDragHandleAnim.start();
-        return true;
-    }
-
-    private void stopDragHandleEducationAnim() {
-        if (mDragHandleAnim != null) {
-            mDragHandleAnim.end();
-        }
-    }
-
-    protected void updateDragHandleBounds() {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        final int left;
-        final int width = getMeasuredWidth();
-        final int top = getMeasuredHeight() - mDragHandleSize.y - grid.getInsets().bottom;
-        final int topMargin;
-
-        if (grid.isVerticalBarLayout()) {
-            topMargin = grid.workspacePadding.bottom + mDragHandlePaddingInVerticalBarLayout;
-            if (grid.isSeascape()) {
-                left = width - grid.getInsets().right - mDragHandleSize.x
-                        - mDragHandlePaddingInVerticalBarLayout;
-            } else {
-                left = grid.getInsets().left + mDragHandlePaddingInVerticalBarLayout;
-            }
-        } else {
-            left = Math.round((width - mDragHandleSize.x) / 2f);
-            topMargin = grid.hotseatBarSizePx;
-        }
-        mDragHandleBounds.offsetTo(left, top - topMargin);
-        mHitRect.set(mDragHandleBounds);
-        // Inset outwards to increase touch size.
-        mHitRect.inset((mDragHandleSize.x - mDragHandleTouchSize) / 2f,
-                (mDragHandleSize.y - mDragHandleTouchSize) / 2f);
-
-        if (mDragHandle != null) {
-            mDragHandle.setBounds(mDragHandleBounds);
-        }
-    }
-
-    @Override
-    public void onAccessibilityStateChanged(boolean enabled) {
-        StateManager<LauncherState> stateManager = mLauncher.getStateManager();
-        stateManager.removeStateListener(mAccessibilityLauncherStateListener);
-
-        if (enabled) {
-            stateManager.addStateListener(mAccessibilityLauncherStateListener);
-            mAccessibilityLauncherStateListener.onStateTransitionComplete(stateManager.getState());
-        } else {
-            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-        }
-        updateDragHandleVisibility();
-    }
-
-    public void updateDragHandleVisibility() {
-        updateDragHandleVisibility(null);
-    }
-
-    private void updateDragHandleVisibility(@Nullable Drawable recycle) {
-        boolean visible = shouldDragHandleBeVisible();
-        boolean wasVisible = mDragHandle != null;
-        if (visible != wasVisible) {
-            if (visible) {
-                mDragHandle = recycle != null ? recycle :
-                        mLauncher.getDrawable(R.drawable.drag_handle_indicator_shadow);
-                mDragHandle.setBounds(mDragHandleBounds);
-
-                updateDragHandleAlpha();
-            } else {
-                mDragHandle = null;
-            }
+    public void setDrawingController(ScrimDrawingController drawingController) {
+        if (mDrawingController != drawingController) {
+            mDrawingController = drawingController;
             invalidate();
         }
     }
 
-    protected boolean shouldDragHandleBeVisible() {
-        return mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled();
-    }
-
-    @Override
-    public boolean dispatchHoverEvent(MotionEvent event) {
-        return mAccessibilityHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        return mAccessibilityHelper.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
-    }
-
-    @Override
-    public void onFocusChanged(boolean gainFocus, int direction,
-            Rect previouslyFocusedRect) {
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-        mAccessibilityHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-    }
-
-    protected class AccessibilityHelper extends ExploreByTouchHelper {
-
-        private static final int DRAG_HANDLE_ID = 1;
-
-        public AccessibilityHelper() {
-            super(ScrimView.this);
-        }
-
-        @Override
-        protected int getVirtualViewAt(float x, float y) {
-            return  mHitRect.contains((int) x, (int) y)
-                    ? DRAG_HANDLE_ID : INVALID_ID;
-        }
-
-        @Override
-        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
-            virtualViewIds.add(DRAG_HANDLE_ID);
-        }
-
-        @Override
-        protected void onPopulateNodeForVirtualView(int virtualViewId,
-                AccessibilityNodeInfoCompat node) {
-            node.setContentDescription(getContext().getString(R.string.all_apps_button_label));
-            node.setBoundsInParent(mDragHandleBounds);
-
-            getLocationOnScreen(mTempPos);
-            mTempRect.set(mDragHandleBounds);
-            mTempRect.offset(mTempPos[0], mTempPos[1]);
-            node.setBoundsInScreen(mTempRect);
-
-            node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
-            node.setClickable(true);
-            node.setFocusable(true);
-
-            if (mLauncher.isInState(NORMAL)) {
-                Context context = getContext();
-                if (Utilities.isWallpaperAllowed(context)) {
-                    node.addAction(
-                            new AccessibilityActionCompat(WALLPAPERS, context.getText(WALLPAPERS)));
-                }
-                node.addAction(new AccessibilityActionCompat(WIDGETS, context.getText(WIDGETS)));
-                node.addAction(new AccessibilityActionCompat(SETTINGS, context.getText(SETTINGS)));
-            }
-        }
-
-        @Override
-        protected boolean onPerformActionForVirtualView(
-                int virtualViewId, int action, Bundle arguments) {
-            if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
-                mLauncher.getUserEventDispatcher().logActionOnControl(
-                        Action.Touch.TAP, ControlType.ALL_APPS_BUTTON,
-                        mLauncher.getStateManager().getState().containerType);
-                mLauncher.getStateManager().goToState(ALL_APPS);
-                return true;
-            } else if (action == WALLPAPERS) {
-                return OptionsPopupView.startWallpaperPicker(ScrimView.this);
-            } else if (action == WIDGETS) {
-                int originalImportanceForAccessibility = getImportantForAccessibility();
-                setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-                WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher);
-                if (widgetsFullSheet == null) {
-                    setImportantForAccessibility(originalImportanceForAccessibility);
-                    return false;
-                }
-                widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
-                    @Override
-                    public void onViewAttachedToWindow(View view) {}
-
-                    @Override
-                    public void onViewDetachedFromWindow(View view) {
-                        setImportantForAccessibility(originalImportanceForAccessibility);
-                        widgetsFullSheet.removeOnAttachStateChangeListener(this);
-                    }
-                });
-                return true;
-            } else if (action == SETTINGS) {
-                return OptionsPopupView.startSettings(ScrimView.this);
-            }
-
-            return false;
-        }
-    }
-
     /**
-     * @return The top of this scrim view, or {@link Float#MAX_VALUE} if there's no distinct top.
+     * A Utility interface allowing for other surfaces to draw on ScrimView
      */
-    public float getVisualTop() {
-        return Float.MAX_VALUE;
+    public interface ScrimDrawingController {
+        /**
+         * Called inside ScrimView#OnDraw
+         */
+        void drawOnScrim(Canvas canvas);
     }
 }
diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
index 513ce59..49fcd2e 100644
--- a/src/com/android/launcher3/views/Snackbar.java
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -167,11 +167,6 @@
     }
 
     @Override
-    public void logActionCommand(int command) {
-        // TODO
-    }
-
-    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_SNACKBAR) != 0;
     }
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index d0ec9d7..923eb19 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -15,51 +15,26 @@
  */
 package com.android.launcher3.views;
 
-import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW;
-import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
-
 import android.content.Context;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
-import android.util.SparseBooleanArray;
-import android.view.View;
 import android.widget.EdgeEffect;
 import android.widget.RelativeLayout;
 
 import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory;
 
+import com.android.launcher3.Utilities;
+
+/**
+ * View group to allow rendering overscroll effect in a child at the parent level
+ */
 public class SpringRelativeLayout extends RelativeLayout {
 
-    private static final float STIFFNESS = (STIFFNESS_MEDIUM + STIFFNESS_LOW) / 2;
-    private static final float DAMPING_RATIO = DAMPING_RATIO_MEDIUM_BOUNCY;
-    private static final float VELOCITY_MULTIPLIER = 0.3f;
-
-    private static final FloatPropertyCompat<SpringRelativeLayout> DAMPED_SCROLL =
-            new FloatPropertyCompat<SpringRelativeLayout>("value") {
-
-                @Override
-                public float getValue(SpringRelativeLayout object) {
-                    return object.mDampedScrollShift;
-                }
-
-                @Override
-                public void setValue(SpringRelativeLayout object, float value) {
-                    object.setDampedScrollShift(value);
-                }
-            };
-
-    protected final SparseBooleanArray mSpringViews = new SparseBooleanArray();
-    private final SpringAnimation mSpring;
-
-    private float mDampedScrollShift = 0;
-    private SpringEdgeEffect mActiveEdge;
+    // fixed edge at the time force is applied
+    private final EdgeEffect mEdgeGlowTop;
+    private final EdgeEffect mEdgeGlowBottom;
 
     public SpringRelativeLayout(Context context) {
         this(context, null);
@@ -71,98 +46,79 @@
 
     public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mSpring = new SpringAnimation(this, DAMPED_SCROLL, 0);
-        mSpring.setSpring(new SpringForce(0)
-                .setStiffness(STIFFNESS)
-                .setDampingRatio(DAMPING_RATIO));
-    }
-
-    public void addSpringView(int id) {
-        mSpringViews.put(id, true);
-    }
-
-    public void removeSpringView(int id) {
-        mSpringViews.delete(id);
-        invalidate();
-    }
-
-    /**
-     * Used to clip the canvas when drawing child views during overscroll.
-     */
-    public int getCanvasClipTopForOverscroll() {
-        return 0;
+        mEdgeGlowTop = Utilities.ATLEAST_S
+                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
+        mEdgeGlowBottom = Utilities.ATLEAST_S
+                ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
+        setWillNotDraw(false);
     }
 
     @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
-            int saveCount = canvas.save();
-
-            canvas.clipRect(0, getCanvasClipTopForOverscroll(), getWidth(), getHeight());
-            canvas.translate(0, mDampedScrollShift);
-            boolean result = super.drawChild(canvas, child, drawingTime);
-
-            canvas.restoreToCount(saveCount);
-
-            return result;
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        if (!mEdgeGlowTop.isFinished()) {
+            final int restoreCount = canvas.save();
+            canvas.translate(0, 0);
+            mEdgeGlowTop.setSize(getWidth(), getHeight());
+            if (mEdgeGlowTop.draw(canvas)) {
+                postInvalidateOnAnimation();
+            }
+            canvas.restoreToCount(restoreCount);
         }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    private void setActiveEdge(SpringEdgeEffect edge) {
-        if (mActiveEdge != edge && mActiveEdge != null) {
-            mActiveEdge.mDistance = 0;
-        }
-        mActiveEdge = edge;
-    }
-
-    protected void setDampedScrollShift(float shift) {
-        if (shift != mDampedScrollShift) {
-            mDampedScrollShift = shift;
-            invalidate();
+        if (!mEdgeGlowBottom.isFinished()) {
+            final int restoreCount = canvas.save();
+            final int width = getWidth();
+            final int height = getHeight();
+            canvas.translate(-width, height);
+            canvas.rotate(180, width, 0);
+            mEdgeGlowBottom.setSize(width, height);
+            if (mEdgeGlowBottom.draw(canvas)) {
+                postInvalidateOnAnimation();
+            }
+            canvas.restoreToCount(restoreCount);
         }
     }
 
-    private void finishScrollWithVelocity(float velocity) {
-        mSpring.setStartVelocity(velocity);
-        mSpring.setStartValue(mDampedScrollShift);
-        mSpring.start();
+
+    /**
+     * Absorbs the velocity as a result for swipe-up fling
+     */
+    protected void absorbSwipeUpVelocity(int velocity) {
+        mEdgeGlowBottom.onAbsorb(velocity);
+        invalidate();
     }
 
-    protected void finishWithShiftAndVelocity(float shift, float velocity,
-            DynamicAnimation.OnAnimationEndListener listener) {
-        setDampedScrollShift(shift);
-        mSpring.addEndListener(listener);
-        finishScrollWithVelocity(velocity);
+    protected void absorbPullDeltaDistance(float deltaDistance, float displacement) {
+        mEdgeGlowBottom.onPull(deltaDistance, displacement);
+        invalidate();
+    }
+
+    public void onRelease() {
+        mEdgeGlowBottom.onRelease();
     }
 
     public EdgeEffectFactory createEdgeEffectFactory() {
-        return new SpringEdgeEffectFactory();
+        return new ProxyEdgeEffectFactory();
     }
 
-    private class SpringEdgeEffectFactory extends EdgeEffectFactory {
+    private class ProxyEdgeEffectFactory extends EdgeEffectFactory {
 
         @NonNull @Override
         protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
-            switch (direction) {
-                case DIRECTION_TOP:
-                    return new SpringEdgeEffect(getContext(), +VELOCITY_MULTIPLIER);
-                case DIRECTION_BOTTOM:
-                    return new SpringEdgeEffect(getContext(), -VELOCITY_MULTIPLIER);
+            if (direction == DIRECTION_TOP) {
+                return new EdgeEffectProxy(getContext(), mEdgeGlowTop);
             }
             return super.createEdgeEffect(view, direction);
         }
     }
 
-    private class SpringEdgeEffect extends EdgeEffect {
+    private class EdgeEffectProxy extends EdgeEffect {
 
-        private final float mVelocityMultiplier;
+        private final EdgeEffect mParent;
 
-        private float mDistance;
-
-        public SpringEdgeEffect(Context context, float velocityMultiplier) {
+        EdgeEffectProxy(Context context, EdgeEffect parent) {
             super(context);
-            mVelocityMultiplier = velocityMultiplier;
+            mParent = parent;
         }
 
         @Override
@@ -170,22 +126,44 @@
             return false;
         }
 
+        private void invalidateParentScrollEffect() {
+            if (!mParent.isFinished()) {
+                invalidate();
+            }
+        }
+
         @Override
         public void onAbsorb(int velocity) {
-            finishScrollWithVelocity(velocity * mVelocityMultiplier);
+            mParent.onAbsorb(velocity);
+            invalidateParentScrollEffect();
+        }
+
+        @Override
+        public void onPull(float deltaDistance) {
+            mParent.onPull(deltaDistance);
+            invalidateParentScrollEffect();
         }
 
         @Override
         public void onPull(float deltaDistance, float displacement) {
-            setActiveEdge(this);
-            mDistance += deltaDistance * (mVelocityMultiplier / 3f);
-            setDampedScrollShift(mDistance * getHeight());
+            mParent.onPull(deltaDistance, displacement);
+            invalidateParentScrollEffect();
         }
 
         @Override
         public void onRelease() {
-            mDistance = 0;
-            finishScrollWithVelocity(0);
+            mParent.onRelease();
+            invalidateParentScrollEffect();
+        }
+
+        @Override
+        public void finish() {
+            mParent.finish();
+        }
+
+        @Override
+        public boolean isFinished() {
+            return mParent.isFinished();
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/TopRoundedCornerView.java b/src/com/android/launcher3/views/TopRoundedCornerView.java
index 7888b08..92cce92 100644
--- a/src/com/android/launcher3/views/TopRoundedCornerView.java
+++ b/src/com/android/launcher3/views/TopRoundedCornerView.java
@@ -17,13 +17,10 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.widget.FrameLayout;
 
-import com.android.launcher3.R;
 import com.android.launcher3.util.Themes;
 
 /**
@@ -35,41 +32,23 @@
     private final Path mClipPath = new Path();
     private float[] mRadii;
 
-    private final Paint mNavBarScrimPaint;
-    private int mNavBarScrimHeight = 0;
-
     public TopRoundedCornerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        int radius = getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
+        float radius = Themes.getDialogCornerRadius(context);
         mRadii = new float[] {radius, radius, radius, radius, 0, 0, 0, 0};
-
-        mNavBarScrimPaint = new Paint();
-        mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
     }
 
     public TopRoundedCornerView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public void setNavBarScrimHeight(int height) {
-        if (mNavBarScrimHeight != height) {
-            mNavBarScrimHeight = height;
-            invalidate();
-        }
-    }
-
     @Override
     public void draw(Canvas canvas) {
         canvas.save();
         canvas.clipPath(mClipPath);
         super.draw(canvas);
         canvas.restore();
-
-        if (mNavBarScrimHeight > 0) {
-            canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
-                    mNavBarScrimPaint);
-        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
new file mode 100644
index 0000000..c2947c7
--- /dev/null
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -0,0 +1,140 @@
+/*
+ * 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.views;
+
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+
+/**
+ * Education view about widgets.
+ */
+public class WidgetsEduView extends AbstractSlideInView<Launcher> implements Insettable {
+
+    private static final int DEFAULT_CLOSE_DURATION = 200;
+
+    private Rect mInsets = new Rect();
+
+    public WidgetsEduView(Context context, AttributeSet attr) {
+        this(context, attr, 0);
+    }
+
+    public WidgetsEduView(Context context, AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        handleClose(true, DEFAULT_CLOSE_DURATION);
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_WIDGETS_EDUCATION_DIALOG) != 0;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mContent = findViewById(R.id.edu_view);
+        findViewById(R.id.edu_close_button)
+                .setOnClickListener(v -> close(/* animate= */ true));
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        mInsets.set(insets);
+        mContent.setPadding(mContent.getPaddingStart(),
+                mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
+    }
+
+    private void show() {
+        attachToContainer();
+        animateOpen();
+    }
+
+    @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);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+        int widthUsed;
+        if (mInsets.bottom > 0) {
+            // Extra space between this view and mContent horizontally when the sheet is shown in
+            // portrait mode.
+            widthUsed = mInsets.left + mInsets.right;
+        } else {
+            // Extra space between this view and mContent horizontally when the sheet is shown in
+            // landscape mode.
+            Rect padding = deviceProfile.workspacePadding;
+            widthUsed = Math.max(padding.left + padding.right,
+                    2 * (mInsets.left + mInsets.right));
+        }
+
+        int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
+        measureChildWithMargins(mContent, widthMeasureSpec,
+                widthUsed, heightMeasureSpec, heightUsed);
+        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
+                MeasureSpec.getSize(heightMeasureSpec));
+    }
+
+    private void animateOpen() {
+        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+            return;
+        }
+        mIsOpen = true;
+        mOpenCloseAnimator.setValues(
+                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+        mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+        mOpenCloseAnimator.start();
+    }
+
+    /** Shows widget education dialog. */
+    public static WidgetsEduView showEducationDialog(Launcher launcher) {
+        LayoutInflater layoutInflater = LayoutInflater.from(launcher);
+        WidgetsEduView v = (WidgetsEduView) layoutInflater.inflate(
+                R.layout.widgets_edu, launcher.getDragLayer(), false);
+        v.show();
+        return v;
+    }
+}
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
deleted file mode 100644
index d35a38f..0000000
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ /dev/null
@@ -1,238 +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.views;
-
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsPagedView;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-
-/**
- * On boarding flow for users right after setting up work profile
- */
-public class WorkEduView extends AbstractSlideInView
-        implements Insettable, StateListener<LauncherState> {
-
-    private static final int DEFAULT_CLOSE_DURATION = 200;
-    public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
-    public static final String KEY_LEGACY_WORK_EDU_SEEN = "showed_bottom_user_education";
-
-    private static final int WORK_EDU_NOT_STARTED = 0;
-    private static final int WORK_EDU_PERSONAL_APPS = 1;
-    private static final int WORK_EDU_WORK_APPS = 2;
-
-    protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
-
-
-    private Rect mInsets = new Rect();
-    private View mViewWrapper;
-    private Button mProceedButton;
-    private TextView mContentText;
-    private AllAppsPagedView mAllAppsPagedView;
-
-    private int mNextWorkEduStep = WORK_EDU_PERSONAL_APPS;
-
-
-    public WorkEduView(Context context, AttributeSet attr) {
-        this(context, attr, 0);
-    }
-
-    public WorkEduView(Context context, AttributeSet attrs,
-            int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mContent = this;
-    }
-
-    @Override
-    protected void handleClose(boolean animate) {
-        mLauncher.getSharedPrefs().edit().putInt(KEY_WORK_EDU_STEP, mNextWorkEduStep).apply();
-        handleClose(true, DEFAULT_CLOSE_DURATION);
-    }
-
-    @Override
-    protected void onCloseComplete() {
-        super.onCloseComplete();
-        mLauncher.getStateManager().removeStateListener(this);
-    }
-
-    @Override
-    public void logActionCommand(int command) {
-        // Since this is on-boarding popup, it is not a user controlled action.
-    }
-
-    @Override
-    public int getLogContainerType() {
-        return LauncherLogProto.ContainerType.TIP;
-    }
-
-    @Override
-    protected boolean isOfType(int type) {
-        return (type & TYPE_ON_BOARD_POPUP) != 0;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mViewWrapper = findViewById(R.id.view_wrapper);
-        mProceedButton = findViewById(R.id.proceed);
-        mContentText = findViewById(R.id.content_text);
-
-        // make sure layout does not shrink when we change the text
-        mContentText.post(() -> mContentText.setMinLines(mContentText.getLineCount()));
-        if (mLauncher.getAppsView().getContentView() instanceof AllAppsPagedView) {
-            mAllAppsPagedView = (AllAppsPagedView) mLauncher.getAppsView().getContentView();
-        }
-
-        mProceedButton.setOnClickListener(view -> {
-            if (mAllAppsPagedView != null) {
-                mAllAppsPagedView.snapToPage(AllAppsContainerView.AdapterHolder.WORK);
-            }
-            goToWorkTab(true);
-        });
-    }
-
-    private void goToWorkTab(boolean animate) {
-        mProceedButton.setText(R.string.work_profile_edu_accept);
-        if (animate) {
-            ObjectAnimator animator = ObjectAnimator.ofFloat(mContentText, ALPHA, 0);
-            animator.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    mContentText.setText(mLauncher.getString(R.string.work_profile_edu_work_apps));
-                    ObjectAnimator.ofFloat(mContentText, ALPHA, 1).start();
-                }
-            });
-            animator.start();
-        } else {
-            mContentText.setText(mLauncher.getString(R.string.work_profile_edu_work_apps));
-        }
-        mNextWorkEduStep = WORK_EDU_WORK_APPS;
-        mProceedButton.setOnClickListener(v -> handleClose(true));
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        int leftInset = insets.left - mInsets.left;
-        int rightInset = insets.right - mInsets.right;
-        int bottomInset = insets.bottom - mInsets.bottom;
-        mInsets.set(insets);
-        setPadding(leftInset, getPaddingTop(), rightInset, 0);
-        mViewWrapper.setPaddingRelative(mViewWrapper.getPaddingStart(),
-                mViewWrapper.getPaddingTop(), mViewWrapper.getPaddingEnd(), bottomInset);
-    }
-
-    private void show() {
-        attachToContainer();
-        animateOpen();
-        mLauncher.getStateManager().addStateListener(this);
-    }
-
-    @Override
-    protected int getScrimColor(Context context) {
-        return FINAL_SCRIM_BG_COLOR;
-    }
-
-    private void goToFirstPage() {
-        if (mAllAppsPagedView != null) {
-            mAllAppsPagedView.snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
-        }
-    }
-
-    private void animateOpen() {
-        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
-            return;
-        }
-        mIsOpen = true;
-        mOpenCloseAnimator.setValues(
-                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-        mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mOpenCloseAnimator.start();
-    }
-
-    /**
-     * Checks if user has not seen onboarding UI yet and shows it when user navigates to all apps
-     */
-    public static StateListener<LauncherState> showEduFlowIfNeeded(Launcher launcher,
-            @Nullable StateListener<LauncherState> oldListener) {
-        if (oldListener != null) {
-            launcher.getStateManager().removeStateListener(oldListener);
-        }
-        if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
-                WORK_EDU_NOT_STARTED) != WORK_EDU_NOT_STARTED) {
-            return null;
-        }
-
-        StateListener<LauncherState> listener = new StateListener<LauncherState>() {
-            @Override
-            public void onStateTransitionComplete(LauncherState finalState) {
-                if (finalState != LauncherState.ALL_APPS) return;
-                LayoutInflater layoutInflater = LayoutInflater.from(launcher);
-                WorkEduView v = (WorkEduView) layoutInflater.inflate(
-                        R.layout.work_profile_edu, launcher.getDragLayer(),
-                        false);
-                v.show();
-                v.goToFirstPage();
-                launcher.getStateManager().removeStateListener(this);
-            }
-        };
-        launcher.getStateManager().addStateListener(listener);
-        return listener;
-    }
-
-    /**
-     * Shows work apps edu if user had dismissed full edu flow
-     */
-    public static void showWorkEduIfNeeded(Launcher launcher) {
-        if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
-                WORK_EDU_NOT_STARTED) != WORK_EDU_PERSONAL_APPS) {
-            return;
-        }
-        LayoutInflater layoutInflater = LayoutInflater.from(launcher);
-        WorkEduView v = (WorkEduView) layoutInflater.inflate(
-                R.layout.work_profile_edu, launcher.getDragLayer(), false);
-        v.show();
-        v.goToWorkTab(false);
-    }
-
-    private static boolean hasSeenLegacyEdu(Launcher launcher) {
-        return launcher.getSharedPrefs().getBoolean(KEY_LEGACY_WORK_EDU_SEEN, false);
-    }
-
-    @Override
-    public void onStateTransitionComplete(LauncherState finalState) {
-        close(false);
-    }
-}
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
new file mode 100644
index 0000000..1cc7f53
--- /dev/null
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget;
+
+import static com.android.launcher3.Utilities.ATLEAST_R;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
+import android.animation.PropertyValuesHolder;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.WindowInsets;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.AddItemActivity;
+import com.android.launcher3.views.AbstractSlideInView;
+
+/**
+ * Bottom sheet for the pin widget.
+ */
+public class AddItemWidgetsBottomSheet extends AbstractSlideInView<AddItemActivity> implements
+        View.OnApplyWindowInsetsListener {
+
+    private static final int DEFAULT_CLOSE_DURATION = 200;
+
+    private final Rect mInsets;
+
+    public AddItemWidgetsBottomSheet(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AddItemWidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mInsets = new Rect();
+    }
+
+    /**
+     * Attaches to activity container and animates open the bottom sheet.
+     */
+    public void show() {
+        ViewParent parent = getParent();
+        if (parent instanceof ViewGroup) {
+            ((ViewGroup) parent).removeView(this);
+        }
+        attachToContainer();
+        setOnApplyWindowInsetsListener(this);
+        animateOpen();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int width = r - l;
+        int height = b - t;
+
+        // Lay out 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);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+        int widthUsed;
+        if (mInsets.bottom > 0) {
+            widthUsed = mInsets.left + mInsets.right;
+        } else {
+            Rect padding = deviceProfile.workspacePadding;
+            widthUsed = Math.max(padding.left + padding.right,
+                    2 * (mInsets.left + mInsets.right));
+        }
+
+        int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
+        measureChildWithMargins(mContent, widthMeasureSpec,
+                widthUsed, heightMeasureSpec, heightUsed);
+        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
+                MeasureSpec.getSize(heightMeasureSpec));
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mContent = findViewById(R.id.add_item_bottom_sheet_content);
+    }
+
+    private void animateOpen() {
+        if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+            return;
+        }
+        mIsOpen = true;
+        mOpenCloseAnimator.setValues(
+                PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+        mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
+        mOpenCloseAnimator.start();
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        handleClose(animate, DEFAULT_CLOSE_DURATION);
+    }
+
+    @Override
+    protected boolean isOfType(@FloatingViewType int type) {
+        return (type & TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP) != 0;
+    }
+
+    @Override
+    protected int getScrimColor(Context context) {
+        return context.getResources().getColor(R.color.widgets_picker_scrim);
+    }
+
+    @SuppressLint("NewApi") // Already added API check.
+    @Override
+    public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
+        if (ATLEAST_R) {
+            Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
+            mInsets.set(insets.left, insets.top, insets.right, insets.bottom);
+        } else {
+            mInsets.set(windowInsets.getSystemWindowInsetLeft(),
+                    windowInsets.getSystemWindowInsetTop(),
+                    windowInsets.getSystemWindowInsetRight(),
+                    windowInsets.getSystemWindowInsetBottom());
+        }
+        mContent.setPadding(mContent.getPaddingStart(),
+                mContent.getPaddingTop(), mContent.getPaddingEnd(), mInsets.bottom);
+        return windowInsets;
+    }
+}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 3e5113a..9f0b9d9 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -15,43 +15,46 @@
  */
 package com.android.launcher3.widget;
 
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-
 import android.content.Context;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.widget.Toast;
 
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.uioverrides.WallpaperColorInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.AbstractSlideInView;
-
-import java.util.ArrayList;
+import com.android.launcher3.views.ArrowTipView;
 
 /**
  * Base class for various widgets popup
  */
-abstract class BaseWidgetSheet extends AbstractSlideInView
+public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
         implements OnClickListener, OnLongClickListener, DragSource,
-        PopupDataProvider.PopupDataChangeListener {
+        PopupDataProvider.PopupDataChangeListener, Insettable {
 
+    protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN =
+            "launcher.widgets_education_tip_seen";
+    protected final Rect mInsets = new Rect();
 
     /* Touch handling related member variables. */
     private Toast mWidgetInstructionToast;
@@ -61,65 +64,109 @@
     }
 
     protected int getScrimColor(Context context) {
-        WallpaperColorInfo colors = WallpaperColorInfo.INSTANCE.get(context);
-        int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
-        return setColorAlphaBound(colors.getSecondaryColor(), alpha);
+        return context.getResources().getColor(R.color.widgets_picker_scrim);
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mLauncher.getPopupDataProvider().setChangeListener(this);
+        mActivityContext.getPopupDataProvider().setChangeListener(this);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mLauncher.getPopupDataProvider().setChangeListener(null);
+        mActivityContext.getPopupDataProvider().setChangeListener(null);
     }
 
     @Override
     public final void onClick(View v) {
-        // Let the user know that they have to long press to add a widget
-        if (mWidgetInstructionToast != null) {
-            mWidgetInstructionToast.cancel();
+        Object tag = null;
+        if (v instanceof WidgetCell) {
+            tag = v.getTag();
+        } else if (v.getParent() instanceof WidgetCell) {
+            tag = ((WidgetCell) v.getParent()).getTag();
+        }
+        if (tag instanceof PendingAddShortcutInfo) {
+            mWidgetInstructionToast = showShortcutToast(getContext(), mWidgetInstructionToast);
+        } else {
+            mWidgetInstructionToast = showWidgetToast(getContext(), mWidgetInstructionToast);
         }
 
-        CharSequence msg = Utilities.wrapForTts(
-                getContext().getText(R.string.long_press_widget_to_add),
-                getContext().getString(R.string.long_accessible_way_to_add));
-        mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT);
-        mWidgetInstructionToast.show();
     }
 
     @Override
     public boolean onLongClick(View v) {
         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
         v.cancelLongPress();
-        if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
+        if (!ItemLongClickListener.canStartDrag(mActivityContext)) return false;
 
         if (v instanceof WidgetCell) {
             return beginDraggingWidget((WidgetCell) v);
+        } else if (v.getParent() instanceof WidgetCell) {
+            return beginDraggingWidget((WidgetCell) v.getParent());
         }
         return true;
     }
 
+    @Override
+    public void setInsets(Rect insets) {
+        mInsets.set(insets);
+    }
+
+
+    /**
+     * Measures the dimension of this view and its children by taking system insets, navigation bar,
+     * status bar, into account.
+     */
+    @GuardedBy("MainThread")
+    protected void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+        int widthUsed;
+        if (mInsets.bottom > 0) {
+            widthUsed = mInsets.left + mInsets.right;
+        } else {
+            Rect padding = deviceProfile.workspacePadding;
+            widthUsed = Math.max(padding.left + padding.right,
+                    2 * (mInsets.left + mInsets.right));
+        }
+
+        int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
+        measureChildWithMargins(mContent, widthMeasureSpec,
+                widthUsed, heightMeasureSpec, heightUsed);
+        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
+                MeasureSpec.getSize(heightMeasureSpec));
+    }
+
     private boolean beginDraggingWidget(WidgetCell v) {
         // Get the widget preview as the drag representation
         WidgetImageView image = v.getWidgetView();
 
         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
         // we abort the drag.
-        if (image.getBitmap() == null) {
+        if (image.getDrawable() == null && v.getAppWidgetHostViewPreview() == null) {
             return false;
         }
 
-        int[] loc = new int[2];
-        getPopupContainer().getLocationInDragLayer(image, loc);
+        PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
+        dragHelper.setRemoteViewsPreview(v.getRemoteViewsPreview());
+        dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
 
-        new PendingItemDragHelper(v).startDrag(
-                image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(),
-                new Point(loc[0], loc[1]), this, new DragOptions());
+        if (image.getDrawable() != null) {
+            int[] loc = new int[2];
+            getPopupContainer().getLocationInDragLayer(image, loc);
+
+            dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
+                    image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
+        } else {
+            View preview = v.getAppWidgetHostViewPreview();
+            int[] loc = new int[2];
+            getPopupContainer().getLocationInDragLayer(preview, loc);
+
+            Rect r = new Rect(0, 0, preview.getWidth(), preview.getHeight());
+            dragHelper.startDrag(r, preview.getMeasuredWidth(), preview.getMeasuredWidth(),
+                    new Point(loc[0], loc[1]), this, new DragOptions());
+        }
         close(true);
         return true;
     }
@@ -149,29 +196,67 @@
                 isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
     }
 
-    @Override
-    public void fillInLogContainerData(ItemInfo childInfo, Target child,
-            ArrayList<Target> parents) {
-        Target target = newContainerTarget(ContainerType.WIDGETS);
-        target.cardinality = getElementsRowCount();
-        parents.add(target);
-    }
-
-    @Override
-    public final void logActionCommand(int command) {
-        Target target = newContainerTarget(getLogContainerType());
-        target.cardinality = getElementsRowCount();
-        mLauncher.getUserEventDispatcher().logActionCommand(command, target);
-    }
-
-    @Override
-    public int getLogContainerType() {
-        return ContainerType.WIDGETS;
-    }
-
-    protected abstract int getElementsRowCount();
-
     protected SystemUiController getSystemUiController() {
-        return mLauncher.getSystemUiController();
+        return mActivityContext.getSystemUiController();
+    }
+
+    /**
+     * Show Widget tap toast prompting user to drag instead
+     */
+    public static Toast showWidgetToast(Context context, Toast toast) {
+        // Let the user know that they have to long press to add a widget
+        if (toast != null) {
+            toast.cancel();
+        }
+
+        CharSequence msg = Utilities.wrapForTts(
+                context.getText(R.string.long_press_widget_to_add),
+                context.getString(R.string.long_accessible_way_to_add));
+        toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
+        toast.show();
+        return toast;
+    }
+
+    /**
+     * Show shortcut tap toast prompting user to drag instead.
+     */
+    private static Toast showShortcutToast(Context context, Toast toast) {
+        // Let the user know that they have to long press to add a widget
+        if (toast != null) {
+            toast.cancel();
+        }
+
+        CharSequence msg = Utilities.wrapForTts(
+                context.getText(R.string.long_press_shortcut_to_add),
+                context.getString(R.string.long_accessible_way_to_add_shortcut));
+        toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
+        toast.show();
+        return toast;
+    }
+
+    /** Shows education tip on top center of {@code view} if view is laid out. */
+    @Nullable
+    protected ArrowTipView showEducationTipOnViewIfPossible(@Nullable View view) {
+        if (view == null || !ViewCompat.isLaidOut(view)) {
+            return null;
+        }
+        int[] coords = new int[2];
+        view.getLocationOnScreen(coords);
+        ArrowTipView arrowTipView =
+                new ArrowTipView(mActivityContext,  /* isPointingUp= */ false).showAtLocation(
+                        getContext().getString(R.string.long_press_widget_to_add),
+                        /* arrowXCoord= */coords[0] + view.getWidth() / 2,
+                        /* yCoord= */coords[1]);
+        if (arrowTipView != null) {
+            mActivityContext.getSharedPrefs().edit()
+                    .putBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, true).apply();
+        }
+        return arrowTipView;
+    }
+
+    /** Returns {@code true} if tip has previously been shown on any of {@link BaseWidgetSheet}. */
+    protected boolean hasSeenEducationTip() {
+        return mActivityContext.getSharedPrefs().getBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, false)
+                || Utilities.IS_RUNNING_IN_TEST_HARNESS;
     }
 }
diff --git a/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java b/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java
new file mode 100644
index 0000000..afceadd
--- /dev/null
+++ b/src/com/android/launcher3/widget/CachingWidgetPreviewLoader.java
@@ -0,0 +1,289 @@
+/*
+ * 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/WidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
similarity index 78%
rename from src/com/android/launcher3/WidgetPreviewLoader.java
rename to src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
index 2c45c77..4ec7e60 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
@@ -1,4 +1,19 @@
-package com.android.launcher3;
+/*
+ * 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.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -21,22 +36,25 @@
 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.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 androidx.annotation.UiThread;
 
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherFiles;
+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.LauncherIcons;
@@ -50,8 +68,7 @@
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.SQLiteCacheHelper;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.WidgetCell;
-import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.util.WidgetSizes;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -61,7 +78,8 @@
 import java.util.WeakHashMap;
 import java.util.concurrent.ExecutionException;
 
-public class WidgetPreviewLoader {
+/** {@link WidgetPreviewLoader} that loads preview images from a {@link CacheDb}. */
+public class DatabaseWidgetPreviewLoader implements WidgetPreviewLoader {
 
     private static final String TAG = "WidgetPreviewLoader";
     private static final boolean DEBUG = false;
@@ -80,74 +98,39 @@
     private final IconCache mIconCache;
     private final UserCache mUserCache;
     private final CacheDb mDb;
+    private final float mPreviewBoxCornerRadius;
 
-    private final UserHandle mMyUser = Process.myUserHandle();
-    private final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
-
-    public WidgetPreviewLoader(Context context, IconCache iconCache) {
+    public DatabaseWidgetPreviewLoader(Context context, IconCache iconCache) {
         mContext = context;
         mIconCache = iconCache;
         mUserCache = UserCache.INSTANCE.get(context);
         mDb = new CacheDb(context);
-    }
-
-    /**
-     * 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;
-        }
+        float previewCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
+        mPreviewBoxCornerRadius = previewCornerRadius > 0
+                ? previewCornerRadius
+                : mContext.getResources().getDimension(R.dimen.widget_preview_corner_radius);
     }
 
     /**
      * Generates the widget preview on {@link AsyncTask#THREAD_POOL_EXECUTOR}. Must be
-     * called on UI thread
+     * called on UI thread.
      *
      * @return a request id which can be used to cancel the request.
      */
-    public CancellationSignal getPreview(WidgetItem item, int previewWidth,
-            int previewHeight, WidgetCell caller) {
+    @Override
+    @NonNull
+    public CancellationSignal loadPreview(
+            @NonNull BaseActivity activity,
+            @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(key, item, previewWidth, previewHeight, caller);
+        PreviewLoadTask task =
+                new PreviewLoadTask(activity, key, item, previewWidth, previewHeight, callback);
         task.executeOnExecutor(Executors.THREAD_POOL_EXECUTOR);
 
         CancellationSignal signal = new CancellationSignal();
@@ -155,6 +138,7 @@
         return signal;
     }
 
+    /** Clears the database storing previews. */
     public void refresh() {
         mDb.clear();
     }
@@ -175,21 +159,37 @@
         private static final String COLUMN_VERSION = "version";
         private static final String COLUMN_PREVIEW_BITMAP = "preview_bitmap";
 
-        public CacheDb(Context context) {
+        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 + ") " +
+            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
+                    + ") "
+                    +
                     ");");
         }
     }
@@ -198,7 +198,7 @@
         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.size);
+        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]);
@@ -206,12 +206,14 @@
         mDb.insertOrReplace(values);
     }
 
+    /** Removes the package from the preview database. */
     public void removePackage(String packageName, UserHandle user) {
         removePackage(packageName, user, mUserCache.getSerialNumberForUser(user));
     }
 
-    private void removePackage(String packageName, UserHandle user, long userSerial) {
-        synchronized(mPackageVersions) {
+    /** Removes the package from the preview database. */
+    public void removePackage(String packageName, UserHandle user, long userSerial) {
+        synchronized (mPackageVersions) {
             mPackageVersions.remove(packageName);
         }
 
@@ -313,7 +315,7 @@
                     new String[]{
                             key.componentName.flattenToShortString(),
                             Long.toString(mUserCache.getSerialNumberForUser(key.user)),
-                            key.size
+                            key.mSize
                     });
             // If cancelled, skip getting the blob and decoding it into a bitmap
             if (loadTask.isCancelled()) {
@@ -342,7 +344,7 @@
     }
 
     /**
-     * Returns generatedPreview for a widget and if the preview should be saved in persistent
+     * Returns a generated preview for a widget and if the preview should be saved in persistent
      * storage.
      * @param launcher
      * @param item
@@ -393,8 +395,10 @@
             if (drawable != null) {
                 drawable = mutateOnMainThread(drawable);
             } else {
-                Log.w(TAG, "Can't load widget preview drawable 0x" +
-                        Integer.toHexString(info.previewImage) + " for provider: " + info.provider);
+                Log.w(TAG, "Can't load widget preview drawable 0x"
+                        + Integer.toHexString(info.previewImage)
+                        + " for provider: "
+                        + info.provider);
             }
         }
 
@@ -413,9 +417,10 @@
             previewHeight = drawable.getIntrinsicHeight();
         } else {
             DeviceProfile dp = launcher.getDeviceProfile();
-            int tileSize = Math.min(dp.cellWidthPx, dp.cellHeightPx);
-            previewWidth = tileSize * spanX;
-            previewHeight = tileSize * spanY;
+            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
@@ -428,37 +433,53 @@
             scale = maxPreviewWidth / (float) (previewWidth);
         }
         if (scale != 1f) {
-            previewWidth = Math.max((int)(scale * previewWidth), 1);
-            previewHeight = Math.max((int)(scale * previewHeight), 1);
+            previewWidth = Math.max((int) (scale * previewWidth), 1);
+            previewHeight = Math.max((int) (scale * previewHeight), 1);
         }
 
-        // If a bitmap is passed in, we use it; otherwise, we create a bitmap of the right size
         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 {
-            // We use the preview bitmap height to determine where the badge will be drawn in the
-            // UI. If its larger than what we need, resize the preview bitmap so that there are
-            // no transparent pixels between the preview and the badge.
-            if (preview.getHeight() > previewHeight) {
-                preview.reconfigure(preview.getWidth(), previewHeight, preview.getConfig());
+            // 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);
             }
-            // Reusing bitmap. Clear it.
+
             c.setBitmap(preview);
             c.drawColor(0, PorterDuff.Mode.CLEAR);
         }
 
         // Draw the scaled preview into the final bitmap
-        int x = (preview.getWidth() - previewWidth) / 2;
         if (widgetPreviewExists) {
-            drawable.setBounds(x, 0, x + previewWidth, previewHeight);
+            drawable.setBounds(0, 0, previewWidth, previewHeight);
             drawable.draw(c);
         } else {
-            RectF boxRect = drawBoxWithShadow(c, previewWidth, previewHeight);
+            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);
+            } else {
+                boxRect = drawBoxWithShadow(c, previewWidth, previewHeight);
+            }
+
             p.setStyle(Paint.Style.STROKE);
             p.setStrokeWidth(mContext.getResources()
                     .getDimension(R.dimen.widget_preview_cell_divider_width));
@@ -506,7 +527,7 @@
 
         ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.WHITE);
         builder.shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
-        builder.radius = res.getDimension(R.dimen.widget_preview_corner_radius);
+        builder.radius = mPreviewBoxCornerRadius;
         builder.keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
 
         builder.bounds.set(builder.shadowBlur, builder.shadowBlur,
@@ -539,20 +560,17 @@
             c.setBitmap(preview);
             c.drawColor(0, PorterDuff.Mode.CLEAR);
         }
-        RectF boxRect = drawBoxWithShadow(c, size, size);
+
+        drawBoxWithShadow(c, size, size);
 
         LauncherIcons li = LauncherIcons.obtain(mContext);
-        Bitmap icon = li.createBadgedIconBitmap(
+        Drawable icon = li.createBadgedIconBitmap(
                 mutateOnMainThread(info.getFullResIcon(mIconCache)),
-                Process.myUserHandle(), 0).icon;
+                Process.myUserHandle(), 0).newIcon(launcher);
         li.recycle();
 
-        Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
-
-        boxRect.set(0, 0, iconSize, iconSize);
-        boxRect.offset(padding, padding);
-        c.drawBitmap(icon, src, boxRect,
-                new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
+        icon.setBounds(padding, padding, padding + iconSize, padding + iconSize);
+        icon.draw(c);
         c.setBitmap(null);
         return preview;
     }
@@ -590,27 +608,28 @@
         }
     }
 
-    public class PreviewLoadTask extends AsyncTask<Void, Void, Bitmap>
+    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 WidgetCell mCaller;
+        private final WidgetPreviewLoadedCallback mCallback;
         private final BaseActivity mActivity;
         @Thunk long[] mVersions;
         @Thunk Bitmap mBitmapToRecycle;
 
+        @Nullable private Bitmap mUnusedPreviewBitmap;
         private boolean mSaveToDB = false;
 
-        PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth,
-                int previewHeight, WidgetCell caller) {
+        PreviewLoadTask(BaseActivity activity, WidgetCacheKey key, WidgetItem info,
+                int previewWidth, int previewHeight, WidgetPreviewLoadedCallback callback) {
+            mActivity = activity;
             mKey = key;
             mInfo = info;
             mPreviewHeight = previewHeight;
             mPreviewWidth = previewWidth;
-            mCaller = caller;
-            mActivity = BaseActivity.fromContext(mCaller.getContext());
+            mCallback = callback;
             if (DEBUG) {
                 Log.d(TAG, String.format("%s, %s, %d, %d",
                         mKey, mInfo, mPreviewHeight, mPreviewWidth));
@@ -628,9 +647,9 @@
             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) {
+                    if (candidate != null && candidate.isMutable()
+                            && candidate.getWidth() == mPreviewWidth
+                            && candidate.getHeight() == mPreviewHeight) {
                         unusedBitmap = candidate;
                         mUnusedBitmaps.remove(unusedBitmap);
                         break;
@@ -661,6 +680,11 @@
                 Pair<Bitmap, Boolean> pair = generatePreview(mActivity, mInfo, unusedBitmap,
                         mPreviewWidth, mPreviewHeight);
                 preview = pair.first;
+
+                if (preview != unusedBitmap) {
+                    mUnusedPreviewBitmap = unusedBitmap;
+                }
+
                 this.mSaveToDB = pair.second;
             }
             return preview;
@@ -668,13 +692,21 @@
 
         @Override
         protected void onPostExecute(final Bitmap preview) {
-            mCaller.applyPreview(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
@@ -738,21 +770,21 @@
 
     private static final class WidgetCacheKey extends ComponentKey {
 
-        @Thunk final String size;
+        @Thunk final String mSize;
 
-        public WidgetCacheKey(ComponentName componentName, UserHandle user, String size) {
+        WidgetCacheKey(ComponentName componentName, UserHandle user, String size) {
             super(componentName, user);
-            this.size = size;
+            this.mSize = size;
         }
 
         @Override
         public int hashCode() {
-            return super.hashCode() ^ size.hashCode();
+            return super.hashCode() ^ mSize.hashCode();
         }
 
         @Override
         public boolean equals(Object o) {
-            return super.equals(o) && ((WidgetCacheKey) o).size.equals(size);
+            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 3a24c3d..149ac57 100644
--- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -44,7 +44,7 @@
         mPaint = new TextPaint();
         mPaint.setColor(Color.WHITE);
         mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
-                mLauncher.getDeviceProfile().getFullScreenProfile().iconTextSizePx,
+                mLauncher.getDeviceProfile().iconTextSizePx,
                 getResources().getDisplayMetrics()));
         setBackgroundResource(R.drawable.bg_deferred_app_widget);
     }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
similarity index 72%
rename from src/com/android/launcher3/LauncherAppWidgetHost.java
rename to src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 7ea6851..fe83f3f 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.widget;
 
 import static android.app.Activity.RESULT_CANCELED;
 
@@ -25,16 +25,22 @@
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.Handler;
 import android.util.SparseArray;
 import android.widget.Toast;
 
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.widget.DeferredAppWidgetHostView;
-import com.android.launcher3.widget.LauncherAppWidgetHostView;
-import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.ArrayList;
@@ -49,8 +55,15 @@
 public class LauncherAppWidgetHost extends AppWidgetHost {
 
     private static final int FLAG_LISTENING = 1;
-    private static final int FLAG_RESUMED = 1 << 1;
-    private static final int FLAG_LISTEN_IF_RESUMED = 1 << 2;
+    private static final int FLAG_STATE_IS_NORMAL = 1 << 1;
+    private static final int FLAG_ACTIVITY_STARTED = 1 << 2;
+    private static final int FLAG_ACTIVITY_RESUMED = 1 << 3;
+    private static final int FLAGS_SHOULD_LISTEN =
+            FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED;
+    // TODO(b/191735836): Replace with ActivityOptions.KEY_SPLASH_SCREEN_STYLE when un-hidden
+    private static final String KEY_SPLASH_SCREEN_STYLE = "android.activity.splashScreenStyle";
+    // TODO(b/191735836): Replace with SplashScreen.SPLASH_SCREEN_STYLE_EMPTY when un-hidden
+    private static final int SPLASH_SCREEN_STYLE_EMPTY = 0;
 
     public static final int APPWIDGET_HOST_ID = 1024;
 
@@ -59,7 +72,7 @@
     private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
 
     private final Context mContext;
-    private int mFlags = FLAG_RESUMED;
+    private int mFlags = FLAG_STATE_IS_NORMAL;
 
     private IntConsumer mAppWidgetRemovedCallback = null;
 
@@ -130,49 +143,45 @@
     }
 
     /**
-     * Updates the resumed state of the host.
-     * When a host is not resumed, it defers calls to startListening until host is resumed again.
-     * But if the host was already listening, it will not call stopListening.
-     *
-     * @see #setListenIfResumed(boolean)
+     * Sets or unsets a flag the can change whether the widget host should be in the listening
+     * state.
      */
-    public void setResumed(boolean isResumed) {
-        if (isResumed == ((mFlags & FLAG_RESUMED) != 0)) {
-            return;
-        }
-        if (isResumed) {
-            mFlags |= FLAG_RESUMED;
-            // Start listening if we were supposed to start listening on resume
-            if ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0 && (mFlags & FLAG_LISTENING) == 0) {
-                startListening();
-            }
+    private void setShouldListenFlag(int flag, boolean on) {
+        if (on) {
+            mFlags |= flag;
         } else {
-            mFlags &= ~FLAG_RESUMED;
+            mFlags &= ~flag;
+        }
+
+        final boolean listening = isListening();
+        if (!listening && (mFlags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN) {
+            // Postpone starting listening until all flags are on.
+            startListening();
+        } else if (listening && (mFlags & FLAG_ACTIVITY_STARTED) == 0) {
+            // Postpone stopping listening until the activity is stopped.
+            stopListening();
         }
     }
 
     /**
-     * Updates the listening state of the host. If the host is not resumed, startListening is
-     * deferred until next resume.
-     *
-     * @see #setResumed(boolean)
+     * Registers an "entering/leaving Normal state" event.
      */
-    public void setListenIfResumed(boolean listenIfResumed) {
-        if (listenIfResumed == ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0)) {
-            return;
-        }
-        if (listenIfResumed) {
-            mFlags |= FLAG_LISTEN_IF_RESUMED;
-            if ((mFlags & FLAG_RESUMED) != 0) {
-                // If we are resumed, start listening immediately. Note we do not check for
-                // duplicate calls before calling startListening as startListening is safe to call
-                // multiple times.
-                startListening();
-            }
-        } else {
-            mFlags &= ~FLAG_LISTEN_IF_RESUMED;
-            stopListening();
-        }
+    public void setStateIsNormal(boolean isNormal) {
+        setShouldListenFlag(FLAG_STATE_IS_NORMAL, isNormal);
+    }
+
+    /**
+     * Registers an "activity started/stopped" event.
+     */
+    public void setActivityStarted(boolean isStarted) {
+        setShouldListenFlag(FLAG_ACTIVITY_STARTED, isStarted);
+    }
+
+    /**
+     * Registers an "activity paused/resumed" event.
+     */
+    public void setActivityResumed(boolean isResumed) {
+        setShouldListenFlag(FLAG_ACTIVITY_RESUMED, isResumed);
     }
 
     @Override
@@ -200,7 +209,7 @@
         }
     }
 
-    void addPendingView(int appWidgetId, PendingAppWidgetHostView view) {
+    public void addPendingView(int appWidgetId, PendingAppWidgetHostView view) {
         mPendingViews.put(appWidgetId, view);
     }
 
@@ -248,7 +257,7 @@
         super.onProviderChanged(appWidgetId, info);
         // The super method updates the dimensions of the providerInfo. Update the
         // launcher spans accordingly.
-        info.initSpans(mContext);
+        info.initSpans(mContext, LauncherAppState.getIDP(mContext));
     }
 
     /**
@@ -292,8 +301,13 @@
         activity.startActivityForResult(intent, requestCode);
     }
 
-
-    public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) {
+    /**
+     * Launches an app widget's configuration activity.
+     * @param activity The activity from which to launch the configuration activity
+     * @param widgetId The id of the bound app widget to be configured
+     * @param requestCode An optional request code to be returned with the result
+     */
+    public void startConfigActivity(BaseDraggingActivity activity, int widgetId, int requestCode) {
         if (WidgetsModel.GO_DISABLE_WIDGETS) {
             sendActionCancelled(activity, requestCode);
             return;
@@ -301,13 +315,29 @@
 
         try {
             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startConfigActivity");
-            startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
+            startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode,
+                    getConfigurationActivityOptions(activity, widgetId));
         } catch (ActivityNotFoundException | SecurityException e) {
             Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
             sendActionCancelled(activity, requestCode);
         }
     }
 
+    /**
+     * Returns an {@link android.app.ActivityOptions} bundle from the {code activity} for launching
+     * the configuration of the {@code widgetId} app widget, or null of options cannot be produced.
+     */
+    @Nullable
+    private Bundle getConfigurationActivityOptions(BaseDraggingActivity activity, int widgetId) {
+        LauncherAppWidgetHostView view = mViews.get(widgetId);
+        if (view == null) return null;
+        Object tag = view.getTag();
+        if (!(tag instanceof ItemInfo)) return null;
+        Bundle bundle = activity.getActivityLaunchOptions(view, (ItemInfo) tag).toBundle();
+        bundle.putInt(KEY_SPLASH_SCREEN_STYLE, SPLASH_SCREEN_STYLE_EMPTY);
+        return bundle;
+    }
+
     private void sendActionCancelled(final BaseActivity activity, final int requestCode) {
         new Handler().post(() -> activity.onActivityResult(requestCode, RESULT_CANCELED, null));
     }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 6f2e179..63bc416 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -19,36 +19,53 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.AdapterView;
 import android.widget.Advanceable;
 import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 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.Executors;
 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}
  */
 public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
-        implements TouchCompleteListener, View.OnLongClickListener {
+        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;
@@ -56,42 +73,95 @@
 
     // Maintains a list of widget ids which are supposed to be auto advanced.
     private static final SparseBooleanArray sAutoAdvanceWidgetIds = new SparseBooleanArray();
+    // Maximum duration for which updates can be deferred.
+    private static final long UPDATE_LOCK_TIMEOUT_MILLIS = 1000;
 
     protected final LayoutInflater mInflater;
 
     private final CheckLongPressHelper mLongPressHelper;
     protected final Launcher mLauncher;
+    private final Workspace mWorkspace;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mReinflateOnConfigChange;
 
+    // Maintain the color manager.
+    private final LocalColorExtractor mColorExtractor;
+
     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 Rect mEnforcedRectangle = new Rect();
+    private final float mEnforcedCornerRadius;
+    private final ViewOutlineProvider mCornerRadiusEnforcementOutline = new ViewOutlineProvider() {
+        @Override
+        public void getOutline(View view, Outline outline) {
+            if (mEnforcedRectangle.isEmpty() || mEnforcedCornerRadius <= 0) {
+                outline.setEmpty();
+            } else {
+                outline.setRoundRect(mEnforcedRectangle, mEnforcedCornerRadius);
+            }
+        }
+    };
+    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;
 
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mLauncher = Launcher.getLauncher(context);
+        mWorkspace = mLauncher.getWorkspace();
         mLongPressHelper = new CheckLongPressHelper(this, this);
         mInflater = LayoutInflater.from(context);
         setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
         setBackgroundResource(R.drawable.widget_internal_focus_bg);
 
-        if (Utilities.ATLEAST_OREO) {
-            setExecutor(Executors.THREAD_POOL_EXECUTOR);
-        }
+        setExecutor(Executors.THREAD_POOL_EXECUTOR);
         if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
             setOnLightBackground(true);
         }
+        mColorExtractor = LocalColorExtractor.newInstance(getContext());
+        mColorExtractor.setListener(this);
+
+        mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(getContext());
+        mDragLayerRelativeCoordinateHelper = new ViewGroupFocusHelper(mLauncher.getDragLayer());
+    }
+
+    @Override
+    public void setColorResources(@Nullable SparseIntArray colors) {
+        if (colors == null) {
+            resetColorResources();
+        } else {
+            super.setColorResources(colors);
+        }
+    }
+
+    @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 = Launcher.getLauncher(getContext()).getDragLayer();
+            DragLayer dragLayer = mLauncher.getDragLayer();
             dragLayer.requestDisallowInterceptTouchEvent(false);
         }
         view.performLongClick();
@@ -105,6 +175,14 @@
 
     @Override
     public void updateAppWidget(RemoteViews remoteViews) {
+        synchronized (mUpdateLock) {
+            if (isDeferringUpdates()) {
+                mDeferredRemoteViews = remoteViews;
+                return;
+            }
+            mDeferredRemoteViews = null;
+        }
+
         super.updateAppWidget(remoteViews);
 
         // The provider info or the views might have changed.
@@ -126,7 +204,7 @@
         if (viewGroup instanceof AdapterView) {
             return true;
         } else {
-            for (int i=0; i < viewGroup.getChildCount(); i++) {
+            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                 View child = viewGroup.getChildAt(i);
                 if (child instanceof ViewGroup) {
                     if (checkScrollableRecursively((ViewGroup) child)) {
@@ -138,9 +216,56 @@
         return false;
     }
 
+    /**
+     * Returns true if the application of {@link RemoteViews} through {@link #updateAppWidget} and
+     * colors through {@link #onColorsChanged} are currently being deferred.
+     * @see #beginDeferringUpdates()
+     */
+    private boolean isDeferringUpdates() {
+        return SystemClock.uptimeMillis() < mDeferUpdatesUntilMillis;
+    }
+
+    /**
+     * Begin deferring the application of any {@link RemoteViews} updates made through
+     * {@link #updateAppWidget} and color changes through {@link #onColorsChanged} until
+     * {@link #endDeferringUpdates()} has been called or the next {@link #updateAppWidget} or
+     * {@link #onColorsChanged} call after {@link #UPDATE_LOCK_TIMEOUT_MILLIS} have elapsed.
+     */
+    public void beginDeferringUpdates() {
+        synchronized (mUpdateLock) {
+            mDeferUpdatesUntilMillis = SystemClock.uptimeMillis() + UPDATE_LOCK_TIMEOUT_MILLIS;
+        }
+    }
+
+    /**
+     * Stop deferring the application of {@link RemoteViews} updates made through
+     * {@link #updateAppWidget} and color changes made through {@link #onColorsChanged} and apply
+     * any deferred updates.
+     */
+    public void endDeferringUpdates() {
+        RemoteViews remoteViews;
+        SparseIntArray deferredColors;
+        boolean hasDeferredColors;
+        synchronized (mUpdateLock) {
+            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);
+        }
+    }
+
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+            DragLayer dragLayer = mLauncher.getDragLayer();
             if (mIsScrollable) {
                 dragLayer.requestDisallowInterceptTouchEvent(true);
             }
@@ -162,6 +287,10 @@
 
         mIsAttachedToWindow = true;
         checkIfAutoAdvance();
+
+        if (mLastLocationRegistered != null) {
+            mColorExtractor.addLocation(List.of(mLastLocationRegistered));
+        }
     }
 
     @Override
@@ -172,6 +301,7 @@
         // state is updated. So isAttachedToWindow() will return true until next frame.
         mIsAttachedToWindow = false;
         checkIfAutoAdvance();
+        mColorExtractor.removeLocations();
     }
 
     @Override
@@ -218,6 +348,110 @@
         }
 
         mIsScrollable = checkScrollableRecursively(this);
+        updateColorExtraction();
+
+        enforceRoundedCorners();
+    }
+
+    /** Starts the drag mode. */
+    public void startDrag(AppWidgetHostViewDragListener dragListener) {
+        mIsInDragMode = true;
+        mDragListener = dragListener;
+    }
+
+    /** Handles a drag event occurred on a workspace page, {@code pageId}. */
+    public void handleDrag(Rect rectInDragLayer, int pageId) {
+        mWidgetSizeAtDrag.set(rectInDragLayer);
+        updateColorExtraction(mWidgetSizeAtDrag, pageId);
+    }
+
+    /** 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;
+    }
+
+    @Override
+    public void onColorsChanged(RectF rectF, SparseIntArray colors) {
+        synchronized (mUpdateLock) {
+            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));
     }
 
     @Override
@@ -312,6 +546,10 @@
             return;
         }
         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+        if (info == null) {
+            // This occurs when LauncherAppWidgetHostView is used to render a preview layout.
+            return;
+        }
         // Remove and rebind the current widget (which was inflated in the wrong
         // orientation), but don't delete it from the database
         mLauncher.removeItem(this, info, false  /* deleteFromDb */);
@@ -326,4 +564,40 @@
         }
         return false;
     }
+
+    @UiThread
+    private void resetRoundedCorners() {
+        setOutlineProvider(ViewOutlineProvider.BACKGROUND);
+        setClipToOutline(false);
+    }
+
+    @UiThread
+    private void enforceRoundedCorners() {
+        if (mEnforcedCornerRadius <= 0 || !RoundedCornerEnforcement.isRoundedCornerEnabled()) {
+            resetRoundedCorners();
+            return;
+        }
+        View background = RoundedCornerEnforcement.findBackground(this);
+        if (background == null
+                || RoundedCornerEnforcement.hasAppWidgetOptedOut(this, background)) {
+            resetRoundedCorners();
+            return;
+        }
+        RoundedCornerEnforcement.computeRoundedRectangle(this,
+                background,
+                mEnforcedRectangle);
+        setOutlineProvider(mCornerRadiusEnforcementOutline);
+        setClipToOutline(true);
+    }
+
+    /** Returns the corner radius currently enforced, in pixels. */
+    public float getEnforcedCornerRadius() {
+        return mEnforcedCornerRadius;
+    }
+
+    /** Returns true if the corner radius are enforced for this App Widget. */
+    public boolean hasEnforcedCornerRadius() {
+        return getClipToOutline();
+    }
+
 }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
new file mode 100644
index 0000000..d77d99d
--- /dev/null
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -0,0 +1,243 @@
+package com.android.launcher3.widget;
+
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.UserHandle;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+
+/**
+ * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
+ * a common object for describing both framework provided AppWidgets as well as custom widgets
+ * (who's implementation is owned by the launcher). This object represents a widget type / class,
+ * as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
+ */
+public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
+        implements ComponentWithLabelAndIcon {
+
+    public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
+
+    /**
+     * The desired number of cells that this widget occupies horizontally in
+     * {@link com.android.launcher3.CellLayout}.
+     */
+    public int spanX;
+
+    /**
+     * The desired number of cells that this widget occupies vertically in
+     * {@link com.android.launcher3.CellLayout}.
+     */
+    public int spanY;
+
+    /**
+     * The minimum number of cells that this widget can occupy horizontally in
+     * {@link com.android.launcher3.CellLayout}.
+     */
+    public int minSpanX;
+
+    /**
+     * The minimum number of cells that this widget can occupy vertically in
+     * {@link com.android.launcher3.CellLayout}.
+     */
+    public int minSpanY;
+
+    /**
+     * The maximum number of cells that this widget can occupy horizontally in
+     * {@link com.android.launcher3.CellLayout}.
+     */
+    public int maxSpanX;
+
+    /**
+     * The maximum number of cells that this widget can occupy vertically in
+     * {@link com.android.launcher3.CellLayout}.
+     */
+    public int maxSpanY;
+
+    private boolean mIsMinSizeFulfilled;
+
+    public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
+            AppWidgetProviderInfo info) {
+        final LauncherAppWidgetProviderInfo launcherInfo;
+        if (info instanceof LauncherAppWidgetProviderInfo) {
+            launcherInfo = (LauncherAppWidgetProviderInfo) info;
+        } else {
+
+            // In lieu of a public super copy constructor, we first write the AppWidgetProviderInfo
+            // into a parcel, and then construct a new LauncherAppWidgetProvider info from the
+            // associated super parcel constructor. This allows us to copy non-public members without
+            // using reflection.
+            Parcel p = Parcel.obtain();
+            info.writeToParcel(p, 0);
+            p.setDataPosition(0);
+            launcherInfo = new LauncherAppWidgetProviderInfo(p);
+            p.recycle();
+        }
+        launcherInfo.initSpans(context, LauncherAppState.getIDP(context));
+        return launcherInfo;
+    }
+
+    protected LauncherAppWidgetProviderInfo() {}
+
+    protected LauncherAppWidgetProviderInfo(Parcel in) {
+        super(in);
+    }
+
+    public void initSpans(Context context, InvariantDeviceProfile idp) {
+        int minSpanX = 0;
+        int minSpanY = 0;
+        int maxSpanX = idp.numColumns;
+        int maxSpanY = idp.numRows;
+        int spanX = 0;
+        int spanY = 0;
+
+        Rect widgetPadding = new Rect();
+        Rect localPadding = new Rect();
+        AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
+
+        Point cellSize = new Point();
+        for (DeviceProfile dp : idp.supportedProfiles) {
+            dp.getCellSize(cellSize);
+            // We want to account for the extra amount of padding that we are adding to the widget
+            // to ensure that it gets the full amount of space that it has requested.
+            // If grids supports insetting widgets, we do not account for widget padding.
+            if (dp.shouldInsetWidgets()) {
+                localPadding.setEmpty();
+            } else {
+                localPadding.set(widgetPadding);
+            }
+            minSpanX = Math.max(minSpanX,
+                    getSpanX(localPadding, minResizeWidth, dp.cellLayoutBorderSpacingPx,
+                            cellSize.x));
+            minSpanY = Math.max(minSpanY,
+                    getSpanY(localPadding, minResizeHeight, dp.cellLayoutBorderSpacingPx,
+                            cellSize.y));
+
+            if (ATLEAST_S) {
+                if (maxResizeWidth > 0) {
+                    maxSpanX = Math.min(maxSpanX,
+                            getSpanX(localPadding, maxResizeWidth, dp.cellLayoutBorderSpacingPx,
+                                    cellSize.x));
+                }
+                if (maxResizeHeight > 0) {
+                    maxSpanY = Math.min(maxSpanY,
+                            getSpanY(localPadding, maxResizeHeight, dp.cellLayoutBorderSpacingPx,
+                                    cellSize.y));
+                }
+            }
+
+            spanX = Math.max(spanX,
+                    getSpanX(localPadding, minWidth, dp.cellLayoutBorderSpacingPx, cellSize.x));
+            spanY = Math.max(spanY,
+                    getSpanY(localPadding, minHeight, dp.cellLayoutBorderSpacingPx, cellSize.y));
+        }
+
+        if (ATLEAST_S) {
+            // Ensures maxSpan >= minSpan
+            maxSpanX = Math.max(maxSpanX, minSpanX);
+            maxSpanY = Math.max(maxSpanY, minSpanY);
+
+            // Use targetCellWidth/Height if it is within the min/max ranges.
+            // Otherwise, use the span of minWidth/Height.
+            if (targetCellWidth >= minSpanX && targetCellWidth <= maxSpanX
+                    && targetCellHeight >= minSpanY && targetCellHeight <= maxSpanY) {
+                spanX = targetCellWidth;
+                spanY = targetCellHeight;
+            }
+        }
+
+        // If minSpanX/Y > spanX/Y, ignore the minSpanX/Y to match the behavior described in
+        // minResizeWidth & minResizeHeight Android documentation. See
+        // https://developer.android.com/reference/android/appwidget/AppWidgetProviderInfo
+        this.minSpanX = Math.min(spanX, minSpanX);
+        this.minSpanY = Math.min(spanY, minSpanY);
+        this.maxSpanX = maxSpanX;
+        this.maxSpanY = maxSpanY;
+        this.mIsMinSizeFulfilled = Math.min(spanX, minSpanX) <= idp.numColumns
+            && Math.min(spanY, minSpanY) <= idp.numRows;
+        // Ensures the default span X and span Y will not exceed the current grid size.
+        this.spanX = Math.min(spanX, idp.numColumns);
+        this.spanY = Math.min(spanY, idp.numRows);
+    }
+
+    /**
+     * Returns {@code true} if the widget's minimum size requirement can be fulfilled in the device
+     * grid setting, {@link InvariantDeviceProfile}, that was passed in
+     * {@link #initSpans(Context, InvariantDeviceProfile)}.
+     */
+    public boolean isMinSizeFulfilled() {
+        return mIsMinSizeFulfilled;
+    }
+
+    private int getSpanX(Rect widgetPadding, int widgetWidth, int cellSpacing, float cellWidth) {
+        return Math.max(1, (int) Math.ceil(
+                (widgetWidth + widgetPadding.left + widgetPadding.right + cellSpacing) / (cellWidth
+                        + cellSpacing)));
+    }
+
+    private int getSpanY(Rect widgetPadding, int widgetHeight, int cellSpacing, float cellHeight) {
+        return Math.max(1, (int) Math.ceil(
+                (widgetHeight + widgetPadding.top + widgetPadding.bottom + cellSpacing) / (
+                        cellHeight + cellSpacing)));
+    }
+
+    public String getLabel(PackageManager packageManager) {
+        return super.loadLabel(packageManager);
+    }
+
+    public Point getMinSpans() {
+        return new Point((resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1,
+                (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1);
+    }
+
+    public boolean isCustomWidget() {
+        return provider.getClassName().startsWith(CLS_CUSTOM_WIDGET_PREFIX);
+    }
+
+    public int getWidgetFeatures() {
+        if (Utilities.ATLEAST_P) {
+            return widgetFeatures;
+        } else {
+            return 0;
+        }
+    }
+
+    public boolean isReconfigurable() {
+        return configure != null && (getWidgetFeatures() & WIDGET_FEATURE_RECONFIGURABLE) != 0;
+    }
+
+    public boolean isConfigurationOptional() {
+        return ATLEAST_S
+                && isReconfigurable()
+                && (getWidgetFeatures() & WIDGET_FEATURE_CONFIGURATION_OPTIONAL) != 0;
+    }
+
+    @Override
+    public final ComponentName getComponent() {
+        return provider;
+    }
+
+    @Override
+    public final UserHandle getUser() {
+        return getProfile();
+    }
+
+    @Override
+    public Drawable getFullResIcon(IconCache cache) {
+        return cache.getFullResIcon(provider.getPackageName(), icon);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/LocalColorExtractor.java b/src/com/android/launcher3/widget/LocalColorExtractor.java
new file mode 100644
index 0000000..23d9e15
--- /dev/null
+++ b/src/com/android/launcher3/widget/LocalColorExtractor.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.app.WallpaperColors;
+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 {
+
+    /** Listener for color changes on a screen location. */
+    public interface Listener {
+        /**
+         * Method called when the colors on a registered location has changed.
+         *
+         * {@code extractedColors} maps the color resources {@code android.R.colors.system_*} to
+         * their value, in a format that can be passed directly to
+         * {@link AppWidgetHostView#setColorResources(SparseIntArray)}.
+         */
+        void onColorsChanged(RectF rect, SparseIntArray extractedColors);
+    }
+
+    /**
+     * Creates a new instance of LocalColorExtractor
+     */
+    public static LocalColorExtractor newInstance(Context context) {
+        return Overrides.getObject(LocalColorExtractor.class, context.getApplicationContext(),
+                R.string.local_colors_extraction_class);
+    }
+
+    /** Sets the object that will receive the color changes. */
+    public void setListener(@Nullable Listener listener) {
+        // 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
+    }
+
+    /**
+     * Updates the base context to contain the colors override
+     */
+    public void applyColorsOverride(Context base, WallpaperColors colors) { }
+
+    /**
+     * Generates color resource overrides from {@link WallpaperColors}.
+     */
+    @Nullable
+    public SparseIntArray generateColorsOverride(WallpaperColors colors) {
+        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 ed42bc4..6163b51 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.widget;
 
 import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -25,8 +26,11 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Reorderable;
 import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.views.ActivityContext;
 
 import java.util.ArrayList;
 
@@ -50,11 +54,16 @@
     private final PointF mTranslationForReorderPreview = new PointF(0, 0);
     private float mScaleForReorderBounce = 1f;
 
+    private final Rect mTempRect = new Rect();
+
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mChildrenFocused;
 
+    protected final BaseActivity mActivity;
+
     public NavigableAppWidgetHostView(Context context) {
         super(context);
+        mActivity = ActivityContext.lookupContext(context);
     }
 
     @Override
@@ -222,6 +231,25 @@
         int width = (int) (getMeasuredWidth() * mScaleToFit);
         int height = (int) (getMeasuredHeight() * mScaleToFit);
 
-        bounds.set(0, 0 , width, height);
+        getWidgetInset(mActivity.getDeviceProfile(), mTempRect);
+        bounds.set(mTempRect.left, mTempRect.top, width - mTempRect.right,
+                height - mTempRect.bottom);
+    }
+
+    /**
+     * Widgets have padding added by the system. We may choose to inset this padding if the grid
+     * supports it.
+     */
+    public void getWidgetInset(DeviceProfile grid, Rect out) {
+        if (!grid.shouldInsetWidgets()) {
+            out.setEmpty();
+            return;
+        }
+        AppWidgetProviderInfo info = getAppWidgetInfo();
+        if (info == null) {
+            out.set(grid.inv.defaultWidgetPadding);
+        } else {
+            AppWidgetHostView.getDefaultPaddingForWidget(getContext(), info.provider, out);
+        }
     }
 }
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index bef9a08..cbec642 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -15,14 +15,16 @@
  */
 package com.android.launcher3.widget;
 
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
-
 import android.appwidget.AppWidgetHostView;
+import android.content.Context;
 import android.os.Bundle;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.widget.util.WidgetSizes;
 
 /**
  * Meta data used for late binding of {@link LauncherAppWidgetProviderInfo}.
@@ -35,8 +37,9 @@
     public LauncherAppWidgetProviderInfo info;
     public AppWidgetHostView boundWidget;
     public Bundle bindOptions = null;
+    public int sourceContainer;
 
-    public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i) {
+    public PendingAddWidgetInfo(LauncherAppWidgetProviderInfo i, int container) {
         if (i.isCustomWidget()) {
             itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
         } else {
@@ -52,10 +55,22 @@
         spanY = i.spanY;
         minSpanX = i.minSpanX;
         minSpanY = i.minSpanY;
-        this.container = CONTAINER_WIDGETS_TRAY;
+        this.sourceContainer = this.container = container;
     }
 
     public WidgetAddFlowHandler getHandler() {
         return new WidgetAddFlowHandler(info);
     }
+
+    public Bundle getDefaultSizeOptions(Context context) {
+        return WidgetSizes.getWidgetSizeOptions(context, componentName, spanX, spanY);
+    }
+
+    @Override
+    public LauncherAtom.ItemInfo buildProto(FolderInfo folderInfo) {
+        LauncherAtom.ItemInfo info = super.buildProto(folderInfo);
+        return info.toBuilder()
+                .setAttribute(LauncherAppWidgetInfo.getAttribute(sourceContainer))
+                .build();
+    }
 }
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index ca47728..57a6d3f 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -16,8 +16,8 @@
 
 package com.android.launcher3.widget;
 
-import static com.android.launcher3.FastBitmapDrawable.newIcon;
 import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
+import static com.android.launcher3.model.data.PackageItemInfo.CONVERSATIONS;
 
 import android.content.Context;
 import android.graphics.Canvas;
@@ -29,15 +29,18 @@
 import android.text.Layout;
 import android.text.StaticLayout;
 import android.text.TextPaint;
+import android.util.SizeF;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.RemoteViews;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.R;
+import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -46,6 +49,8 @@
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Themes;
 
+import java.util.List;
+
 public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
         implements OnClickListener, ItemInfoUpdateReceiver {
     private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
@@ -80,8 +85,7 @@
         setBackgroundResource(R.drawable.pending_widget_bg);
         setWillNotDraw(false);
 
-        setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
-        updateAppWidget(null);
+        super.updateAppWidget(null);
         setOnClickListener(ItemClickHandler.INSTANCE);
 
         if (info.pendingItemInfo == null) {
@@ -95,9 +99,9 @@
 
     @Override
     public void updateAppWidget(RemoteViews remoteViews) {
-        super.updateAppWidget(remoteViews);
         WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(getContext());
         if (widgetManagerHelper.isAppWidgetRestored(mInfo.appWidgetId)) {
+            super.updateAppWidget(remoteViews);
             reInflate();
         }
     }
@@ -109,6 +113,11 @@
     }
 
     @Override
+    public void updateAppWidgetSize(Bundle newOptions, List<SizeF> sizes) {
+        // No-op
+    }
+
+    @Override
     protected View getDefaultView() {
         View defaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
         defaultView.setOnClickListener(this);
@@ -139,21 +148,32 @@
             mCenterDrawable = null;
         }
         if (info.bitmap.icon != null) {
+            Drawable widgetCategoryIcon = getWidgetCategoryIcon();
             // The view displays three modes,
             //   1) App icon in the center
             //   2) Preload icon in the center
-            //   3) Setup icon in the center and app icon in the top right corner.
+            //   3) App icon in the center with a setup icon on the top left corner.
             if (mDisabledForSafeMode) {
-                FastBitmapDrawable disabledIcon = newIcon(getContext(), info);
-                disabledIcon.setIsDisabled(true);
-                mCenterDrawable = disabledIcon;
+                if (widgetCategoryIcon == null) {
+                    FastBitmapDrawable disabledIcon = info.newIcon(getContext());
+                    disabledIcon.setIsDisabled(true);
+                    mCenterDrawable = disabledIcon;
+                } else {
+                    widgetCategoryIcon.setColorFilter(
+                            FastBitmapDrawable.getDisabledFColorFilter(/* disabledAlpha= */ 1f));
+                    mCenterDrawable = widgetCategoryIcon;
+                }
                 mSettingIconDrawable = null;
             } else if (isReadyForClickSetup()) {
-                mCenterDrawable = newIcon(getContext(), info);
+                mCenterDrawable = widgetCategoryIcon == null
+                        ? info.newIcon(getContext())
+                        : widgetCategoryIcon;
                 mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
                 updateSettingColor(info.bitmap.color);
             } else {
-                mCenterDrawable = newPendingIcon(getContext(), info);
+                mCenterDrawable = widgetCategoryIcon == null
+                        ? newPendingIcon(getContext(), info)
+                        : widgetCategoryIcon;
                 mSettingIconDrawable = null;
                 applyState();
             }
@@ -309,4 +329,19 @@
         }
 
     }
+
+    /**
+     * Returns the widget category icon for {@link #mInfo}.
+     *
+     * <p>If {@link #mInfo}'s category is {@code PackageItemInfo#NO_CATEGORY} or unknown, returns
+     * {@code null}.
+     */
+    @Nullable
+    private Drawable getWidgetCategoryIcon() {
+        switch (mInfo.pendingItemInfo.category) {
+            case CONVERSATIONS:
+                return getContext().getDrawable(R.drawable.ic_conversations_widget_category);
+        }
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 3c11274..cea4de7 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -23,8 +23,11 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.view.View;
+import android.view.View.MeasureSpec;
 import android.widget.RemoteViews;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.Launcher;
@@ -33,9 +36,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DraggableView;
-import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
 import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.RoundDrawableWrapper;
+import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 
 /**
  * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
@@ -48,15 +53,29 @@
     private final PendingAddItemInfo mAddInfo;
     private int[] mEstimatedCellSize;
 
-    private RemoteViews mPreview;
+    @Nullable private RemoteViews mRemoteViewsPreview;
+    @Nullable private NavigableAppWidgetHostView mAppWidgetHostViewPreview;
+    private final float mEnforcedRoundedCornersForWidget;
 
     public PendingItemDragHelper(View view) {
         super(view);
         mAddInfo = (PendingAddItemInfo) view.getTag();
+        mEnforcedRoundedCornersForWidget = RoundedCornerEnforcement.computeEnforcedRadius(
+                view.getContext());
     }
 
-    public void setPreview(RemoteViews preview) {
-        mPreview = preview;
+    /**
+     * 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) {
+        mRemoteViewsPreview = remoteViewsPreview;
+    }
+
+    /** Sets a {@link NavigableAppWidgetHostView} which shows a preview layout of an app widget. */
+    public void setAppWidgetHostViewPreview(
+            @Nullable NavigableAppWidgetHostView appWidgetHostViewPreview) {
+        mAppWidgetHostViewPreview = appWidgetHostViewPreview;
     }
 
     /**
@@ -73,7 +92,9 @@
         final Launcher launcher = Launcher.getLauncher(mView.getContext());
         LauncherAppState app = LauncherAppState.getInstance(launcher);
 
-        Bitmap preview = null;
+        Drawable preview = null;
+        final int previewWidth;
+        final int previewHeight;
         final float scale;
         final Point dragOffset;
         final Rect dragRegion;
@@ -89,13 +110,38 @@
 
             int[] previewSizeBeforeScale = new int[1];
 
-            if (mPreview != null) {
-                preview = LivePreviewWidgetCell.generateFromRemoteViews(launcher, mPreview,
-                        createWidgetInfo.info, maxWidth, previewSizeBeforeScale);
+            if (mRemoteViewsPreview != null) {
+                mAppWidgetHostViewPreview = new LauncherAppWidgetHostView(launcher);
+                mAppWidgetHostViewPreview.setAppWidget(/* appWidgetId= */ -1,
+                        ((PendingAddWidgetInfo) mAddInfo).info);
+                DeviceProfile deviceProfile = launcher.getDeviceProfile();
+                Rect padding = new Rect();
+                mAppWidgetHostViewPreview.getWidgetInset(deviceProfile, padding);
+                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;
+                mAppWidgetHostViewPreview.measure(
+                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
             }
-            if (preview == null) {
-                preview = app.getWidgetCache().generateWidgetPreview(launcher,
-                        createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale).first;
+            if (mAppWidgetHostViewPreview != null) {
+                previewSizeBeforeScale[0] = mAppWidgetHostViewPreview.getMeasuredWidth();
+                launcher.getDragController()
+                        .addDragListener(new AppWidgetHostViewDragListener(launcher));
+            }
+            if (preview == null && mAppWidgetHostViewPreview == null) {
+                Drawable p = new FastBitmapDrawable(
+                        app.getWidgetCache().generateWidgetPreview(launcher,
+                                createWidgetInfo.info, maxWidth, null,
+                                previewSizeBeforeScale).first);
+                if (RoundedCornerEnforcement.isRoundedCornerEnabled()) {
+                    p = new RoundDrawableWrapper(p, mEnforcedRoundedCornersForWidget);
+                }
+                preview = p;
             }
 
             if (previewSizeBeforeScale[0] < previewBitmapWidth) {
@@ -108,7 +154,14 @@
                 previewBounds.left += padding;
                 previewBounds.right -= padding;
             }
-            scale = previewBounds.width() / (float) preview.getWidth();
+            if (mAppWidgetHostViewPreview != null) {
+                previewWidth = mAppWidgetHostViewPreview.getMeasuredWidth();
+                previewHeight = mAppWidgetHostViewPreview.getMeasuredHeight();
+            } else {
+                previewWidth = preview.getIntrinsicWidth();
+                previewHeight = preview.getIntrinsicHeight();
+            }
+            scale = previewBounds.width() / (float) previewWidth;
             launcher.getDragController().addDragListener(new WidgetHostViewLoader(launcher, mView));
 
             dragOffset = null;
@@ -118,9 +171,12 @@
             PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
             Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
             LauncherIcons li = LauncherIcons.obtain(launcher);
-            preview = li.createScaledBitmapWithoutShadow(icon, 0);
+            preview = new FastBitmapDrawable(
+                    li.createScaledBitmapWithoutShadow(icon, 0));
+            previewWidth = preview.getIntrinsicWidth();
+            previewHeight = preview.getIntrinsicHeight();
             li.recycle();
-            scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();
+            scale = ((float) launcher.getDeviceProfile().iconSizePx) / previewWidth;
 
             dragOffset = new Point(previewPadding / 2, previewPadding / 2);
 
@@ -148,13 +204,19 @@
         launcher.getWorkspace().prepareDragWithProvider(this);
 
         int dragLayerX = screenPos.x + previewBounds.left
-                + (int) ((scale * preview.getWidth() - preview.getWidth()) / 2);
+                + (int) ((scale * previewWidth - previewWidth) / 2);
         int dragLayerY = screenPos.y + previewBounds.top
-                + (int) ((scale * preview.getHeight() - preview.getHeight()) / 2);
+                + (int) ((scale * previewHeight - previewHeight) / 2);
 
         // Start the drag
-        launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY,
-                source, mAddInfo, dragOffset, dragRegion, scale, scale, options);
+        if (mAppWidgetHostViewPreview != null) {
+            launcher.getDragController().startDrag(mAppWidgetHostViewPreview, draggableView,
+                    dragLayerX, dragLayerY, source, mAddInfo, dragOffset, dragRegion, scale, scale,
+                    options);
+        } else {
+            launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY,
+                    source, mAddInfo, dragOffset, dragRegion, scale, scale, options);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/RoundedCornerEnforcement.java b/src/com/android/launcher3/widget/RoundedCornerEnforcement.java
new file mode 100644
index 0000000..1e46ffd
--- /dev/null
+++ b/src/com/android/launcher3/widget/RoundedCornerEnforcement.java
@@ -0,0 +1,166 @@
+/*
+ * 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.appwidget.AppWidgetHostView;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utilities to compute the enforced the use of rounded corners on App Widgets.
+ */
+public class RoundedCornerEnforcement {
+    // This class is only a namespace and not meant to be instantiated.
+    private RoundedCornerEnforcement() {
+    }
+
+    /**
+     * Find the background view for a widget.
+     *
+     * @param appWidget the view containing the App Widget (typically the instance of
+     * {@link AppWidgetHostView}).
+     */
+    @Nullable
+    public static View findBackground(@NonNull View appWidget) {
+        List<View> backgrounds = findViewsWithId(appWidget, android.R.id.background);
+        if (backgrounds.size() == 1) {
+            return backgrounds.get(0);
+        }
+        // Really, the argument should contain the widget, so it cannot be the background.
+        if (appWidget instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) appWidget;
+            if (vg.getChildCount() > 0) {
+                return findUndefinedBackground(vg.getChildAt(0));
+            }
+        }
+        return appWidget;
+    }
+
+    /**
+     * Check whether the app widget has opted out of the enforcement.
+     */
+    public static boolean hasAppWidgetOptedOut(@NonNull View appWidget, @NonNull View background) {
+        return background.getId() == android.R.id.background && background.getClipToOutline();
+    }
+
+    /** Check if the app widget is in the deny list. */
+    public static boolean isRoundedCornerEnabled() {
+        return Utilities.ATLEAST_S && FeatureFlags.ENABLE_ENFORCED_ROUNDED_CORNERS.get();
+    }
+
+    /**
+     * Computes the rounded rectangle needed for this app widget.
+     *
+     * @param appWidget View onto which the rounded rectangle will be applied.
+     * @param background Background view. This must be either {@code appWidget} or a descendant
+     *                  of {@code appWidget}.
+     * @param outRect Rectangle set to the rounded rectangle coordinates, in the reference frame
+     *                of {@code appWidget}.
+     */
+    public static void computeRoundedRectangle(@NonNull View appWidget, @NonNull View background,
+            @NonNull Rect outRect) {
+        outRect.left = 0;
+        outRect.right = background.getWidth();
+        outRect.top = 0;
+        outRect.bottom = background.getHeight();
+        while (background != appWidget) {
+            outRect.offset(background.getLeft(), background.getTop());
+            background = (View) background.getParent();
+        }
+    }
+
+    /**
+     * Computes the radius of the rounded rectangle that should be applied to a widget expanded
+     * in the given context.
+     */
+    public static float computeEnforcedRadius(@NonNull Context context) {
+        if (!Utilities.ATLEAST_S) {
+            return 0;
+        }
+        Resources res = context.getResources();
+        float systemRadius = res.getDimension(android.R.dimen.system_app_widget_background_radius);
+        float defaultRadius = res.getDimension(R.dimen.enforced_rounded_corner_max_radius);
+        return Math.min(defaultRadius, systemRadius);
+    }
+
+    private static List<View> findViewsWithId(View view, @IdRes int viewId) {
+        List<View> output = new ArrayList<>();
+        accumulateViewsWithId(view, viewId, output);
+        return output;
+    }
+
+    // Traverse views. If the predicate returns true, continue on the children, otherwise, don't.
+    private static void accumulateViewsWithId(View view, @IdRes int viewId, List<View> output) {
+        if (view.getId() == viewId) {
+            output.add(view);
+            return;
+        }
+        if (view instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) view;
+            for (int i = 0; i < vg.getChildCount(); i++) {
+                accumulateViewsWithId(vg.getChildAt(i), viewId, output);
+            }
+        }
+    }
+
+    private static boolean isViewVisible(View view) {
+        if (view.getVisibility() != View.VISIBLE) {
+            return false;
+        }
+        return !view.willNotDraw() || view.getForeground() != null || view.getBackground() != null;
+    }
+
+    @Nullable
+    private static View findUndefinedBackground(View current) {
+        if (current.getVisibility() != View.VISIBLE) {
+            return null;
+        }
+        if (isViewVisible(current)) {
+            return current;
+        }
+        View lastVisibleView = null;
+        // Find the first view that is either not a ViewGroup, or a ViewGroup which will draw
+        // something, or a ViewGroup that contains more than one view.
+        if (current instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) current;
+            for (int i = 0; i < vg.getChildCount(); i++) {
+                View visibleView = findUndefinedBackground(vg.getChildAt(i));
+                if (visibleView != null) {
+                    if (lastVisibleView != null) {
+                        return current; // At least two visible children
+                    }
+                    lastVisibleView = visibleView;
+                }
+            }
+        }
+        return lastVisibleView;
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
index ebc2a25..9313266 100644
--- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
+++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
@@ -15,13 +15,15 @@
  */
 package com.android.launcher3.widget;
 
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL;
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE;
+
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.util.PendingRequestArgs;
@@ -79,8 +81,22 @@
         return true;
     }
 
+    /**
+     * Checks whether the widget needs configuration.
+     *
+     * A widget needs configuration if (1) it has a configuration activity and (2)
+     * it's configuration is not optional.
+     *
+     * @return true if the widget needs configuration, false otherwise.
+     */
     public boolean needsConfigure() {
-        return mProviderInfo.configure != null;
+        int featureFlags = mProviderInfo.widgetFeatures;
+        // A widget's configuration is optional only if it's configuration is marked as optional AND
+        // it can be reconfigured later.
+        boolean configurationOptional = (featureFlags & WIDGET_FEATURE_CONFIGURATION_OPTIONAL) != 0
+                && (featureFlags & WIDGET_FEATURE_RECONFIGURABLE) != 0;
+
+        return mProviderInfo.configure != null && !configurationOptional;
     }
 
     public LauncherAppWidgetProviderInfo getProviderInfo(Context context) {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index bef91d2..5769ba0 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -16,27 +16,39 @@
 
 package com.android.launcher3.widget;
 
+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.LinearLayout;
+import android.widget.RemoteViews;
 import android.widget.TextView;
 
+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.WidgetPreviewLoader;
-import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.FastBitmapDrawable;
+import com.android.launcher3.icons.RoundDrawableWrapper;
 import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.widget.util.WidgetSizes;
 
 /**
  * Represents the individual cell of the widget inside the widget tray. The preview is drawn
@@ -60,12 +72,17 @@
     /** 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;
     protected int mPresetPreviewSize;
     private int mCellSize;
+    private float mPreviewScale = 1f;
 
+    private FrameLayout mWidgetImageContainer;
     private WidgetImageView mWidgetImage;
     private TextView mWidgetName;
     private TextView mWidgetDims;
+    private TextView mWidgetDescription;
 
     protected WidgetItem mItem;
 
@@ -75,11 +92,16 @@
     private boolean mAnimatePreview = true;
 
     private boolean mApplyBitmapDeferred = false;
-    private Bitmap mDeferredBitmap;
+    private Drawable mDeferredDrawable;
 
     protected final BaseActivity mActivity;
-    protected final DeviceProfile mDeviceProfile;
     private final CheckLongPressHelper mLongPressHelper;
+    private final float mEnforcedCornerRadius;
+    private final int mShortcutPreviewPadding;
+
+    private RemoteViews mRemoteViewsPreview;
+    private NavigableAppWidgetHostView mAppWidgetHostViewPreview;
+    private int mSourceContainer = CONTAINER_WIDGETS_TRAY;
 
     public WidgetCell(Context context) {
         this(context, null);
@@ -93,28 +115,42 @@
         super(context, attrs, defStyle);
 
         mActivity = BaseActivity.fromContext(context);
-        mDeviceProfile = mActivity.getDeviceProfile();
         mLongPressHelper = new CheckLongPressHelper(this);
-
         mLongPressHelper.setLongPressTimeoutFactor(1);
+
         setContainerWidth();
         setWillNotDraw(false);
         setClipToPadding(false);
         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
+        mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
+        mShortcutPreviewPadding =
+                2 * getResources().getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
     }
 
     private void setContainerWidth() {
-        mCellSize = (int) (mDeviceProfile.allAppsIconSizePx * WIDTH_SCALE);
+        mCellSize = (int) (mActivity.getDeviceProfile().allAppsIconSizePx * WIDTH_SCALE);
         mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
+        mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview);
-        mWidgetName = ((TextView) findViewById(R.id.widget_name));
-        mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
+        mWidgetImageContainer = findViewById(R.id.widget_preview_container);
+        mWidgetImage = findViewById(R.id.widget_preview);
+        mWidgetName = findViewById(R.id.widget_name);
+        mWidgetDims = findViewById(R.id.widget_dims);
+        mWidgetDescription = findViewById(R.id.widget_description);
+    }
+
+    public void setRemoteViewsPreview(RemoteViews view) {
+        mRemoteViewsPreview = view;
+    }
+
+    @Nullable
+    public RemoteViews getRemoteViewsPreview() {
+        return mRemoteViewsPreview;
     }
 
     /**
@@ -125,36 +161,116 @@
             Log.d(TAG, "reset called on:" + mWidgetName.getText());
         }
         mWidgetImage.animate().cancel();
-        mWidgetImage.setBitmap(null, null);
+        mWidgetImage.setDrawable(null);
+        mWidgetImage.setVisibility(View.VISIBLE);
         mWidgetName.setText(null);
         mWidgetDims.setText(null);
+        mWidgetDescription.setText(null);
+        mWidgetDescription.setVisibility(GONE);
+        mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
 
         if (mActiveRequest != null) {
             mActiveRequest.cancel();
             mActiveRequest = null;
         }
+        mRemoteViewsPreview = null;
+        if (mAppWidgetHostViewPreview != null) {
+            mWidgetImageContainer.removeView(mAppWidgetHostViewPreview);
+        }
+        mAppWidgetHostViewPreview = null;
+        mItem = null;
+    }
+
+    public void setSourceContainer(int sourceContainer) {
+        this.mSourceContainer = sourceContainer;
     }
 
     public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
+        applyPreviewOnAppWidgetHostView(item);
+
+        Context context = getContext();
         mItem = item;
         mWidgetName.setText(mItem.label);
-        mWidgetDims.setText(getContext().getString(R.string.widget_dims_format,
+        mWidgetName.setContentDescription(
+                context.getString(R.string.widget_preview_context_description, mItem.label));
+        mWidgetDims.setText(context.getString(R.string.widget_dims_format,
                 mItem.spanX, mItem.spanY));
-        mWidgetDims.setContentDescription(getContext().getString(
+        mWidgetDims.setContentDescription(context.getString(
                 R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY));
-        mWidgetPreviewLoader = loader;
+        if (ATLEAST_S && mItem.widgetInfo != null) {
+            CharSequence description = mItem.widgetInfo.loadDescription(context);
+            if (description != null && description.length() > 0) {
+                mWidgetDescription.setText(description);
+                mWidgetDescription.setVisibility(VISIBLE);
+            } else {
+                mWidgetDescription.setVisibility(GONE);
+            }
+        }
 
+        mWidgetPreviewLoader = loader;
         if (item.activityInfo != null) {
             setTag(new PendingAddShortcutInfo(item.activityInfo));
         } else {
-            setTag(new PendingAddWidgetInfo(item.widgetInfo));
+            setTag(new PendingAddWidgetInfo(item.widgetInfo, mSourceContainer));
         }
     }
 
+
+    private void applyPreviewOnAppWidgetHostView(WidgetItem item) {
+        if (mRemoteViewsPreview != null) {
+            mAppWidgetHostViewPreview = createAppWidgetHostView(getContext());
+            setAppWidgetHostViewPreview(mAppWidgetHostViewPreview, item.widgetInfo,
+                    mRemoteViewsPreview);
+            return;
+        }
+
+        if (!item.hasPreviewLayout()) return;
+
+        Context context = getContext();
+        // If the context is a Launcher activity, DragView will show mAppWidgetHostViewPreview as
+        // a preview during drag & drop. And thus, we should use LauncherAppWidgetHostView, which
+        // supports applying local color extraction during drag & drop.
+        mAppWidgetHostViewPreview = isLauncherContext(context)
+                ? new LauncherAppWidgetHostView(context)
+                : createAppWidgetHostView(context);
+        LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
+                LauncherAppWidgetProviderInfo.fromProviderInfo(context, item.widgetInfo.clone());
+        // A hack to force the initial layout to be the preview layout since there is no API for
+        // rendering a preview layout for work profile apps yet. For non-work profile layout, a
+        // proper solution is to use RemoteViews(PackageName, LayoutId).
+        launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;
+        setAppWidgetHostViewPreview(mAppWidgetHostViewPreview,
+                launcherAppWidgetProviderInfo, /* remoteViews= */ null);
+    }
+
+    private void setAppWidgetHostViewPreview(
+            NavigableAppWidgetHostView appWidgetHostViewPreview,
+            LauncherAppWidgetProviderInfo providerInfo,
+            @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);
+    }
+
     public WidgetImageView getWidgetView() {
         return mWidgetImage;
     }
 
+    @Nullable
+    public NavigableAppWidgetHostView getAppWidgetHostViewPreview() {
+        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
@@ -164,9 +280,9 @@
     public void setApplyBitmapDeferred(boolean isDeferred) {
         if (mApplyBitmapDeferred != isDeferred) {
             mApplyBitmapDeferred = isDeferred;
-            if (!mApplyBitmapDeferred && mDeferredBitmap != null) {
-                applyPreview(mDeferredBitmap);
-                mDeferredBitmap = null;
+            if (!mApplyBitmapDeferred && mDeferredDrawable != null) {
+                applyPreview(mDeferredDrawable);
+                mDeferredDrawable = null;
             }
         }
     }
@@ -176,29 +292,82 @@
     }
 
     public void applyPreview(Bitmap bitmap) {
+        FastBitmapDrawable drawable = new FastBitmapDrawable(bitmap);
+        applyPreview(new RoundDrawableWrapper(drawable, mEnforcedCornerRadius));
+    }
+
+    private void applyPreview(Drawable drawable) {
         if (mApplyBitmapDeferred) {
-            mDeferredBitmap = bitmap;
+            mDeferredDrawable = drawable;
             return;
         }
-        if (bitmap != null) {
-            mWidgetImage.setBitmap(bitmap, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
-                    BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx)));
-            if (mAnimatePreview) {
-                mWidgetImage.setAlpha(0f);
-                ViewPropertyAnimator anim = mWidgetImage.animate();
-                anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
-            } else {
-                mWidgetImage.setAlpha(1f);
+        if (drawable != null) {
+            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;
+                scale = Math.min(maxWidth / previewWidth, 1);
             }
+            setContainerSize(
+                    Math.round(drawable.getIntrinsicWidth() * scale),
+                    Math.round(drawable.getIntrinsicHeight() * scale));
+            mWidgetImage.setDrawable(drawable);
+            mWidgetImage.setVisibility(View.VISIBLE);
+            if (mAppWidgetHostViewPreview != null) {
+                removeView(mAppWidgetHostViewPreview);
+                mAppWidgetHostViewPreview = null;
+            }
+        }
+        if (mAnimatePreview) {
+            mWidgetImageContainer.setAlpha(0f);
+            ViewPropertyAnimator anim = mWidgetImageContainer.animate();
+            anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
+        } else {
+            mWidgetImageContainer.setAlpha(1f);
         }
     }
 
+    private void setContainerSize(int width, int height) {
+        LayoutParams layoutParams = (LayoutParams) mWidgetImageContainer.getLayoutParams();
+        layoutParams.width = (int) (width * mPreviewScale);
+        layoutParams.height = (int) (height * mPreviewScale);
+        mWidgetImageContainer.setLayoutParams(layoutParams);
+    }
+
     public void ensurePreview() {
+        if (mAppWidgetHostViewPreview != null) {
+            setContainerSize(mPreviewWidth, mPreviewHeight);
+            FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+                    mPreviewWidth, mPreviewHeight, Gravity.FILL);
+            mAppWidgetHostViewPreview.setLayoutParams(params);
+            mWidgetImageContainer.addView(mAppWidgetHostViewPreview, /* index= */ 0);
+            mWidgetImage.setVisibility(View.GONE);
+            applyPreview((Drawable) null);
+            return;
+        }
         if (mActiveRequest != null) {
             return;
         }
-        mActiveRequest = mWidgetPreviewLoader.getPreview(
-                mItem, mPresetPreviewSize, mPresetPreviewSize, this);
+        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
@@ -232,10 +401,22 @@
         return "";
     }
 
-    @Override
-    public void setLayoutParams(ViewGroup.LayoutParams params) {
-        params.width = params.height = mCellSize;
-        super.setLayoutParams(params);
+    private static NavigableAppWidgetHostView createAppWidgetHostView(Context context) {
+        return new NavigableAppWidgetHostView(context) {
+            @Override
+            protected boolean shouldAllowDirectClick() {
+                return false;
+            }
+        };
+    }
+
+    private static boolean isLauncherContext(Context context) {
+        try {
+            Launcher.getLauncher(context);
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/WidgetCellPreview.java b/src/com/android/launcher3/widget/WidgetCellPreview.java
new file mode 100644
index 0000000..9f45d71
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetCellPreview.java
@@ -0,0 +1,71 @@
+/*
+ * 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.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * View group managing the widget preview: either using a {@link WidgetImageView} or an actual
+ * {@link LauncherAppWidgetHostView}.
+ */
+public class WidgetCellPreview extends FrameLayout {
+    public WidgetCellPreview(Context context) {
+        this(context, null);
+    }
+
+    public WidgetCellPreview(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WidgetCellPreview(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        super.onInterceptTouchEvent(ev);
+        return true;
+    }
+
+    /** Returns {@code true} if this container has a preview layout. */
+    public boolean hasPreviewLayout() {
+        for (int i = 0; i < getChildCount(); i++) {
+            if (getChildAt(i) instanceof LauncherAppWidgetHostView) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns {@link LauncherAppWidgetHostView} if this container has a preview layout. Otherwise,
+     * returns null.
+     */
+    @Nullable
+    public LauncherAppWidgetHostView getPreviewLayout() {
+        for (int i = 0; i < getChildCount(); i++) {
+            if (getChildAt(i) instanceof LauncherAppWidgetHostView) {
+                return (LauncherAppWidgetHostView) getChildAt(i);
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index c022374..46141e0 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -1,18 +1,14 @@
 package com.android.launcher3.widget;
 
 import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
 import android.content.Context;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
 import android.view.View;
 
-import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -87,7 +83,7 @@
         if (pInfo.isCustomWidget()) {
             return false;
         }
-        final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo);
+        final Bundle options = mInfo.getDefaultSizeOptions(mLauncher);
 
         // If there is a configuration activity, do not follow thru bound and inflate.
         if (mInfo.getHandler().needsConfigure()) {
@@ -151,25 +147,4 @@
         return true;
     }
 
-    public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) {
-        Rect rect = new Rect();
-        AppWidgetResizeFrame.getWidgetSizeRanges(context, info.spanX, info.spanY, rect);
-        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context,
-                info.componentName, null);
-
-        float density = context.getResources().getDisplayMetrics().density;
-        int xPaddingDips = (int) ((padding.left + padding.right) / density);
-        int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
-
-        Bundle options = new Bundle();
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
-                rect.left - xPaddingDips);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
-                rect.top - yPaddingDips);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
-                rect.right - xPaddingDips);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
-                rect.bottom - yPaddingDips);
-        return options;
-    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
index df2bcff..11f4485 100644
--- a/src/com/android/launcher3/widget/WidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -17,9 +17,7 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
@@ -27,7 +25,6 @@
 import android.view.View;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 
 /**
  * View that draws a bitmap horizontally centered. If the image width is greater than the view
@@ -35,12 +32,10 @@
  */
 public class WidgetImageView extends View {
 
-    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
     private final RectF mDstRectF = new RectF();
     private final int mBadgeMargin;
 
-    private Bitmap mBitmap;
-    private Drawable mBadge;
+    private Drawable mDrawable;
 
     public WidgetImageView(Context context) {
         this(context, null);
@@ -57,26 +52,22 @@
                 .getDimensionPixelSize(R.dimen.profile_badge_margin);
     }
 
-    public void setBitmap(Bitmap bitmap, Drawable badge) {
-        mBitmap = bitmap;
-        mBadge = badge;
+    /** Set the drawable to use for this view. */
+    public void setDrawable(Drawable drawable) {
+        mDrawable = drawable;
         invalidate();
     }
 
-    public Bitmap getBitmap() {
-        return mBitmap;
+    public Drawable getDrawable() {
+        return mDrawable;
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
-        if (mBitmap != null) {
+        if (mDrawable != null) {
             updateDstRectF();
-            canvas.drawBitmap(mBitmap, null, mDstRectF, mPaint);
-
-            // Only draw the badge if a preview was drawn.
-            if (mBadge != null) {
-                mBadge.draw(canvas);
-            }
+            mDrawable.setBounds(getBitmapBounds());
+            mDrawable.draw(canvas);
         }
     }
 
@@ -91,11 +82,11 @@
     private void updateDstRectF() {
         float myWidth = getWidth();
         float myHeight = getHeight();
-        float bitmapWidth = mBitmap.getWidth();
+        float bitmapWidth = mDrawable.getIntrinsicWidth();
 
         final float scale = bitmapWidth > myWidth ? myWidth / bitmapWidth : 1;
         float scaledWidth = bitmapWidth * scale;
-        float scaledHeight = mBitmap.getHeight() * scale;
+        float scaledHeight = mDrawable.getIntrinsicHeight() * scale;
 
         mDstRectF.left = (myWidth - scaledWidth) / 2;
         mDstRectF.right = (myWidth + scaledWidth) / 2;
@@ -107,17 +98,6 @@
             mDstRectF.top = (myHeight - scaledHeight) / 2;
             mDstRectF.bottom = (myHeight + scaledHeight) / 2;
         }
-
-        if (mBadge != null) {
-            Rect bounds = mBadge.getBounds();
-            int left = Utilities.boundToRange(
-                    (int) (mDstRectF.right + mBadgeMargin - bounds.width()),
-                    mBadgeMargin, getWidth() - bounds.width());
-            int top = Utilities.boundToRange(
-                    (int) (mDstRectF.bottom + mBadgeMargin - bounds.height()),
-                    mBadgeMargin, getHeight() - bounds.height());
-            mBadge.setBounds(left, top, bounds.width() + left, bounds.height() + top);
-        }
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/WidgetListRowEntry.java b/src/com/android/launcher3/widget/WidgetListRowEntry.java
deleted file mode 100644
index 17e4673..0000000
--- a/src/com/android/launcher3/widget/WidgetListRowEntry.java
+++ /dev/null
@@ -1,48 +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.widget;
-
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.PackageItemInfo;
-
-import java.util.ArrayList;
-
-/**
- * Holder class to store all the information related to a single row in the widget list
- */
-public class WidgetListRowEntry {
-
-    public final PackageItemInfo pkgItem;
-
-    public final ArrayList<WidgetItem> widgets;
-
-    /**
-     * Character that is used as a section name for the {@link ItemInfo#title}.
-     * (e.g., "G" will be stored if title is "Google")
-     */
-    public String titleSectionName;
-
-    public WidgetListRowEntry(PackageItemInfo pkgItem, ArrayList<WidgetItem> items) {
-        this.pkgItem = pkgItem;
-        this.widgets = items;
-    }
-
-    @Override
-    public String toString() {
-        return pkgItem.packageName + ":" + widgets.size();
-    }
-}
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index 4b6c569..15fa844 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -23,24 +23,18 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Process;
 import android.os.UserHandle;
 
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 
 import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -84,22 +78,8 @@
             return allWidgetsSteam(mContext).collect(Collectors.toList());
         }
 
-        if (Utilities.ATLEAST_OREO) {
-            return mAppWidgetManager.getInstalledProvidersForPackage(
-                    packageUser.mPackageName, packageUser.mUser);
-        }
-
-        String pkg = packageUser.mPackageName;
-        return Stream.concat(
-                // Only get providers for the given package/user.
-                mAppWidgetManager.getInstalledProvidersForProfile(packageUser.mUser)
-                        .stream()
-                        .filter(w -> w.provider.equals(pkg)),
-                Process.myUserHandle().equals(packageUser.mUser)
-                        && mContext.getPackageName().equals(pkg)
-                        ? CustomWidgetManager.INSTANCE.get(mContext).stream()
-                        : Stream.empty())
-                .collect(Collectors.toList());
+        return mAppWidgetManager.getInstalledProvidersForPackage(
+                packageUser.mPackageName, packageUser.mUser);
     }
 
     /**
@@ -138,15 +118,6 @@
                 appWidgetId).getBoolean(WIDGET_OPTION_RESTORE_COMPLETED);
     }
 
-    public static Map<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap(Context context) {
-        if (WidgetsModel.GO_DISABLE_WIDGETS) {
-            return Collections.emptyMap();
-        }
-        return allWidgetsSteam(context).collect(
-                        Collectors.toMap(info -> new ComponentKey(info.provider, info.getProfile()),
-                        Function.identity()));
-    }
-
     private static Stream<AppWidgetProviderInfo> allWidgetsSteam(Context context) {
         AppWidgetManager awm = context.getSystemService(AppWidgetManager.class);
         return Stream.concat(
diff --git a/src/com/android/launcher3/widget/WidgetPreviewLoader.java b/src/com/android/launcher3/widget/WidgetPreviewLoader.java
new file mode 100644
index 0000000..ff5c82f
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetPreviewLoader.java
@@ -0,0 +1,47 @@
+/*
+ * 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/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 30be7a6..dedcc65 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 
 import android.animation.PropertyValuesHolder;
@@ -26,26 +27,31 @@
 import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
+import android.widget.ScrollView;
+import android.widget.TableLayout;
+import android.widget.TableRow;
 import android.widget.TextView;
 
-import com.android.launcher3.Insettable;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.util.WidgetsTableUtils;
 
 import java.util.List;
 
 /**
  * Bottom sheet for the "Widgets" system shortcut in the long-press popup.
  */
-public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
+public class WidgetsBottomSheet extends BaseWidgetSheet {
+    private static final String TAG = "WidgetsBottomSheet";
 
     private static final IntProperty<View> PADDING_BOTTOM =
             new IntProperty<View>("paddingBottom") {
@@ -62,8 +68,40 @@
             };
 
     private static final int DEFAULT_CLOSE_DURATION = 200;
+    private static final long EDUCATION_TIP_DELAY_MS = 300;
+
     private ItemInfo mOriginalItemInfo;
-    private Rect mInsets;
+    private final int mMaxTableHeight;
+    private int mMaxHorizontalSpan = 4;
+
+    private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
+            new OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    if (hasSeenEducationTip()) {
+                        removeOnLayoutChangeListener(this);
+                        return;
+                    }
+                    // Widgets are loaded asynchronously, We are adding a delay because we only want
+                    // to show the tip when the widget preview has finished loading and rendering in
+                    // this view.
+                    removeCallbacks(mShowEducationTipTask);
+                    postDelayed(mShowEducationTipTask, EDUCATION_TIP_DELAY_MS);
+                }
+            };
+
+    private final Runnable mShowEducationTipTask = () -> {
+        if (hasSeenEducationTip()) {
+            removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+            return;
+        }
+        View viewForTip = ((ViewGroup) ((TableLayout) findViewById(R.id.widgets_table))
+                                    .getChildAt(0)).getChildAt(0);
+        if (showEducationTipOnViewIfPossible(viewForTip) != null) {
+            removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+        }
+    };
 
     public WidgetsBottomSheet(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -72,20 +110,73 @@
     public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setWillNotDraw(false);
-        mInsets = new Rect();
-        mContent = this;
+        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);
+        }
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mContent = findViewById(R.id.widgets_bottom_sheet);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        doMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (updateMaxSpansPerRow()) {
+            doMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    /** Returns {@code true} if the max spans have been updated. */
+    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);
+        if (mMaxHorizontalSpan != maxHorizontalSpan) {
+            // Ensure the table layout is showing widgets in the right column after measure.
+            mMaxHorizontalSpan = maxHorizontalSpan;
+            onWidgetsBound();
+            return true;
+        }
+        return false;
     }
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
+        int width = r - l;
+        int height = b - t;
+
+        // Content is laid out 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);
+
+        // 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);
+            findViewById(R.id.collapse_handle).setVisibility(VISIBLE);
+        }
     }
 
     public void populateAndShow(ItemInfo itemInfo) {
         mOriginalItemInfo = itemInfo;
-        ((TextView) findViewById(R.id.title)).setText(getContext().getString(
-                R.string.widgets_bottom_sheet_title, mOriginalItemInfo.title));
+        ((TextView) findViewById(R.id.title)).setText(mOriginalItemInfo.title);
 
         onWidgetsBound();
         attachToContainer();
@@ -95,53 +186,51 @@
 
     @Override
     public void onWidgetsBound() {
-        List<WidgetItem> widgets = mLauncher.getPopupDataProvider().getWidgetsForPackageUser(
+        List<WidgetItem> widgets = mActivityContext.getPopupDataProvider().getWidgetsForPackageUser(
                 new PackageUserKey(
                         mOriginalItemInfo.getTargetComponent().getPackageName(),
                         mOriginalItemInfo.user));
 
-        ViewGroup widgetRow = findViewById(R.id.widgets);
-        ViewGroup widgetCells = widgetRow.findViewById(R.id.widgets_cell_list);
+        TableLayout widgetsTable = findViewById(R.id.widgets_table);
+        widgetsTable.removeAllViews();
 
-        widgetCells.removeAllViews();
-
-        for (int i = 0; i < widgets.size(); i++) {
-            WidgetCell widget = addItemCell(widgetCells);
-            widget.applyFromCellItem(widgets.get(i), LauncherAppState.getInstance(mLauncher)
-                    .getWidgetCache());
-            widget.ensurePreview();
-            widget.setVisibility(View.VISIBLE);
-            if (i < widgets.size() - 1) {
-                addDivider(widgetCells);
-            }
-        }
-
-        if (widgets.size() == 1) {
-            // If there is only one widget, we want to center it instead of left-align.
-            WidgetsBottomSheet.LayoutParams params = (WidgetsBottomSheet.LayoutParams)
-                    widgetRow.getLayoutParams();
-            params.gravity = Gravity.CENTER_HORIZONTAL;
-        } else {
-            // Otherwise, add an empty view to the start as padding (but still scroll edge to edge).
-            View leftPaddingView = LayoutInflater.from(getContext()).inflate(
-                    R.layout.widget_list_divider, widgetRow, false);
-            leftPaddingView.getLayoutParams().width = ResourceUtils.pxFromDp(
-                    16, getResources().getDisplayMetrics());
-            widgetCells.addView(leftPaddingView, 0);
-        }
+        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);
+        });
     }
 
-    private void addDivider(ViewGroup parent) {
-        LayoutInflater.from(getContext()).inflate(R.layout.widget_list_divider, parent, true);
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mNoIntercept = false;
+            ScrollView scrollView = findViewById(R.id.widgets_table_scroll_view);
+            if (getPopupContainer().isEventOverView(scrollView, ev)
+                    && scrollView.getScrollY() > 0) {
+                mNoIntercept = true;
+            }
+        }
+        return super.onControllerInterceptTouchEvent(ev);
     }
 
     protected WidgetCell addItemCell(ViewGroup parent) {
-        WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext()).inflate(
-                R.layout.widget_cell, parent, false);
+        WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
+                .inflate(R.layout.widget_cell, parent, false);
 
-        widget.setOnClickListener(this);
-        widget.setOnLongClickListener(this);
+        View previewContainer = widget.findViewById(R.id.widget_preview_container);
+        previewContainer.setOnClickListener(this);
+        previewContainer.setOnLongClickListener(this);
         widget.setAnimatePreview(false);
+        widget.setSourceContainer(CONTAINER_BOTTOM_WIDGETS_TRAY);
 
         parent.addView(widget);
         return widget;
@@ -171,17 +260,15 @@
 
     @Override
     public void setInsets(Rect insets) {
-        // Extend behind left, right, and bottom insets.
-        int leftInset = insets.left - mInsets.left;
-        int rightInset = insets.right - mInsets.right;
-        int bottomInset = insets.bottom - mInsets.bottom;
-        mInsets.set(insets);
-        setPadding(leftInset, getPaddingTop(), rightInset, bottomInset);
-    }
+        super.setInsets(insets);
 
-    @Override
-    protected int getElementsRowCount() {
-        return 1;
+        mContent.setPadding(mContent.getPaddingStart(),
+                mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
+        if (insets.bottom > 0) {
+            setupNavBarColor();
+        } else {
+            clearNavBarColor();
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
deleted file mode 100644
index df6e2c3..0000000
--- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.widget;
-
-import android.util.Log;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-/**
- * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
- * methods accordingly.
- */
-public class WidgetsDiffReporter {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "WidgetsDiffReporter";
-
-    private final IconCache mIconCache;
-    private final RecyclerView.Adapter mListener;
-
-    public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) {
-        mIconCache = iconCache;
-        mListener = listener;
-    }
-
-    public void process(ArrayList<WidgetListRowEntry> currentEntries,
-            ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator) {
-        if (DEBUG) {
-            Log.d(TAG, "process oldEntries#=" + currentEntries.size()
-                    + " newEntries#=" + newEntries.size());
-        }
-        // Early exit if either of the list is empty
-        if (currentEntries.isEmpty() || newEntries.isEmpty()) {
-            // Skip if both list are empty.
-            // On rotation, we open the widget tray with empty. Then try to fetch the list again
-            // when the animation completes (which still gives empty). And we get the final result
-            // when the bind actually completes.
-            if (currentEntries.size() != newEntries.size()) {
-                currentEntries.clear();
-                currentEntries.addAll(newEntries);
-                mListener.notifyDataSetChanged();
-            }
-            return;
-        }
-        ArrayList<WidgetListRowEntry> orgEntries =
-                (ArrayList<WidgetListRowEntry>) currentEntries.clone();
-        Iterator<WidgetListRowEntry> orgIter = orgEntries.iterator();
-        Iterator<WidgetListRowEntry> newIter = newEntries.iterator();
-
-        WidgetListRowEntry orgRowEntry = orgIter.next();
-        WidgetListRowEntry newRowEntry = newIter.next();
-
-        do {
-            int diff = comparePackageName(orgRowEntry, newRowEntry, comparator);
-            if (DEBUG) {
-                Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
-                        diff, orgRowEntry != null? orgRowEntry.toString() : null,
-                        newRowEntry != null? newRowEntry.toString() : null));
-            }
-            int index = -1;
-            if (diff < 0) {
-                index = currentEntries.indexOf(orgRowEntry);
-                mListener.notifyItemRemoved(index);
-                if (DEBUG) {
-                    Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
-                            orgRowEntry.titleSectionName));
-                }
-                currentEntries.remove(index);
-                orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
-            } else if (diff > 0) {
-                index = orgRowEntry != null? currentEntries.indexOf(orgRowEntry):
-                        currentEntries.size();
-                currentEntries.add(index, newRowEntry);
-                if (DEBUG) {
-                    Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
-                            newRowEntry.titleSectionName));
-                }
-                newRowEntry = newIter.hasNext() ? newIter.next() : null;
-                mListener.notifyItemInserted(index);
-
-            } else {
-                // same package name but,
-                // did the icon, title, etc, change?
-                // or did the widget size and desc, span, etc change?
-                if (!isSamePackageItemInfo(orgRowEntry.pkgItem, newRowEntry.pkgItem) ||
-                        !orgRowEntry.widgets.equals(newRowEntry.widgets)) {
-                    index = currentEntries.indexOf(orgRowEntry);
-                    currentEntries.set(index, newRowEntry);
-                    mListener.notifyItemChanged(index);
-                    if (DEBUG) {
-                        Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
-                                newRowEntry.titleSectionName));
-                    }
-                }
-                orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
-                newRowEntry = newIter.hasNext() ? newIter.next() : null;
-            }
-        } while(orgRowEntry != null || newRowEntry != null);
-    }
-
-    /**
-     * Compare package name using the same comparator as in {@link WidgetsListAdapter}.
-     * Also handle null row pointers.
-     */
-    private int comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow,
-            WidgetListRowEntryComparator comparator) {
-        if (curRow == null && newRow == null) {
-            throw new IllegalStateException("Cannot compare PackageItemInfo if both rows are null.");
-        }
-
-        if (curRow == null && newRow != null) {
-            return 1; // new row needs to be inserted
-        } else if (curRow != null && newRow == null) {
-            return -1; // old row needs to be deleted
-        }
-        return comparator.compare(curRow, newRow);
-    }
-
-    private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
-        return curInfo.bitmap.icon.equals(newInfo.bitmap.icon)
-                && !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user);
-    }
-}
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
deleted file mode 100644
index 68a3ec5..0000000
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.widget;
-
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.views.RecyclerViewFastScroller;
-import com.android.launcher3.views.TopRoundedCornerView;
-
-/**
- * Popup for showing the full list of available widgets
- */
-public class WidgetsFullSheet extends BaseWidgetSheet
-        implements Insettable, ProviderChangedListener {
-
-    private static final long DEFAULT_OPEN_DURATION = 267;
-    private static final long FADE_IN_DURATION = 150;
-    private static final float VERTICAL_START_POSITION = 0.3f;
-
-    private final Rect mInsets = new Rect();
-
-    private final WidgetsListAdapter mAdapter;
-
-    private WidgetsRecyclerView mRecyclerView;
-
-    public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        LauncherAppState apps = LauncherAppState.getInstance(context);
-        mAdapter = new WidgetsListAdapter(context,
-                LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(),
-                this, this);
-
-    }
-
-    public WidgetsFullSheet(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mContent = findViewById(R.id.container);
-
-        mRecyclerView = findViewById(R.id.widgets_list_view);
-        mRecyclerView.setAdapter(mAdapter);
-        mAdapter.setApplyBitmapDeferred(true, mRecyclerView);
-
-        TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
-        springLayout.addSpringView(R.id.widgets_list_view);
-        mRecyclerView.setEdgeEffectFactory(springLayout.createEdgeEffectFactory());
-        onWidgetsBound();
-    }
-
-    @VisibleForTesting
-    public WidgetsRecyclerView getRecyclerView() {
-        return mRecyclerView;
-    }
-
-    @Override
-    protected Pair<View, String> getAccessibilityTarget() {
-        return Pair.create(mRecyclerView, getContext().getString(
-                mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mLauncher.getAppWidgetHost().addProviderChangeListener(this);
-        notifyWidgetProvidersChanged();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        mInsets.set(insets);
-
-        mRecyclerView.setPadding(
-                mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
-                mRecyclerView.getPaddingRight(), insets.bottom);
-        if (insets.bottom > 0) {
-            setupNavBarColor();
-        } else {
-            clearNavBarColor();
-        }
-
-        ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
-        requestLayout();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int widthUsed;
-        if (mInsets.bottom > 0) {
-            widthUsed = mInsets.left + mInsets.right;
-        } else {
-            Rect padding = mLauncher.getDeviceProfile().workspacePadding;
-            widthUsed = Math.max(padding.left + padding.right,
-                    2 * (mInsets.left + mInsets.right));
-        }
-
-        int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx;
-        measureChildWithMargins(mContent, widthMeasureSpec,
-                widthUsed, heightMeasureSpec, heightUsed);
-        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
-                MeasureSpec.getSize(heightMeasureSpec));
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        int width = r - l;
-        int height = b - t;
-
-        // Content is laid out 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);
-    }
-
-    @Override
-    public void notifyWidgetProvidersChanged() {
-        mLauncher.refreshAndBindWidgetsForPackageUser(null);
-    }
-
-    @Override
-    public void onWidgetsBound() {
-        mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets());
-    }
-
-    private void open(boolean animate) {
-        if (animate) {
-            if (getPopupContainer().getInsets().bottom > 0) {
-                mContent.setAlpha(0);
-                setTranslationShift(VERTICAL_START_POSITION);
-            }
-            mOpenCloseAnimator.setValues(
-                    PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
-            mOpenCloseAnimator
-                    .setDuration(DEFAULT_OPEN_DURATION)
-                    .setInterpolator(AnimationUtils.loadInterpolator(
-                            getContext(), android.R.interpolator.linear_out_slow_in));
-            mRecyclerView.setLayoutFrozen(true);
-            mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mRecyclerView.setLayoutFrozen(false);
-                    mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
-                    mOpenCloseAnimator.removeListener(this);
-                }
-            });
-            post(() -> {
-                mOpenCloseAnimator.start();
-                mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
-            });
-        } else {
-            setTranslationShift(TRANSLATION_SHIFT_OPENED);
-            mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
-            post(this::announceAccessibilityChanges);
-        }
-    }
-
-    @Override
-    protected void handleClose(boolean animate) {
-        handleClose(animate, DEFAULT_OPEN_DURATION);
-    }
-
-    @Override
-    protected boolean isOfType(int type) {
-        return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
-    }
-
-    @Override
-    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        // Disable swipe down when recycler view is scrolling
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mNoIntercept = false;
-            RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
-            if (scroller.getThumbOffsetY() >= 0 &&
-                    getPopupContainer().isEventOverView(scroller, ev)) {
-                mNoIntercept = true;
-            } else if (getPopupContainer().isEventOverView(mContent, ev)) {
-                mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, getPopupContainer());
-            }
-        }
-        return super.onControllerInterceptTouchEvent(ev);
-    }
-
-    public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
-        WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
-                .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
-        sheet.attachToContainer();
-        sheet.mIsOpen = true;
-        sheet.open(animate);
-        return sheet;
-    }
-
-    @VisibleForTesting
-    public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
-        return launcher.findViewById(R.id.widgets_list_view);
-    }
-
-    @Override
-    protected int getElementsRowCount() {
-        return mAdapter.getItemCount();
-    }
-
-    @Override
-    public void addHintCloseAnim(
-            float distanceToMove, Interpolator interpolator, PendingAnimation target) {
-        target.setFloat(mRecyclerView, VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
-        target.setViewAlpha(mRecyclerView, 0.5f, interpolator);
-    }
-
-    @Override
-    protected void onCloseComplete() {
-        super.onCloseComplete();
-        AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
-    }
-}
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
deleted file mode 100644
index a45521d..0000000
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ /dev/null
@@ -1,219 +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.
- */
-package com.android.launcher3.widget;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.R;
-import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.util.LabelComparator;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-
-/**
- * List view adapter for the widget tray.
- *
- * <p>Memory vs. Performance:
- * The less number of types of views are inserted into a {@link RecyclerView}, the more recycling
- * happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is
- * only a single type of view.
- */
-public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
-
-    private static final String TAG = "WidgetsListAdapter";
-    private static final boolean DEBUG = false;
-
-    private final WidgetPreviewLoader mWidgetPreviewLoader;
-    private final LayoutInflater mLayoutInflater;
-
-    private final OnClickListener mIconClickListener;
-    private final OnLongClickListener mIconLongClickListener;
-    private final int mIndent;
-    private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
-    private final WidgetsDiffReporter mDiffReporter;
-
-    private boolean mApplyBitmapDeferred;
-
-    public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
-            WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
-            OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
-        mLayoutInflater = layoutInflater;
-        mWidgetPreviewLoader = widgetPreviewLoader;
-        mIconClickListener = iconClickListener;
-        mIconLongClickListener = iconLongClickListener;
-        mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
-        mDiffReporter = new WidgetsDiffReporter(iconCache, this);
-    }
-
-    /**
-     * Defers applying bitmap on all the {@link WidgetCell} in the {@param rv}
-     *
-     * @see WidgetCell#setApplyBitmapDeferred(boolean)
-     */
-    public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) {
-        mApplyBitmapDeferred = isDeferred;
-
-        for (int i = rv.getChildCount() - 1; i >= 0; i--) {
-            WidgetsRowViewHolder holder = (WidgetsRowViewHolder)
-                    rv.getChildViewHolder(rv.getChildAt(i));
-            for (int j = holder.cellContainer.getChildCount() - 1; j >= 0; j--) {
-                View v = holder.cellContainer.getChildAt(j);
-                if (v instanceof WidgetCell) {
-                    ((WidgetCell) v).setApplyBitmapDeferred(mApplyBitmapDeferred);
-                }
-            }
-        }
-    }
-
-    /**
-     * Update the widget list.
-     */
-    public void setWidgets(ArrayList<WidgetListRowEntry> tempEntries) {
-        WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator();
-        Collections.sort(tempEntries, rowComparator);
-        mDiffReporter.process(mEntries, tempEntries, rowComparator);
-    }
-
-    @Override
-    public int getItemCount() {
-        return mEntries.size();
-    }
-
-    public String getSectionName(int pos) {
-        return mEntries.get(pos).titleSectionName;
-    }
-
-    @Override
-    public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
-        WidgetListRowEntry entry = mEntries.get(pos);
-        List<WidgetItem> infoList = entry.widgets;
-
-        ViewGroup row = holder.cellContainer;
-        if (DEBUG) {
-            Log.d(TAG, String.format(
-                    "onBindViewHolder [pos=%d, widget#=%d, row.getChildCount=%d]",
-                    pos, infoList.size(), row.getChildCount()));
-        }
-
-        // Add more views.
-        // if there are too many, hide them.
-        int expectedChildCount = infoList.size() + Math.max(0, infoList.size() - 1);
-        int childCount = row.getChildCount();
-
-        if (expectedChildCount > childCount) {
-            for (int i = childCount ; i < expectedChildCount; i++) {
-                if ((i & 1) == 1) {
-                    // Add a divider for odd index
-                    mLayoutInflater.inflate(R.layout.widget_list_divider, row);
-                } else {
-                    // Add cell for even index
-                    WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
-                            R.layout.widget_cell, row, false);
-
-                    // set up touch.
-                    widget.setOnClickListener(mIconClickListener);
-                    widget.setOnLongClickListener(mIconLongClickListener);
-                    row.addView(widget);
-                }
-            }
-        } else if (expectedChildCount < childCount) {
-            for (int i = expectedChildCount ; i < childCount; i++) {
-                row.getChildAt(i).setVisibility(View.GONE);
-            }
-        }
-
-        // Bind the views in the application info section.
-        holder.title.applyFromPackageItemInfo(entry.pkgItem);
-
-        // Bind the view in the widget horizontal tray region.
-        for (int i=0; i < infoList.size(); i++) {
-            WidgetCell widget = (WidgetCell) row.getChildAt(2*i);
-            widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader);
-            widget.setApplyBitmapDeferred(mApplyBitmapDeferred);
-            widget.ensurePreview();
-            widget.setVisibility(View.VISIBLE);
-
-            if (i > 0) {
-                row.getChildAt(2*i - 1).setVisibility(View.VISIBLE);
-            }
-        }
-    }
-
-    @Override
-    public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        if (DEBUG) {
-            Log.v(TAG, "\nonCreateViewHolder");
-        }
-
-        ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
-                R.layout.widgets_list_row_view, parent, false);
-
-        // if the end padding is 0, then container view (horizontal scroll view) doesn't respect
-        // the end of the linear layout width + the start padding and doesn't allow scrolling.
-        container.findViewById(R.id.widgets_cell_list).setPaddingRelative(mIndent, 0, 1, 0);
-
-        return new WidgetsRowViewHolder(container);
-    }
-
-    @Override
-    public void onViewRecycled(WidgetsRowViewHolder holder) {
-        int total = holder.cellContainer.getChildCount();
-        for (int i = 0; i < total; i+=2) {
-            WidgetCell widget = (WidgetCell) holder.cellContainer.getChildAt(i);
-            widget.clear();
-        }
-    }
-
-    public boolean onFailedToRecycleView(WidgetsRowViewHolder holder) {
-        // If child views are animating, then the RecyclerView may choose not to recycle the view,
-        // causing extraneous onCreateViewHolder() calls.  It is safe in this case to continue
-        // recycling this view, and take care in onViewRecycled() to cancel any existing
-        // animations.
-        return true;
-    }
-
-    @Override
-    public long getItemId(int pos) {
-        return pos;
-    }
-
-    /**
-     * Comparator for sorting WidgetListRowEntry based on package title
-     */
-    public static class WidgetListRowEntryComparator implements Comparator<WidgetListRowEntry> {
-
-        private final LabelComparator mComparator = new LabelComparator();
-
-        @Override
-        public int compare(WidgetListRowEntry a, WidgetListRowEntry b) {
-            return mComparator.compare(a.pkgItem.title.toString(), b.pkgItem.title.toString());
-        }
-    }
-}
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
deleted file mode 100644
index 82d4110..0000000
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ /dev/null
@@ -1,176 +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.
- */
-
-package com.android.launcher3.widget;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-
-import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
-
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
-
-/**
- * The widgets recycler view.
- */
-public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouchListener {
-
-    private WidgetsListAdapter mAdapter;
-
-    private final int mScrollbarTop;
-
-    private final Point mFastScrollerOffset = new Point();
-    private boolean mTouchDownOnScroller;
-
-    public WidgetsRecyclerView(Context context) {
-        this(context, null);
-    }
-
-    public WidgetsRecyclerView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
-        // API 21 and below only support 3 parameter ctor.
-        super(context, attrs, defStyleAttr);
-        mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
-        addOnItemTouchListener(this);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        // create a layout manager with Launcher's context so that scroll position
-        // can be preserved during screen rotation.
-        setLayoutManager(new LinearLayoutManager(getContext()));
-    }
-
-    @Override
-    public void setAdapter(Adapter adapter) {
-        super.setAdapter(adapter);
-        mAdapter = (WidgetsListAdapter) adapter;
-    }
-
-    /**
-     * Maps the touch (from 0..1) to the adapter position that should be visible.
-     */
-    @Override
-    public String scrollToPositionAtProgress(float touchFraction) {
-        // Skip early if widgets are not bound.
-        if (isModelNotReady()) {
-            return "";
-        }
-
-        // Stop the scroller if it is scrolling
-        stopScroll();
-
-        int rowCount = mAdapter.getItemCount();
-        float pos = rowCount * touchFraction;
-        int availableScrollHeight = getAvailableScrollHeight();
-        LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
-        layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
-
-        int posInt = (int) ((touchFraction == 1)? pos -1 : pos);
-        return mAdapter.getSectionName(posInt);
-    }
-
-    /**
-     * Updates the bounds for the scrollbar.
-     */
-    @Override
-    public void onUpdateScrollbar(int dy) {
-        // Skip early if widgets are not bound.
-        if (isModelNotReady()) {
-            return;
-        }
-
-        // Skip early if, there no child laid out in the container.
-        int scrollY = getCurrentScrollY();
-        if (scrollY < 0) {
-            mScrollbar.setThumbOffsetY(-1);
-            return;
-        }
-
-        synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight());
-    }
-
-    @Override
-    public int getCurrentScrollY() {
-        // Skip early if widgets are not bound.
-        if (isModelNotReady() || getChildCount() == 0) {
-            return -1;
-        }
-
-        View child = getChildAt(0);
-        int rowIndex = getChildPosition(child);
-        int y = (child.getMeasuredHeight() * rowIndex);
-        int offset = getLayoutManager().getDecoratedTop(child);
-
-        return getPaddingTop() + y - offset;
-    }
-
-    /**
-     * Returns the available scroll height:
-     *   AvailableScrollHeight = Total height of the all items - last page height
-     */
-    @Override
-    protected int getAvailableScrollHeight() {
-        View child = getChildAt(0);
-        return child.getMeasuredHeight() * mAdapter.getItemCount() - getScrollbarTrackHeight()
-                - mScrollbarTop;
-    }
-
-    private boolean isModelNotReady() {
-        return mAdapter.getItemCount() == 0;
-    }
-
-    @Override
-    public int getScrollBarTop() {
-        return mScrollbarTop;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
-        if (e.getAction() == MotionEvent.ACTION_DOWN) {
-            mTouchDownOnScroller =
-                    mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
-        }
-        if (mTouchDownOnScroller) {
-            return mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
-        }
-        return false;
-    }
-
-    @Override
-    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
-        if (mTouchDownOnScroller) {
-            mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
-        }
-    }
-
-    @Override
-    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
index 1086987..8b3bbce 100644
--- a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
@@ -22,8 +22,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
 /**
  * Custom app widget provider info that can be used as a widget, but provide extra functionality
@@ -57,7 +58,7 @@
     }
 
     @Override
-    public void initSpans(Context context) { }
+    public void initSpans(Context context, InvariantDeviceProfile idp) { }
 
     @Override
     public String getLabel(PackageManager packageManager) {
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
index 0b66ec0..329a444 100644
--- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.widget.custom;
 
-import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
+import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
 
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -29,12 +29,12 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.systemui.plugins.CustomWidgetPlugin;
 import com.android.systemui.plugins.PluginListener;
 
diff --git a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
new file mode 100644
index 0000000..4a60983
--- /dev/null
+++ b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
@@ -0,0 +1,57 @@
+/*
+ * 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.dragndrop;
+
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+
+/** 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) {
+        mLauncher = launcher;
+    }
+
+    @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);
+        } else {
+            mLauncher.getDragController().removeDragListener(this);
+        }
+    }
+
+    @Override
+    public void onDragEnd() {
+        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/WidgetsListBaseEntry.java b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
new file mode 100644
index 0000000..abc79ff
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
@@ -0,0 +1,83 @@
+/*
+ * 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 static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.widget.WidgetItemComparator;
+
+import java.lang.annotation.Retention;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Holder class to store the package information of an entry shown in the widgets list. */
+public abstract class WidgetsListBaseEntry {
+    public final PackageItemInfo mPkgItem;
+
+    /**
+     * Character that is used as a section name for the {@link ItemInfo#title}.
+     * (e.g., "G" will be stored if title is "Google")
+     */
+    public final String mTitleSectionName;
+
+    public final List<WidgetItem> mWidgets;
+
+    public WidgetsListBaseEntry(PackageItemInfo pkgItem, String titleSectionName,
+            List<WidgetItem> items) {
+        mPkgItem = pkgItem;
+        mTitleSectionName = titleSectionName;
+        this.mWidgets =
+                items.stream().sorted(new WidgetItemComparator()).collect(Collectors.toList());
+    }
+
+    /**
+     * Returns the ranking of this entry in the
+     * {@link com.android.launcher3.widget.picker.WidgetsListAdapter}.
+     *
+     * <p>Entries with smaller value should be shown first. See
+     * {@link com.android.launcher3.widget.picker.WidgetsDiffReporter} for more details.
+     */
+    @Rank
+    public abstract int getRank();
+
+    /**
+     * Marker interface for subclasses that are headers for widget list items.
+     *
+     * @param <T> The type of this class.
+     */
+    public interface Header<T extends WidgetsListBaseEntry & Header<T>> {
+        /** Returns whether the widget list is currently expanded. */
+        boolean isWidgetListShown();
+
+        /** Returns a copy of the item with the widget list shown. */
+        T withWidgetListShown();
+    }
+
+    @Retention(SOURCE)
+    @IntDef({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;
+}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
new file mode 100644
index 0000000..73b17f1
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
@@ -0,0 +1,102 @@
+/*
+ * 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.widget.model;
+
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.List;
+
+/**
+ * Holder class to store all the information related to a list of widgets from the same app which is
+ * shown in the {@link com.android.launcher3.widget.picker.WidgetsFullSheet}.
+ */
+public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
+
+    private final int mMaxSpanSizeInCells;
+
+    /**
+     * Constructor for {@link WidgetsListContentEntry}.
+     *
+     * @param pkgItem package info associated with the entry
+     * @param titleSectionName title section name associated with the entry.
+     * @param items list of widgets for the package.
+     */
+    public WidgetsListContentEntry(PackageItemInfo pkgItem, String titleSectionName,
+            List<WidgetItem> items) {
+        this(pkgItem, titleSectionName, items, /* maxSpanSizeInCells= */ 0);
+    }
+
+    /**
+     * Constructor for {@link WidgetsListContentEntry}.
+     *
+     * @param pkgItem package info associated with the entry
+     * @param titleSectionName title section name associated with the entry.
+     * @param items list of widgets for the package.
+     * @param maxSpanSizeInCells the max horizontal span in cells that is allowed for grouping more
+     *                           than one widgets in a table row.
+     */
+    public WidgetsListContentEntry(PackageItemInfo pkgItem, String titleSectionName,
+            List<WidgetItem> items, int maxSpanSizeInCells) {
+        super(pkgItem, titleSectionName, items);
+        mMaxSpanSizeInCells = maxSpanSizeInCells;
+    }
+
+    @Override
+    public String toString() {
+        return "Content:" + mPkgItem.packageName + ":" + mWidgets.size() + " maxSpanSizeInCells: "
+                + mMaxSpanSizeInCells;
+    }
+
+    @Override
+    @Rank
+    public int getRank() {
+        return RANK_WIDGETS_LIST_CONTENT;
+    }
+
+    /**
+     * Returns a copy of this {@link WidgetsListContentEntry} with updated
+     * {@param maxSpanSizeInCells}.
+     *
+     * @param maxSpanSizeInCells the maximum horizontal span in cells that is allowed for grouping
+     *                           more than one widgets in a table row.
+     */
+    public WidgetsListContentEntry withMaxSpanSize(int maxSpanSizeInCells) {
+        if (mMaxSpanSizeInCells == maxSpanSizeInCells) return this;
+        return new WidgetsListContentEntry(
+                mPkgItem,
+                mTitleSectionName,
+                mWidgets,
+                /* maxSpanSizeInCells= */ maxSpanSizeInCells);
+    }
+
+    /**
+     * Returns the max horizontal span size in cells that is allowed for grouping more than one
+     * widget in a table row.
+     */
+    public int getMaxSpanSizeInCells() {
+        return mMaxSpanSizeInCells;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof WidgetsListContentEntry)) return false;
+        WidgetsListContentEntry otherEntry = (WidgetsListContentEntry) obj;
+        return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
+                && mTitleSectionName.equals(otherEntry.mTitleSectionName)
+                && mMaxSpanSizeInCells == otherEntry.mMaxSpanSizeInCells;
+    }
+}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
new file mode 100644
index 0000000..5b3ea94
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
@@ -0,0 +1,81 @@
+/*
+ * 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 com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.List;
+
+/** An information holder for an app which has widgets or/and shortcuts. */
+public final class WidgetsListHeaderEntry extends WidgetsListBaseEntry
+        implements WidgetsListBaseEntry.Header<WidgetsListHeaderEntry> {
+
+    public final int widgetsCount;
+    public final int shortcutsCount;
+
+    private final boolean mIsWidgetListShown;
+
+    public WidgetsListHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
+            List<WidgetItem> items) {
+        this(pkgItem, titleSectionName, items, /* isWidgetListShown= */ false);
+    }
+
+    private WidgetsListHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
+            List<WidgetItem> items, boolean isWidgetListShown) {
+        super(pkgItem, titleSectionName, items);
+        widgetsCount = (int) items.stream().filter(item -> item.widgetInfo != null).count();
+        shortcutsCount = Math.max(0, items.size() - widgetsCount);
+        mIsWidgetListShown = isWidgetListShown;
+    }
+
+    /** Returns {@code true} if the widgets list associated with this header is shown. */
+    @Override
+    public boolean isWidgetListShown() {
+        return mIsWidgetListShown;
+    }
+
+    @Override
+    public String toString() {
+        return "Header:" + mPkgItem.packageName + ":" + mWidgets.size();
+    }
+
+    @Override
+    @Rank
+    public int getRank() {
+        return RANK_WIDGETS_LIST_HEADER;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof WidgetsListHeaderEntry)) return false;
+        WidgetsListHeaderEntry otherEntry = (WidgetsListHeaderEntry) obj;
+        return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
+                && mTitleSectionName.equals(otherEntry.mTitleSectionName)
+                && mIsWidgetListShown == otherEntry.mIsWidgetListShown;
+    }
+
+    /** Returns a copy of this {@link WidgetsListHeaderEntry} with the widget list shown. */
+    @Override
+    public WidgetsListHeaderEntry withWidgetListShown() {
+        if (mIsWidgetListShown) return this;
+        return new WidgetsListHeaderEntry(
+                mPkgItem,
+                mTitleSectionName,
+                mWidgets,
+                /* isWidgetListShown= */ true);
+    }
+}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListSearchHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListSearchHeaderEntry.java
new file mode 100644
index 0000000..055e4ec
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListSearchHeaderEntry.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.widget.model;
+
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.List;
+
+/** An information holder for an app which has widgets or/and shortcuts, to be shown in search. */
+public final class WidgetsListSearchHeaderEntry extends WidgetsListBaseEntry
+        implements WidgetsListBaseEntry.Header<WidgetsListSearchHeaderEntry> {
+
+    private final boolean mIsWidgetListShown;
+
+    public WidgetsListSearchHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
+            List<WidgetItem> items) {
+        this(pkgItem, titleSectionName, items, /* isWidgetListShown= */ false);
+    }
+
+    private WidgetsListSearchHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
+            List<WidgetItem> items, boolean isWidgetListShown) {
+        super(pkgItem, titleSectionName, items);
+        mIsWidgetListShown = isWidgetListShown;
+    }
+
+    /** Returns {@code true} if the widgets list associated with this header is shown. */
+    @Override
+    public boolean isWidgetListShown() {
+        return mIsWidgetListShown;
+    }
+
+    @Override
+    public String toString() {
+        return "SearchHeader:" + mPkgItem.packageName + ":" + mWidgets.size();
+    }
+
+    @Override
+    @Rank
+    public int getRank() {
+        return RANK_WIDGETS_LIST_SEARCH_HEADER;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof WidgetsListSearchHeaderEntry)) return false;
+        WidgetsListSearchHeaderEntry otherEntry = (WidgetsListSearchHeaderEntry) obj;
+        return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
+                && mTitleSectionName.equals(otherEntry.mTitleSectionName)
+                && mIsWidgetListShown == otherEntry.mIsWidgetListShown;
+    }
+
+    /** Returns a copy of this {@link WidgetsListSearchHeaderEntry} with the widget list shown. */
+    @Override
+    public WidgetsListSearchHeaderEntry withWidgetListShown() {
+        if (mIsWidgetListShown) return this;
+        return new WidgetsListSearchHeaderEntry(
+                mPkgItem,
+                mTitleSectionName,
+                mWidgets,
+                /* isWidgetListShown= */ true);
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/OnHeaderClickListener.java b/src/com/android/launcher3/widget/picker/OnHeaderClickListener.java
new file mode 100644
index 0000000..7372751
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/OnHeaderClickListener.java
@@ -0,0 +1,28 @@
+/*
+ * 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 com.android.launcher3.util.PackageUserKey;
+
+/**
+ * A listener to be invoked when a header is clicked.
+ */
+public interface OnHeaderClickListener {
+    /**
+     * Calls when a header is clicked to show / hide widgets for a package.
+     */
+    void onHeaderClicked(boolean showWidgets, PackageUserKey packageUserKey);
+}
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
new file mode 100644
index 0000000..6643779
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -0,0 +1,373 @@
+/*
+ * 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.animation.ValueAnimator;
+import android.graphics.Point;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.widget.picker.WidgetsFullSheet.SearchAndRecommendationViewHolder;
+import com.android.launcher3.workprofile.PersonalWorkPagedView;
+
+/**
+ * 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;
+
+    // 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 WidgetsRecyclerView mCurrentRecyclerView;
+    private int mCurrentRecyclerViewScrollY = 0;
+
+    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 boolean mShouldForwardToRecyclerView = false;
+
+    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);
+    }
+
+    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;
+        }
+        if (mCurrentRecyclerView != null) {
+            mCurrentRecyclerView.setOnContentChangeListener(null);
+        }
+        mCurrentRecyclerView = currentRecyclerView;
+        mCurrentRecyclerView.setOnContentChangeListener(mOnContentChangeListener);
+        reset(animateReset);
+    }
+
+    /**
+     * Updates padding of {@link WidgetsFullSheet} contents to include {@code bottomInset} wherever
+     * necessary.
+     */
+    public boolean updateBottomInset(int bottomInset) {
+        mBottomInset = bottomInset;
+        return updateMarginAndPadding();
+    }
+
+    /**
+     * Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
+     *
+     * @return {@code true} if margins or/and padding of views in the search and recommendations
+     * container have been updated.
+     */
+    public boolean updateMarginAndPadding() {
+        boolean hasMarginOrPaddingUpdated = false;
+        mCollapsibleHeightForSearch = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
+        mCollapsibleHeightForRecommendation =
+                measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
+                        + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
+                        + measureHeightWithVerticalMargins((View) mViewHolder.mSearchBarContainer)
+                        + measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
+
+        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);
+        }
+
+        if (mCollapsibleHeightForSearch > 0) {
+            int searchYDisplacement = Math.max(-mCurrentRecyclerViewScrollY,
+                    -mCollapsibleHeightForSearch);
+            mViewHolder.mSearchBarContainer.setTranslationY(searchYDisplacement);
+        }
+
+        if (mHasWorkProfile && mCollapsibleHeightForTabs > 0) {
+            int yDisplacementForTabs = Math.max(-mCurrentRecyclerViewScrollY,
+                    -mCollapsibleHeightForTabs);
+            mPrimaryWorkTabsView.setTranslationY(yDisplacementForTabs);
+        }
+    }
+
+    /** Resets any previous view translation. */
+    public void reset(boolean animate) {
+        if (mCurrentRecyclerViewScrollY == 0) {
+            return;
+        }
+        if (mAnimator.isStarted()) {
+            mAnimator.cancel();
+        }
+
+        if (animate) {
+            mAnimator.setIntValues(mCurrentRecyclerViewScrollY, 0);
+            mAnimator.addUpdateListener(this);
+            mAnimator.setDuration(300);
+            mAnimator.start();
+        } else {
+            mCurrentRecyclerViewScrollY = 0;
+            applyVerticalTransition();
+        }
+    }
+
+    /**
+     * 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);
+        }
+    }
+
+    /**
+     * 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;
+    }
+
+    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;
+        }
+        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
+        return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
+                + marginLayoutParams.topMargin;
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator animation) {
+        mCurrentRecyclerViewScrollY = (Integer) animation.getAnimatedValue();
+        applyVerticalTransition();
+    }
+
+    /**
+     * 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();
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
new file mode 100644
index 0000000..0d7d2b5
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.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.widget.picker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+/**
+ * A {@link LinearLayout} container for holding search and widgets recommendation.
+ *
+ * <p>This class intercepts touch events and dispatch them to the right view.
+ */
+public class SearchAndRecommendationsView extends LinearLayout {
+    private SearchAndRecommendationsScrollController mController;
+
+    public SearchAndRecommendationsView(Context context) {
+        this(context, /* attrs= */ null);
+    }
+
+    public SearchAndRecommendationsView(Context context, AttributeSet attrs) {
+        this(context, attrs, /* defStyleAttr= */ 0);
+    }
+
+    public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
+    }
+
+    public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setSearchAndRecommendationScrollController(
+            SearchAndRecommendationsScrollController controller) {
+        mController = controller;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        return mController.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mController.onTouchEvent(event) || super.onTouchEvent(event);
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java
new file mode 100644
index 0000000..99374f5
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget.picker;
+
+import android.util.Log;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.PackageItemInfo;
+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.WidgetsListAdapter.WidgetListBaseRowEntryComparator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
+ * methods accordingly.
+ */
+public class WidgetsDiffReporter {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "WidgetsDiffReporter";
+
+    private final IconCache mIconCache;
+    private final RecyclerView.Adapter mListener;
+
+    public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) {
+        mIconCache = iconCache;
+        mListener = listener;
+    }
+
+    /**
+     * Notifies the difference between {@code currentEntries} & {@code newEntries} by calling the
+     * relevant {@link androidx.recyclerview.widget.RecyclerView.RecyclerViewDataObserver} methods.
+     */
+    public void process(ArrayList<WidgetsListBaseEntry> currentEntries,
+            List<WidgetsListBaseEntry> newEntries,
+            WidgetListBaseRowEntryComparator comparator) {
+        if (DEBUG) {
+            Log.d(TAG, "process oldEntries#=" + currentEntries.size()
+                    + " newEntries#=" + newEntries.size());
+        }
+        // Early exit if either of the list is empty
+        if (currentEntries.isEmpty() || newEntries.isEmpty()) {
+            // Skip if both list are empty.
+            // On rotation, we open the widget tray with empty. Then try to fetch the list again
+            // when the animation completes (which still gives empty). And we get the final result
+            // when the bind actually completes.
+            if (currentEntries.size() != newEntries.size()) {
+                currentEntries.clear();
+                currentEntries.addAll(newEntries);
+                mListener.notifyDataSetChanged();
+            }
+            return;
+        }
+        ArrayList<WidgetsListBaseEntry> orgEntries =
+                (ArrayList<WidgetsListBaseEntry>) currentEntries.clone();
+        Iterator<WidgetsListBaseEntry> orgIter = orgEntries.iterator();
+        Iterator<WidgetsListBaseEntry> newIter = newEntries.iterator();
+
+        WidgetsListBaseEntry orgRowEntry = orgIter.next();
+        WidgetsListBaseEntry newRowEntry = newIter.next();
+
+        do {
+            int diff = compareAppNameAndType(orgRowEntry, newRowEntry, comparator);
+            if (DEBUG) {
+                Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
+                        diff, orgRowEntry != null ? orgRowEntry.toString() : null,
+                        newRowEntry != null ? newRowEntry.toString() : null));
+            }
+            int index = -1;
+            if (diff < 0) {
+                index = currentEntries.indexOf(orgRowEntry);
+                mListener.notifyItemRemoved(index);
+                if (DEBUG) {
+                    Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
+                            orgRowEntry.mTitleSectionName));
+                }
+                currentEntries.remove(index);
+                orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
+            } else if (diff > 0) {
+                index = orgRowEntry != null ? currentEntries.indexOf(orgRowEntry)
+                        : currentEntries.size();
+                currentEntries.add(index, newRowEntry);
+                if (DEBUG) {
+                    Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
+                            newRowEntry.mTitleSectionName));
+                }
+                newRowEntry = newIter.hasNext() ? newIter.next() : null;
+                mListener.notifyItemInserted(index);
+
+            } else {
+                // same app name & type but,
+                // did the icon, title, etc, change?
+                // or did the header view changed due to user interactions?
+                // or did the widget size and desc, span, etc change?
+                if (!isSamePackageItemInfo(orgRowEntry.mPkgItem, newRowEntry.mPkgItem)
+                        || hasHeaderUpdated(orgRowEntry, newRowEntry)
+                        || hasWidgetsListContentChanged(orgRowEntry, newRowEntry)) {
+                    index = currentEntries.indexOf(orgRowEntry);
+                    currentEntries.set(index, newRowEntry);
+                    mListener.notifyItemChanged(index);
+                    if (DEBUG) {
+                        Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
+                                newRowEntry.mTitleSectionName));
+                    }
+                }
+                orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
+                newRowEntry = newIter.hasNext() ? newIter.next() : null;
+            }
+        } while(orgRowEntry != null || newRowEntry != null);
+    }
+
+    /**
+     * Compares the app name and then entry type for the given {@link WidgetsListBaseEntry}s.
+     *
+     * @Return 0 if both entries' order is the same. Negative integer if {@code newRowEntry} should
+     *         order before {@code orgRowEntry}. Positive integer if {@code orgRowEntry} should
+     *         order before {@code newRowEntry}.
+     */
+    private int compareAppNameAndType(WidgetsListBaseEntry curRow, WidgetsListBaseEntry newRow,
+            WidgetListBaseRowEntryComparator comparator) {
+        if (curRow == null && newRow == null) {
+            throw new IllegalStateException(
+                    "Cannot compare PackageItemInfo if both rows are null.");
+        }
+
+        if (curRow == null && newRow != null) {
+            return 1; // new row needs to be inserted
+        } else if (curRow != null && newRow == null) {
+            return -1; // old row needs to be deleted
+        }
+        int diff = comparator.compare(curRow, newRow);
+        if (diff == 0) {
+            return newRow.getRank() - curRow.getRank();
+        }
+        return diff;
+    }
+
+    /**
+     * Returns {@code true} if both {@code curRow} & {@code newRow} are
+     * {@link WidgetsListContentEntry}s with a different list or arrangement of widgets.
+     */
+    private boolean hasWidgetsListContentChanged(WidgetsListBaseEntry curRow,
+            WidgetsListBaseEntry newRow) {
+        if (!(curRow instanceof WidgetsListContentEntry)
+                || !(newRow instanceof WidgetsListContentEntry)) {
+            return false;
+        }
+        return !curRow.equals(newRow);
+    }
+
+    /**
+     * Returns {@code true} if {@code newRow} is {@link WidgetsListHeaderEntry} and its content has
+     * been changed due to user interactions.
+     */
+    private boolean hasHeaderUpdated(WidgetsListBaseEntry curRow, WidgetsListBaseEntry newRow) {
+        if (newRow instanceof WidgetsListHeaderEntry && curRow instanceof WidgetsListHeaderEntry) {
+            return !curRow.equals(newRow);
+        }
+        if (newRow instanceof WidgetsListSearchHeaderEntry
+                && curRow instanceof WidgetsListSearchHeaderEntry) {
+            // Always refresh search header entries to reset rounded corners in their view holder.
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
+        return curInfo.bitmap.icon.equals(newInfo.bitmap.icon)
+                && !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user);
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
new file mode 100644
index 0000000..f2fee0a
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.picker;
+
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
+import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+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.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;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.IntStream;
+
+/**
+ * Popup for showing the full list of available widgets
+ */
+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;
+    private static final long EDUCATION_TIP_DELAY_MS = 200;
+    private static final long EDUCATION_DIALOG_DELAY_MS = 500;
+    private static final float VERTICAL_START_POSITION = 0.3f;
+    // The widget recommendation table can easily take over the entire screen on devices with small
+    // resolution or landscape on phone. This ratio defines the max percentage of content area that
+    // the table can display.
+    private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
+
+    private static final String KEY_WIDGETS_EDUCATION_DIALOG_SEEN =
+            "launcher.widgets_education_dialog_seen";
+
+    private final Rect mInsets = new Rect();
+    private final boolean mHasWorkProfile;
+    private final SparseArray<AdapterHolder> mAdapters = new SparseArray();
+    private final UserHandle mCurrentUser = Process.myUserHandle();
+    private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter =
+            entry -> mCurrentUser.equals(entry.mPkgItem.user);
+    private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
+            mPrimaryWidgetsFilter.negate();
+    @Nullable private ArrowTipView mLatestEducationalTip;
+    private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
+            new OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    if (hasSeenEducationTip()) {
+                        removeOnLayoutChangeListener(this);
+                        return;
+                    }
+
+                    // Widgets are loaded asynchronously, We are adding a delay because we only want
+                    // to show the tip when the widget preview has finished loading and rendering in
+                    // this view.
+                    removeCallbacks(mShowEducationTipTask);
+                    postDelayed(mShowEducationTipTask, EDUCATION_TIP_DELAY_MS);
+                }
+            };
+
+    private final Runnable mShowEducationTipTask = () -> {
+        if (hasSeenEducationTip()) {
+            removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+            return;
+        }
+        mLatestEducationalTip = showEducationTipOnViewIfPossible(getViewToShowEducationTip());
+        if (mLatestEducationalTip != null) {
+            removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+        }
+    };
+
+    private final OnAttachStateChangeListener mBindScrollbarInSearchMode =
+            new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View view) {
+                    WidgetsRecyclerView searchRecyclerView =
+                            mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView;
+                    if (mIsInSearchMode && searchRecyclerView != null) {
+                        searchRecyclerView.bindFastScrollbar();
+                    }
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View view) {
+                }
+            };
+
+    private final int mTabsHeight;
+    private final int mViewPagerTopPadding;
+    private final int mSearchAndRecommendationContainerBottomMargin;
+    private final int mWidgetCellHorizontalPadding;
+
+    @Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
+    @Nullable private PersonalWorkPagedView mViewPager;
+    private boolean mIsInSearchMode;
+    private boolean mIsNoWidgetsViewNeeded;
+    private int mMaxSpansPerRow = 4;
+    private View mTabsView;
+    private TextView mNoWidgetsView;
+    private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
+    private SearchAndRecommendationsScrollController mSearchAndRecommendationsScrollController;
+
+    public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
+        mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
+        mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
+        mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
+        mTabsHeight = mHasWorkProfile
+                ? getContext().getResources()
+                        .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(
+                R.dimen.widget_cell_horizontal_padding);
+    }
+
+    public WidgetsFullSheet(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    @Override
+    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);
+
+        RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
+        mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
+        mAdapters.get(AdapterHolder.SEARCH).setup(findViewById(R.id.search_widgets_list_view));
+        if (mHasWorkProfile) {
+            mViewPager = findViewById(R.id.widgets_view_pager);
+            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(
+                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);
+
+
+        onRecommendedWidgetsBound();
+        onWidgetsBound();
+
+        mSearchAndRecommendationViewHolder.mSearchBar.initialize(
+                mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this);
+
+        setUpEducationViewsIfNeeded();
+    }
+
+    @Override
+    public void onActivePageChanged(int currentActivePage) {
+        AdapterHolder currentAdapterHolder = mAdapters.get(currentActivePage);
+        WidgetsRecyclerView currentRecyclerView =
+                mAdapters.get(currentActivePage).mWidgetsRecyclerView;
+
+        updateRecyclerViewVisibility(currentAdapterHolder);
+        attachScrollbarToRecyclerView(currentRecyclerView);
+    }
+
+    private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
+        recyclerView.bindFastScrollbar();
+        if (mCurrentWidgetsRecyclerView != recyclerView) {
+            // Only reset the scroll position & expanded apps if the currently shown recycler view
+            // has been updated.
+            reset();
+            resetExpandedHeaders();
+            mCurrentWidgetsRecyclerView = recyclerView;
+            mSearchAndRecommendationsScrollController.setCurrentRecyclerView(recyclerView);
+        }
+    }
+
+    private void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
+        boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.getItemCount() > 0;
+        adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
+
+        mNoWidgetsView.setText(
+                adapterHolder.mAdapterType == AdapterHolder.SEARCH
+                        ? R.string.no_search_results
+                        : R.string.no_widgets_available);
+        mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE);
+    }
+
+    private void reset() {
+        mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop();
+        if (mHasWorkProfile) {
+            mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
+        }
+        mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
+        mSearchAndRecommendationsScrollController.reset(/* animate= */ true);
+    }
+
+    @VisibleForTesting
+    public WidgetsRecyclerView getRecyclerView() {
+        if (mIsInSearchMode) {
+            return mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView;
+        }
+        if (!mHasWorkProfile || mViewPager.getCurrentPage() == AdapterHolder.PRIMARY) {
+            return mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
+        }
+        return mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView;
+    }
+
+    @Override
+    protected Pair<View, String> getAccessibilityTarget() {
+        return Pair.create(getRecyclerView(), getContext().getString(
+                mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mActivityContext.getAppWidgetHost().addProviderChangeListener(this);
+        notifyWidgetProvidersChanged();
+        onRecommendedWidgetsBound();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mActivityContext.getAppWidgetHost().removeProviderChangeListener(this);
+        mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView
+                .removeOnAttachStateChangeListener(mBindScrollbarInSearchMode);
+        if (mHasWorkProfile) {
+            mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView
+                    .removeOnAttachStateChangeListener(mBindScrollbarInSearchMode);
+        }
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        super.setInsets(insets);
+
+        setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, insets.bottom);
+        setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, insets.bottom);
+        if (mHasWorkProfile) {
+            setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
+        }
+        mSearchAndRecommendationsScrollController.updateBottomInset(insets.bottom);
+        if (insets.bottom > 0) {
+            setupNavBarColor();
+        } else {
+            clearNavBarColor();
+        }
+
+        requestLayout();
+    }
+
+    private void setBottomPadding(RecyclerView recyclerView, int bottomPadding) {
+        recyclerView.setPadding(
+                recyclerView.getPaddingLeft(),
+                recyclerView.getPaddingTop(),
+                recyclerView.getPaddingRight(),
+                bottomPadding);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        doMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
+            doMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+
+        if (updateMaxSpansPerRow()) {
+            doMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
+                doMeasure(widthMeasureSpec, heightMeasureSpec);
+            }
+        }
+    }
+
+    /** Returns {@code true} if the max spans have been updated. */
+    private boolean updateMaxSpansPerRow() {
+        if (getMeasuredWidth() == 0) return false;
+
+        int previousMaxSpansPerRow = mMaxSpansPerRow;
+        mMaxSpansPerRow = getMeasuredWidth()
+                / (mActivityContext.getDeviceProfile().cellWidthPx + mWidgetCellHorizontalPadding);
+
+        if (previousMaxSpansPerRow != mMaxSpansPerRow) {
+            mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+                    mMaxSpansPerRow);
+            mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+                    mMaxSpansPerRow);
+            if (mHasWorkProfile) {
+                mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+                        mMaxSpansPerRow);
+            }
+            onRecommendedWidgetsBound();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int width = r - l;
+        int height = b - t;
+
+        // Content is laid out 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);
+    }
+
+    @Override
+    public void notifyWidgetProvidersChanged() {
+        mActivityContext.refreshAndBindWidgetsForPackageUser(null);
+    }
+
+    @Override
+    public void onWidgetsBound() {
+        if (mIsInSearchMode) {
+            return;
+        }
+        List<WidgetsListBaseEntry> allWidgets =
+                mActivityContext.getPopupDataProvider().getAllWidgets();
+
+        AdapterHolder primaryUserAdapterHolder = mAdapters.get(AdapterHolder.PRIMARY);
+        primaryUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
+
+        if (mHasWorkProfile) {
+            mViewPager.setVisibility(VISIBLE);
+            mTabsView.setVisibility(VISIBLE);
+            AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
+            workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
+            onActivePageChanged(mViewPager.getCurrentPage());
+        } else {
+            updateRecyclerViewVisibility(primaryUserAdapterHolder);
+        }
+        // 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
+                        || (mHasWorkProfile && mAdapters.get(AdapterHolder.WORK)
+                                .mWidgetsListAdapter.getItemCount() == 0);
+        if (mIsNoWidgetsViewNeeded != isNoWidgetsViewNeeded) {
+            mIsNoWidgetsViewNeeded = isNoWidgetsViewNeeded;
+            onRecommendedWidgetsBound();
+        }
+    }
+
+    @Override
+    public void enterSearchMode() {
+        if (mIsInSearchMode) return;
+        setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
+        attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView);
+        mActivityContext.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_SEARCHED);
+    }
+
+    @Override
+    public void exitSearchMode() {
+        if (!mIsInSearchMode) return;
+        onSearchResults(new ArrayList<>());
+        setViewVisibilityBasedOnSearch(/*isInSearchMode=*/ false);
+        if (mHasWorkProfile) {
+            mViewPager.snapToPage(AdapterHolder.PRIMARY);
+        }
+        attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView);
+
+        mSearchAndRecommendationsScrollController.updateMarginAndPadding();
+    }
+
+    @Override
+    public void onSearchResults(List<WidgetsListBaseEntry> entries) {
+        mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
+        updateRecyclerViewVisibility(mAdapters.get(AdapterHolder.SEARCH));
+        mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
+    }
+
+    private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
+        mIsInSearchMode = isInSearchMode;
+        if (isInSearchMode) {
+            mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.setVisibility(GONE);
+            if (mHasWorkProfile) {
+                mViewPager.setVisibility(GONE);
+                mTabsView.setVisibility(GONE);
+            } else {
+                mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.setVisibility(GONE);
+            }
+            updateRecyclerViewVisibility(mAdapters.get(AdapterHolder.SEARCH));
+            // Hide no search results view to prevent it from flashing on enter search.
+            mNoWidgetsView.setVisibility(GONE);
+        } else {
+            mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.setVisibility(GONE);
+            // Visibility of recommended widgets, recycler views and headers are handled in methods
+            // below.
+            onRecommendedWidgetsBound();
+            onWidgetsBound();
+        }
+    }
+
+    private void resetExpandedHeaders() {
+        mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.resetExpandedHeader();
+        mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.resetExpandedHeader();
+    }
+
+    @Override
+    public void onRecommendedWidgetsBound() {
+        if (mIsInSearchMode) {
+            return;
+        }
+        List<WidgetItem> recommendedWidgets =
+                mActivityContext.getPopupDataProvider().getRecommendedWidgets();
+        WidgetsRecommendationTableLayout table =
+                mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable;
+        if (recommendedWidgets.size() > 0) {
+            float noWidgetsViewHeight = 0;
+            if (mIsNoWidgetsViewNeeded) {
+                // Make sure recommended section leaves enough space for noWidgetsView.
+                Rect noWidgetsViewTextBounds = new Rect();
+                mNoWidgetsView.getPaint()
+                        .getTextBounds(mNoWidgetsView.getText().toString(), /* start= */ 0,
+                                mNoWidgetsView.getText().length(), noWidgetsViewTextBounds);
+                noWidgetsViewHeight = noWidgetsViewTextBounds.height();
+            }
+            doMeasure(
+                    makeMeasureSpec(mActivityContext.getDeviceProfile().availableWidthPx,
+                            MeasureSpec.EXACTLY),
+                    makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
+                            MeasureSpec.EXACTLY));
+            float maxTableHeight = (mContent.getMeasuredHeight()
+                    - mTabsHeight - mViewPagerTopPadding - getHeaderViewHeight()
+                    - noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+
+            List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
+                    WidgetsTableUtils.groupWidgetItemsIntoTable(recommendedWidgets,
+                            mMaxSpansPerRow);
+            table.setRecommendedWidgets(recommendedWidgetsInTable, maxTableHeight);
+        } else {
+            table.setVisibility(GONE);
+        }
+    }
+
+    private void open(boolean animate) {
+        if (animate) {
+            if (getPopupContainer().getInsets().bottom > 0) {
+                mContent.setAlpha(0);
+                setTranslationShift(VERTICAL_START_POSITION);
+            }
+            mOpenCloseAnimator.setValues(
+                    PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+            mOpenCloseAnimator
+                    .setDuration(DEFAULT_OPEN_DURATION)
+                    .setInterpolator(AnimationUtils.loadInterpolator(
+                            getContext(), android.R.interpolator.linear_out_slow_in));
+            mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mOpenCloseAnimator.removeListener(this);
+                }
+            });
+            post(() -> {
+                mOpenCloseAnimator.start();
+                mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
+            });
+        } else {
+            setTranslationShift(TRANSLATION_SHIFT_OPENED);
+            post(this::announceAccessibilityChanges);
+        }
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        handleClose(animate, DEFAULT_OPEN_DURATION);
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        // Disable swipe down when recycler view is scrolling
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mNoIntercept = false;
+            RecyclerViewFastScroller scroller = getRecyclerView().getScrollbar();
+            if (scroller.getThumbOffsetY() >= 0
+                    && getPopupContainer().isEventOverView(scroller, ev)) {
+                mNoIntercept = true;
+            } else if (getPopupContainer().isEventOverView(mContent, ev)) {
+                mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
+            }
+
+            if (mSearchAndRecommendationViewHolder.mSearchBar.isSearchBarFocused()
+                    && !getPopupContainer().isEventOverView(
+                            mSearchAndRecommendationViewHolder.mSearchBarContainer, ev)) {
+                mSearchAndRecommendationViewHolder.mSearchBar.clearSearchBarFocus();
+            }
+        }
+        return super.onControllerInterceptTouchEvent(ev);
+    }
+
+    /** Shows the {@link WidgetsFullSheet} on the launcher. */
+    public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
+        WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
+                .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
+        sheet.attachToContainer();
+        sheet.mIsOpen = true;
+        sheet.open(animate);
+        return sheet;
+    }
+
+    /** Gets the {@link WidgetsRecyclerView} which shows all widgets in {@link WidgetsFullSheet}. */
+    @VisibleForTesting
+    public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
+        return launcher.findViewById(R.id.primary_widgets_list_view);
+    }
+
+    @Override
+    public void addHintCloseAnim(
+            float distanceToMove, Interpolator interpolator, PendingAnimation target) {
+        target.setFloat(getRecyclerView(), VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
+        target.setViewAlpha(getRecyclerView(), 0.5f, interpolator);
+    }
+
+    @Override
+    protected void onCloseComplete() {
+        super.onCloseComplete();
+        removeCallbacks(mShowEducationTipTask);
+        if (mLatestEducationalTip != null) {
+            mLatestEducationalTip.close(false);
+        }
+        AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
+    }
+
+    @Override
+    public int getHeaderViewHeight() {
+        return measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mCollapseHandle)
+                + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mHeaderTitle)
+                + measureHeightWithVerticalMargins(
+                        (View) mSearchAndRecommendationViewHolder.mSearchBarContainer);
+    }
+
+    /** private the height, in pixel, + the vertical margins of a given view. */
+    private static int measureHeightWithVerticalMargins(View view) {
+        if (view.getVisibility() != VISIBLE) {
+            return 0;
+        }
+        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
+        return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
+                + marginLayoutParams.topMargin;
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mIsInSearchMode) {
+            mSearchAndRecommendationViewHolder.mSearchBar.reset();
+        }
+    }
+
+    @Override
+    public boolean onBackPressed() {
+        if (mIsInSearchMode) {
+            mSearchAndRecommendationViewHolder.mSearchBar.reset();
+            return true;
+        }
+        return super.onBackPressed();
+    }
+
+    @Override
+    public void onDragStart(boolean start, float startDisplacement) {
+        super.onDragStart(start, startDisplacement);
+        getWindowInsetsController().hide(WindowInsets.Type.ime());
+    }
+
+    @Nullable private View getViewToShowEducationTip() {
+        if (mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getVisibility() == VISIBLE
+                && mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getChildCount() > 0
+        ) {
+            return ((ViewGroup) mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable
+                    .getChildAt(0)).getChildAt(0);
+        }
+
+        AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode
+                ? AdapterHolder.SEARCH
+                : mViewPager == null
+                        ? AdapterHolder.PRIMARY
+                        : mViewPager.getCurrentPage());
+        WidgetsRowViewHolder viewHolderForTip =
+                (WidgetsRowViewHolder) IntStream.range(
+                                0, adapterHolder.mWidgetsListAdapter.getItemCount())
+                        .mapToObj(adapterHolder.mWidgetsRecyclerView::
+                                findViewHolderForAdapterPosition)
+                        .filter(viewHolder -> viewHolder instanceof WidgetsRowViewHolder)
+                        .findFirst()
+                        .orElse(null);
+        if (viewHolderForTip != null) {
+            return ((ViewGroup) viewHolderForTip.mTableContainer.getChildAt(0)).getChildAt(0);
+        }
+
+        return null;
+    }
+
+    /** Shows education dialog for widgets. */
+    private WidgetsEduView showEducationDialog() {
+        mActivityContext.getSharedPrefs().edit()
+                .putBoolean(KEY_WIDGETS_EDUCATION_DIALOG_SEEN, true).apply();
+        return WidgetsEduView.showEducationDialog(mActivityContext);
+    }
+
+    /** Returns {@code true} if education dialog has previously been shown. */
+    protected boolean hasSeenEducationDialog() {
+        return mActivityContext.getSharedPrefs()
+                .getBoolean(KEY_WIDGETS_EDUCATION_DIALOG_SEEN, false)
+                || Utilities.IS_RUNNING_IN_TEST_HARNESS;
+    }
+
+    private void setUpEducationViewsIfNeeded() {
+        if (!hasSeenEducationDialog()) {
+            postDelayed(() -> {
+                WidgetsEduView eduDialog = showEducationDialog();
+                eduDialog.addOnCloseListener(() -> {
+                    if (!hasSeenEducationTip()) {
+                        addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+                        // Call #requestLayout() to trigger layout change listener in order to show
+                        // arrow tip immediately if there is a widget to show it on.
+                        requestLayout();
+                    }
+                });
+            }, EDUCATION_DIALOG_DELAY_MS);
+        } else if (!hasSeenEducationTip()) {
+            addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
+        }
+    }
+
+    /** A holder class for holding adapters & their corresponding recycler view. */
+    private final class AdapterHolder {
+        static final int PRIMARY = 0;
+        static final int WORK = 1;
+        static final int SEARCH = 2;
+
+        private final int mAdapterType;
+        private final WidgetsListAdapter mWidgetsListAdapter;
+        private final DefaultItemAnimator mWidgetsListItemAnimator;
+
+        private WidgetsRecyclerView mWidgetsRecyclerView;
+
+        AdapterHolder(int adapterType) {
+            mAdapterType = adapterType;
+
+            Context context = getContext();
+            LauncherAppState apps = LauncherAppState.getInstance(context);
+            mWidgetsListAdapter = new WidgetsListAdapter(
+                    context,
+                    LayoutInflater.from(context),
+                    apps.getWidgetCache(),
+                    apps.getIconCache(),
+                    /* iconClickListener= */ WidgetsFullSheet.this,
+                    /* iconLongClickListener= */ WidgetsFullSheet.this);
+            mWidgetsListAdapter.setHasStableIds(true);
+            switch (mAdapterType) {
+                case PRIMARY:
+                    mWidgetsListAdapter.setFilter(mPrimaryWidgetsFilter);
+                    break;
+                case WORK:
+                    mWidgetsListAdapter.setFilter(mWorkWidgetsFilter);
+                    break;
+                default:
+                    break;
+            }
+            mWidgetsListItemAnimator = new DefaultItemAnimator();
+            // Disable change animations because it disrupts the item focus upon adapter item
+            // change.
+            mWidgetsListItemAnimator.setSupportsChangeAnimations(false);
+        }
+
+        void setup(WidgetsRecyclerView recyclerView) {
+            mWidgetsRecyclerView = recyclerView;
+            mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
+            mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
+            mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
+            mWidgetsRecyclerView.setEdgeEffectFactory(
+                    ((TopRoundedCornerView) 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
new file mode 100644
index 0000000..1125b82
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -0,0 +1,546 @@
+/*
+ * 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.
+ */
+package com.android.launcher3.widget.picker;
+
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_APP_EXPANDED;
+
+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;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+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.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.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.OptionalInt;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * Recycler view adapter for the widget tray.
+ *
+ * <p>This adapter supports view binding of subclasses of {@link WidgetsListBaseEntry}. There are 2
+ * subclasses: {@link WidgetsListHeader} & {@link WidgetsListContentEntry}.
+ * {@link WidgetsListHeader} entries are always visible in the recycler view. At most one
+ * {@link WidgetsListContentEntry} is shown in the recycler view at any time. Clicking a
+ * {@link WidgetsListHeader} will result in expanding / collapsing a corresponding
+ * {@link WidgetsListContentEntry} of the same app.
+ */
+public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderClickListener {
+
+    private static final String TAG = "WidgetsListAdapter";
+    private static final boolean DEBUG = false;
+
+    /** Uniquely identifies widgets list view type within the app. */
+    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 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)
+                            .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,
+            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_HEADER,
+                new WidgetsListHeaderViewHolderBinder(
+                        layoutInflater,
+                        /* onHeaderClickListener= */ this,
+                        listDrawableFactory,
+                        /* listAdapter= */ this));
+        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);
+        mSpacingBetweenEntries =
+                context.getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
+    }
+
+    @Override
+    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
+        mRecyclerView = recyclerView;
+
+        mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
+            @Override
+            public void getItemOffsets(
+                    @NonNull Rect outRect,
+                    @NonNull View view,
+                    @NonNull RecyclerView parent,
+                    @NonNull RecyclerView.State state) {
+                super.getItemOffsets(outRect, view, parent, state);
+                int position = ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
+                boolean isHeader =
+                        view.getTag(R.id.tag_widget_entry) instanceof WidgetsListBaseEntry.Header;
+                outRect.top += position > 0 && isHeader ? mSpacingBetweenEntries : 0;
+            }
+        });
+    }
+
+    @Override
+    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
+        mRecyclerView = null;
+    }
+
+    public void setFilter(Predicate<WidgetsListBaseEntry> filter) {
+        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 all items that will be drawn in a recycler view. */
+    public List<WidgetsListBaseEntry> getItems() {
+        return mVisibleEntries;
+    }
+
+    /** Gets the section name for {@link com.android.launcher3.views.RecyclerViewFastScroller}. */
+    public String getSectionName(int pos) {
+        return mVisibleEntries.get(pos).mTitleSectionName;
+    }
+
+    /** Updates the widget list based on {@code tempEntries}. */
+    public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
+        mCachingPreviewLoader.clearAll();
+        mAllEntries = tempEntries.stream().sorted(mRowComparator)
+                .collect(Collectors.toList());
+        if (shouldClearVisibleEntries()) {
+            mVisibleEntries.clear();
+        }
+        updateVisibleEntries();
+    }
+
+    /** Updates the widget list based on {@code searchResults}. */
+    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 =
+                getPositionForPackageUserKey(mPendingClickHeader);
+        OptionalInt topForPackageUserKey =
+                getOffsetForPosition(previousPositionForPackageUserKey);
+
+        List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
+                .filter(entry -> (mFilter == null || mFilter.test(entry))
+                        && mHeaderAndSelectedContentFilter.test(entry))
+                .map(entry -> {
+                    if (entry instanceof WidgetsListBaseEntry.Header<?>
+                            && matchesKey(entry, mWidgetsContentVisiblePackageUserKey)) {
+                        // Adjust the original entries to expand headers for the selected content.
+                        return ((WidgetsListBaseEntry.Header<?>) entry).withWidgetListShown();
+                    } else if (entry instanceof WidgetsListContentEntry) {
+                        // Adjust the original content entries to accommodate for the current
+                        // maxSpanSize.
+                        return ((WidgetsListContentEntry) entry).withMaxSpanSize(mMaxSpanSize);
+                    }
+                    return entry;
+                })
+                .collect(Collectors.toList());
+
+        mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator);
+
+        if (mPendingClickHeader != null) {
+            // Get the position for the clicked header after adjusting the visible entries. The
+            // position may have changed if another header had previously been expanded.
+            OptionalInt positionForPackageUserKey =
+                    getPositionForPackageUserKey(mPendingClickHeader);
+            scrollToPositionAndMaintainOffset(positionForPackageUserKey, topForPackageUserKey);
+            mPendingClickHeader = null;
+        }
+    }
+
+    /**
+     * 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(
+            @NonNull WidgetsListBaseEntry entry, @Nullable PackageUserKey key) {
+        return entry instanceof WidgetsListBaseEntry.Header && matchesKey(entry, 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.user.equals(key.mUser);
+    }
+
+    /**
+     * Resets any expanded widget header.
+     */
+    public void resetExpandedHeader() {
+        if (mWidgetsContentVisiblePackageUserKey != null) {
+            mWidgetsContentVisiblePackageUserKey = null;
+            cancelLoadingPreviews();
+            updateVisibleEntries();
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int pos) {
+        ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
+        WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
+        viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), pos);
+        holder.itemView.setTag(R.id.tag_widget_entry, entry);
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        if (DEBUG) {
+            Log.v(TAG, "\nonCreateViewHolder");
+        }
+
+        return mViewHolderBinders.get(viewType).newViewHolder(parent);
+    }
+
+    @Override
+    public void onViewRecycled(ViewHolder holder) {
+        mViewHolderBinders.get(holder.getItemViewType()).unbindViewHolder(holder);
+    }
+
+    @Override
+    public boolean onFailedToRecycleView(ViewHolder holder) {
+        // If child views are animating, then the RecyclerView may choose not to recycle the view,
+        // causing extraneous onCreateViewHolder() calls.  It is safe in this case to continue
+        // recycling this view, and take care in onViewRecycled() to cancel any existing
+        // animations.
+        return true;
+    }
+
+    @Override
+    public long getItemId(int pos) {
+        return Arrays.hashCode(new Object[]{
+                mVisibleEntries.get(pos).mPkgItem.hashCode(),
+                getItemViewType(pos)});
+    }
+
+    @Override
+    public int getItemViewType(int pos) {
+        WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
+        if (entry instanceof WidgetsListContentEntry) {
+            return VIEW_TYPE_WIDGETS_LIST;
+        } else if (entry instanceof WidgetsListHeaderEntry) {
+            return VIEW_TYPE_WIDGETS_HEADER;
+        } else if (entry instanceof WidgetsListSearchHeaderEntry) {
+            return VIEW_TYPE_WIDGETS_SEARCH_HEADER;
+        }
+        throw new UnsupportedOperationException("ViewHolderBinder not found for " + entry);
+    }
+
+    @Override
+    public void onHeaderClicked(boolean showWidgets, PackageUserKey packageUserKey) {
+        // 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);
+        } else {
+            mWidgetsContentVisiblePackageUserKey = null;
+        }
+
+        // Store the header that was clicked so that its position will be maintained the next time
+        // we update the entries.
+        mPendingClickHeader = packageUserKey;
+
+        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.
+     */
+    @NonNull
+    private OptionalInt getPositionForPackageUserKey(@Nullable PackageUserKey key) {
+        return IntStream.range(0, mVisibleEntries.size())
+                .filter(index -> isHeaderForPackageUserKey(mVisibleEntries.get(index), key))
+                .findFirst();
+    }
+
+    /**
+     * Returns the top of {@code positionOptional} in the recycler view, or empty if its view
+     * can't be found for any reason, including the position not being currently visible. The
+     * returned value does not include the top padding of the recycler view.
+     */
+    private OptionalInt getOffsetForPosition(OptionalInt positionOptional) {
+        if (!positionOptional.isPresent() || mRecyclerView == null) return OptionalInt.empty();
+
+        RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
+        if (layoutManager == null) return OptionalInt.empty();
+
+        View view = layoutManager.findViewByPosition(positionOptional.getAsInt());
+        if (view == null) return OptionalInt.empty();
+
+        return OptionalInt.of(layoutManager.getDecoratedTop(view));
+    }
+
+    /**
+     * Scrolls to the selected header position with the provided offset. LinearLayoutManager
+     * scrolls the minimum distance necessary, so this will keep the selected header in place during
+     * clicks, without interrupting the animation.
+     *
+     * @param positionOptional The position too scroll to. No scrolling will be done if empty.
+     * @param offsetOptional The offset from the top to maintain. If empty, then the list will
+     *                       scroll to the top of the position.
+     */
+    private void scrollToPositionAndMaintainOffset(
+            OptionalInt positionOptional,
+            OptionalInt offsetOptional) {
+        if (!positionOptional.isPresent() || mRecyclerView == null) return;
+        int position = positionOptional.getAsInt();
+
+        LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
+        if (layoutManager == null) return;
+
+        if (position == mVisibleEntries.size() - 2
+                && mVisibleEntries.get(mVisibleEntries.size() - 1)
+                instanceof WidgetsListContentEntry) {
+            // If the selected header is in the last position and its content is showing, then
+            // scroll to the final position so the last list of widgets will show.
+            layoutManager.scrollToPosition(mVisibleEntries.size() - 1);
+            return;
+        }
+
+        // Scroll to the header view's current offset, accounting for the recycler view's padding.
+        // If the header view couldn't be found, then it will appear at the top of the list.
+        layoutManager.scrollToPositionWithOffset(
+                position,
+                offsetOptional.orElse(0) - mRecyclerView.getPaddingTop());
+    }
+
+    /**
+     * Sets the max horizontal span in cells that is allowed for grouping more than one widget in a
+     * table row.
+     */
+    public void setMaxHorizontalSpansPerRow(int maxHorizontalSpans) {
+        mMaxSpanSize = maxHorizontalSpans;
+        updateVisibleEntries();
+    }
+
+    /**
+     * Returns {@code true} if there is a change in {@link #mAllEntries} that results in an
+     * invalidation of {@link #mVisibleEntries}. e.g. there is change in the device language.
+     */
+    private boolean shouldClearVisibleEntries() {
+        Map<PackageUserKey, PackageItemInfo> packagesInfo =
+                mAllEntries.stream()
+                        .filter(entry -> entry instanceof WidgetsListHeaderEntry)
+                        .map(entry -> entry.mPkgItem)
+                        .collect(Collectors.toMap(
+                                entry -> new PackageUserKey(entry.packageName, entry.user),
+                                entry -> entry));
+        for (WidgetsListBaseEntry visibleEntry: mVisibleEntries) {
+            PackageUserKey key = new PackageUserKey(visibleEntry.mPkgItem.packageName,
+                    visibleEntry.mPkgItem.user);
+            PackageItemInfo packageItemInfo = packagesInfo.get(key);
+            if (packageItemInfo != null
+                    && !visibleEntry.mPkgItem.title.equals(packageItemInfo.title)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Comparator for sorting WidgetListRowEntry based on package title. */
+    public static class WidgetListBaseRowEntryComparator implements
+            Comparator<WidgetsListBaseEntry> {
+
+        private final LabelComparator mComparator = new LabelComparator();
+
+        @Override
+        public int compare(WidgetsListBaseEntry a, WidgetsListBaseEntry b) {
+            int i = mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
+            if (i != 0) {
+                return i;
+            }
+            // Prioritize entries from current user over other users if the entries are same.
+            if (a.mPkgItem.user.equals(b.mPkgItem.user)) return 0;
+            if (a.mPkgItem.user.equals(Process.myUserHandle())) return -1;
+            return 1;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java
new file mode 100644
index 0000000..c61e3a4
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.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.widget.picker;
+
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.FIRST;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.FIRST_EXPANDED;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE_EXPANDED;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.SINGLE;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.StateListDrawable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+/** Factory for creating drawables to use as background for list elements. */
+final class WidgetsListDrawableFactory {
+
+    private final float mTopBottomCornerRadius;
+    private final float mMiddleCornerRadius;
+    private final ColorStateList mSurfaceColor;
+    private final ColorStateList mRippleColor;
+
+    WidgetsListDrawableFactory(Context context) {
+        Resources res = context.getResources();
+        mTopBottomCornerRadius = res.getDimension(R.dimen.widget_list_top_bottom_corner_radius);
+        mMiddleCornerRadius = res.getDimension(R.dimen.widget_list_content_corner_radius);
+        mSurfaceColor = context.getColorStateList(R.color.surface);
+        mRippleColor = ColorStateList.valueOf(
+                Themes.getAttrColor(context, android.R.attr.colorControlHighlight));
+    }
+
+    /**
+     * Creates a drawable for widget header list items. This drawable supports all positions
+     * in {@link WidgetsListDrawableState}.
+     */
+    Drawable createHeaderBackgroundDrawable() {
+        StateListDrawable stateList = new StateListDrawable();
+        stateList.addState(
+                SINGLE.mStateSet,
+                createRoundedRectDrawable(mTopBottomCornerRadius, mTopBottomCornerRadius));
+        stateList.addState(
+                FIRST_EXPANDED.mStateSet,
+                createRoundedRectDrawable(mTopBottomCornerRadius, 0));
+        stateList.addState(
+                FIRST.mStateSet,
+                createRoundedRectDrawable(mTopBottomCornerRadius, mMiddleCornerRadius));
+        stateList.addState(
+                MIDDLE_EXPANDED.mStateSet,
+                createRoundedRectDrawable(mMiddleCornerRadius, 0));
+        stateList.addState(
+                MIDDLE.mStateSet,
+                createRoundedRectDrawable(mMiddleCornerRadius, mMiddleCornerRadius));
+        stateList.addState(
+                LAST.mStateSet,
+                createRoundedRectDrawable(mMiddleCornerRadius, mTopBottomCornerRadius));
+        return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList);
+    }
+
+    /**
+     * Creates a drawable for widget content list items. This state list supports the middle and
+     * last states.
+     */
+    Drawable createContentBackgroundDrawable() {
+        StateListDrawable stateList = new StateListDrawable();
+        stateList.addState(
+                MIDDLE.mStateSet,
+                createRoundedRectDrawable(0, mMiddleCornerRadius));
+        stateList.addState(
+                LAST.mStateSet,
+                createRoundedRectDrawable(0, mTopBottomCornerRadius));
+        return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList);
+    }
+
+    /** Creates a rounded-rect drawable with the specified radii. */
+    private Drawable createRoundedRectDrawable(float topRadius, float bottomRadius) {
+        GradientDrawable backgroundMask = new GradientDrawable();
+        backgroundMask.setColor(mSurfaceColor);
+        backgroundMask.setShape(GradientDrawable.RECTANGLE);
+        backgroundMask.setCornerRadii(
+                new float[]{
+                        topRadius,
+                        topRadius,
+                        topRadius,
+                        topRadius,
+                        bottomRadius,
+                        bottomRadius,
+                        bottomRadius,
+                        bottomRadius
+                });
+        return backgroundMask;
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java
new file mode 100644
index 0000000..94f292b
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * Different possible list position states for an item in the widgets list to have. Note that only
+ * headers use the expanded state.
+ */
+enum WidgetsListDrawableState {
+    FIRST(new int[]{android.R.attr.state_first}),
+    FIRST_EXPANDED(new int[]{android.R.attr.state_first, android.R.attr.state_expanded}),
+    MIDDLE(new int[]{android.R.attr.state_middle}),
+    MIDDLE_EXPANDED(new int[]{android.R.attr.state_middle, android.R.attr.state_expanded}),
+    LAST(new int[]{android.R.attr.state_last}),
+    SINGLE(new int[]{android.R.attr.state_single});
+
+    final int[] mStateSet;
+
+    WidgetsListDrawableState(int[] stateSet) {
+        mStateSet = stateSet;
+    }
+
+    static WidgetsListDrawableState obtain(boolean isFirst, boolean isLast, boolean isExpanded) {
+        if (isFirst && isLast) return SINGLE;
+        if (isFirst && isExpanded) return FIRST_EXPANDED;
+        if (isFirst) return FIRST;
+        if (isLast) return LAST;
+        if (isExpanded) return MIDDLE_EXPANDED;
+        return MIDDLE;
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
new file mode 100644
index 0000000..ef2adbb
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -0,0 +1,309 @@
+/*
+ * 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 android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.PlaceHolderIconDrawable;
+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.views.ActivityContext;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
+
+import java.util.stream.Collectors;
+
+/**
+ * A UI represents a header of an app shown in the full widgets tray.
+ *
+ * It is a {@link LinearLayout} which contains an app icon, an app name, a subtitle and a checkbox
+ * which indicates if the widgets content view underneath this header should be shown.
+ */
+public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpdateReceiver {
+
+    private boolean mEnableIconUpdateAnimation = false;
+
+    @Nullable private HandlerRunnable mIconLoadRequest;
+    @Nullable private Drawable mIconDrawable;
+    private final int mIconSize;
+
+    private ImageView mAppIcon;
+    private TextView mTitle;
+    private TextView mSubtitle;
+
+    private CheckBox mExpandToggle;
+    private boolean mIsExpanded = false;
+    @Nullable private WidgetsListDrawableState mListDrawableState;
+
+    public WidgetsListHeader(Context context) {
+        this(context, /* attrs= */ null);
+    }
+
+    public WidgetsListHeader(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, /* defStyle= */ 0);
+    }
+
+    public WidgetsListHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        ActivityContext activity = ActivityContext.lookupContext(context);
+        DeviceProfile grid = activity.getDeviceProfile();
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
+        mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
+                grid.iconSizePx);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mAppIcon = findViewById(R.id.app_icon);
+        mTitle = findViewById(R.id.app_title);
+        mSubtitle = findViewById(R.id.app_subtitle);
+        mExpandToggle = findViewById(R.id.toggle);
+        findViewById(R.id.app_container).setAccessibilityDelegate(new AccessibilityDelegate() {
+
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                if (mIsExpanded) {
+                    info.removeAction(AccessibilityNodeInfo.ACTION_EXPAND);
+                    info.addAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
+                } else {
+                    info.removeAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
+                    info.addAction(AccessibilityNodeInfo.ACTION_EXPAND);
+                }
+                super.onInitializeAccessibilityNodeInfo(host, info);
+            }
+
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                switch (action) {
+                    case AccessibilityNodeInfo.ACTION_EXPAND:
+                    case AccessibilityNodeInfo.ACTION_COLLAPSE:
+                        callOnClick();
+                        return true;
+                    default:
+                        return super.performAccessibilityAction(host, action, args);
+                }
+            }
+        });
+    }
+
+    /**
+     * Sets a {@link OnExpansionChangeListener} to get a callback when this app widgets section
+     * expands / collapses.
+     */
+    @UiThread
+    public void setOnExpandChangeListener(
+            @Nullable OnExpansionChangeListener onExpandChangeListener) {
+        // Use the entire touch area of this view to expand / collapse an app widgets section.
+        setOnClickListener(view -> {
+            setExpanded(!mIsExpanded);
+            if (onExpandChangeListener != null) {
+                onExpandChangeListener.onExpansionChange(mIsExpanded);
+            }
+        });
+    }
+
+    /** Sets the expand toggle to expand / collapse. */
+    @UiThread
+    public void setExpanded(boolean isExpanded) {
+        this.mIsExpanded = isExpanded;
+        mExpandToggle.setChecked(isExpanded);
+    }
+
+    /** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
+    @UiThread
+    public void setListDrawableState(WidgetsListDrawableState state) {
+        if (state == mListDrawableState) return;
+        this.mListDrawableState = state;
+        refreshDrawableState();
+    }
+
+    /** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */
+    @UiThread
+    public void applyFromItemInfoWithIcon(WidgetsListHeaderEntry entry) {
+        applyIconAndLabel(entry);
+    }
+
+    @UiThread
+    private void applyIconAndLabel(WidgetsListHeaderEntry entry) {
+        PackageItemInfo info = entry.mPkgItem;
+        setIcon(info);
+        setTitles(entry);
+        setExpanded(entry.isWidgetListShown());
+
+        super.setTag(info);
+
+        verifyHighRes();
+    }
+
+    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());
+        }
+        applyDrawables(icon);
+        mIconDrawable = icon;
+        if (mIconDrawable != null) {
+            mIconDrawable.setVisible(
+                    /* visible= */ getWindowVisibility() == VISIBLE && isShown(),
+                    /* restart= */ false);
+        }
+    }
+
+    private void applyDrawables(Drawable icon) {
+        icon.setBounds(0, 0, mIconSize, mIconSize);
+
+        LinearLayout.LayoutParams layoutParams =
+                (LinearLayout.LayoutParams) mAppIcon.getLayoutParams();
+        layoutParams.width = mIconSize;
+        layoutParams.height = mIconSize;
+        mAppIcon.setLayoutParams(layoutParams);
+        mAppIcon.setImageDrawable(icon);
+
+        // If the current icon is a placeholder color, animate its update.
+        if (mIconDrawable != null
+                && mIconDrawable instanceof PlaceHolderIconDrawable
+                && mEnableIconUpdateAnimation) {
+            ((PlaceHolderIconDrawable) mIconDrawable).animateIconUpdate(icon);
+        }
+    }
+
+    private void setTitles(WidgetsListHeaderEntry entry) {
+        mTitle.setText(entry.mPkgItem.title);
+
+        Resources resources = getContext().getResources();
+        if (entry.widgetsCount == 0 && entry.shortcutsCount == 0) {
+            mSubtitle.setVisibility(GONE);
+            return;
+        }
+
+        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);
+            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);
+        } else {
+            subtitle = resources.getQuantityString(R.plurals.shortcuts_count,
+                    entry.shortcutsCount, entry.shortcutsCount);
+        }
+        mSubtitle.setText(subtitle);
+        mSubtitle.setVisibility(VISIBLE);
+    }
+
+    /** Apply app icon, labels and tag using a generic {@link WidgetsListSearchHeaderEntry}. */
+    @UiThread
+    public void applyFromItemInfoWithIcon(WidgetsListSearchHeaderEntry entry) {
+        applyIconAndLabel(entry);
+    }
+
+    @UiThread
+    private void applyIconAndLabel(WidgetsListSearchHeaderEntry entry) {
+        PackageItemInfo info = entry.mPkgItem;
+        setIcon(info);
+        setTitles(entry);
+        setExpanded(entry.isWidgetListShown());
+
+        super.setTag(info);
+
+        verifyHighRes();
+    }
+
+    private void setTitles(WidgetsListSearchHeaderEntry entry) {
+        mTitle.setText(entry.mPkgItem.title);
+
+        mSubtitle.setText(entry.mWidgets.stream()
+                .map(item -> item.label).sorted().collect(Collectors.joining(", ")));
+        mSubtitle.setVisibility(VISIBLE);
+    }
+
+    @Override
+    public void reapplyItemInfo(ItemInfoWithIcon info) {
+        if (getTag() == info) {
+            mIconLoadRequest = null;
+            mEnableIconUpdateAnimation = true;
+
+            // Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
+            info.bitmap.icon.prepareToDraw();
+
+            setIcon((PackageItemInfo) info);
+
+            mEnableIconUpdateAnimation = false;
+        }
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace);
+        // Augment the state set from the super implementation with the custom states from
+        // mListDrawableState.
+        int[] drawableState =
+                super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length);
+        mergeDrawableStates(drawableState, mListDrawableState.mStateSet);
+        return drawableState;
+    }
+
+    /** Verifies that the current icon is high-res otherwise posts a request to load the icon. */
+    public void verifyHighRes() {
+        if (mIconLoadRequest != null) {
+            mIconLoadRequest.cancel();
+            mIconLoadRequest = null;
+        }
+        if (getTag() instanceof ItemInfoWithIcon) {
+            ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+            if (info.usingLowResIcon()) {
+                mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache()
+                        .updateIconInBackground(this, info);
+            }
+        }
+    }
+
+    /** A listener for the widget section expansion / collapse events. */
+    public interface OnExpansionChangeListener {
+        /** Notifies that the widget section is expanded or collapsed. */
+        void onExpansionChange(boolean isExpanded);
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderHolder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderHolder.java
new file mode 100644
index 0000000..d4e1b1c
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderHolder.java
@@ -0,0 +1,32 @@
+/*
+ * 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 androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * A {@link ViewHolder} for {@link WidgetsListHeader} of an app, which renders the app icon, the app
+ * name, label and a button for showing / hiding widgets.
+ */
+public final class WidgetsListHeaderHolder extends ViewHolder {
+    final WidgetsListHeader mWidgetsListHeader;
+
+    public WidgetsListHeaderHolder(WidgetsListHeader view) {
+        super(view);
+
+        mWidgetsListHeader = view;
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
new file mode 100644
index 0000000..2f8f1ba
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -0,0 +1,71 @@
+/*
+ * 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.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.launcher3.R;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+
+/**
+ * Binds data from {@link WidgetsListHeaderEntry} to UI elements in {@link WidgetsListHeaderHolder}.
+ */
+public final class WidgetsListHeaderViewHolderBinder implements
+        ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> {
+    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) {
+        mLayoutInflater = layoutInflater;
+        mOnHeaderClickListener = onHeaderClickListener;
+        mListDrawableFactory = listDrawableFactory;
+        mWidgetsListAdapter = listAdapter;
+    }
+
+    @Override
+    public WidgetsListHeaderHolder newViewHolder(ViewGroup parent) {
+        WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
+                R.layout.widgets_list_row_header, parent, false);
+        header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
+        return new WidgetsListHeaderHolder(header);
+    }
+
+    @Override
+    public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
+            int position) {
+        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+        widgetsListHeader.applyFromItemInfoWithIcon(data);
+        widgetsListHeader.setExpanded(data.isWidgetListShown());
+        widgetsListHeader.setListDrawableState(
+                WidgetsListDrawableState.obtain(
+                        /* isFirst= */ position == 0,
+                        /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
+                        /* isExpanded= */ data.isWidgetListShown()));
+        widgetsListHeader.setOnExpandChangeListener(isExpanded ->
+                mOnHeaderClickListener.onHeaderClicked(
+                        isExpanded,
+                        new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)
+                ));
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.java b/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.java
new file mode 100644
index 0000000..2b7f544
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListLayoutManager.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.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/WidgetsListSearchHeaderHolder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderHolder.java
new file mode 100644
index 0000000..9562af3
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderHolder.java
@@ -0,0 +1,32 @@
+/*
+ * 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 androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * A {@link ViewHolder} for {@link WidgetsListHeader} of an app, which renders the app icon, the app
+ * name, label and a button for showing / hiding widgets.
+ */
+public final class WidgetsListSearchHeaderHolder extends ViewHolder {
+    final WidgetsListHeader mWidgetsListHeader;
+
+    public WidgetsListSearchHeaderHolder(WidgetsListHeader view) {
+        super(view);
+
+        mWidgetsListHeader = view;
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
new file mode 100644
index 0000000..31dd9ee
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.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.widget.picker;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.launcher3.R;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
+
+/**
+ * Binds data from {@link WidgetsListHeaderEntry} to UI elements in {@link WidgetsListHeaderHolder}.
+ */
+public final class WidgetsListSearchHeaderViewHolderBinder implements
+        ViewHolderBinder<WidgetsListSearchHeaderEntry, WidgetsListSearchHeaderHolder> {
+    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) {
+        mLayoutInflater = layoutInflater;
+        mOnHeaderClickListener = onHeaderClickListener;
+        mListDrawableFactory = listDrawableFactory;
+        mWidgetsListAdapter = listAdapter;
+    }
+
+    @Override
+    public WidgetsListSearchHeaderHolder newViewHolder(ViewGroup parent) {
+        WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
+                R.layout.widgets_list_row_header, parent, false);
+        header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable());
+        return new WidgetsListSearchHeaderHolder(header);
+    }
+
+    @Override
+    public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
+            WidgetsListSearchHeaderEntry data, int position) {
+        WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+        widgetsListHeader.applyFromItemInfoWithIcon(data);
+        widgetsListHeader.setExpanded(data.isWidgetListShown());
+        widgetsListHeader.setListDrawableState(
+                WidgetsListDrawableState.obtain(
+                        /* isFirst= */ position == 0,
+                        /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
+                        /* isExpanded= */ data.isWidgetListShown()));
+        widgetsListHeader.setOnExpandChangeListener(isExpanded ->
+                mOnHeaderClickListener.onHeaderClicked(isExpanded,
+                        new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user)));
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableView.java b/src/com/android/launcher3/widget/picker/WidgetsListTableView.java
new file mode 100644
index 0000000..d30e7b6
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableView.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.util.AttributeSet;
+import android.widget.TableLayout;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+/**
+ * Extension of {@link TableLayout} to support the drawable states used by
+ * {@link WidgetsListDrawableState}.
+ */
+public class WidgetsListTableView extends TableLayout {
+
+    @Nullable private WidgetsListDrawableState mListDrawableState;
+
+    public WidgetsListTableView(Context context) {
+        super(context);
+    }
+
+    public WidgetsListTableView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
+    @UiThread
+    public void setListDrawableState(WidgetsListDrawableState state) {
+        if (state == mListDrawableState) return;
+        mListDrawableState = state;
+        refreshDrawableState();
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace);
+        // Augment the state set from the super implementation with the custom states from
+        // mListDrawableState.
+        int[] drawableState =
+                super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length);
+        mergeDrawableStates(drawableState, mListDrawableState.mStateSet);
+        return drawableState;
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
new file mode 100644
index 0000000..9c06558
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -0,0 +1,191 @@
+/*
+ * 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 com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST;
+import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE;
+
+import android.graphics.Bitmap;
+import android.util.Log;
+import android.util.Size;
+import android.view.Gravity;
+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.TableLayout;
+import android.widget.TableRow;
+
+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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Binds data from {@link WidgetsListContentEntry} to UI elements in {@link WidgetsRowViewHolder}.
+ */
+public final class WidgetsListTableViewHolderBinder
+        implements ViewHolderBinder<WidgetsListContentEntry, WidgetsRowViewHolder> {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "WidgetsListRowViewHolderBinder";
+
+    private final LayoutInflater mLayoutInflater;
+    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) {
+        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
+    public WidgetsRowViewHolder newViewHolder(ViewGroup parent) {
+        if (DEBUG) {
+            Log.v(TAG, "\nonCreateViewHolder");
+        }
+
+        WidgetsRowViewHolder viewHolder =
+                new WidgetsRowViewHolder(mLayoutInflater.inflate(
+                        R.layout.widgets_table_container, parent, false));
+        viewHolder.mTableContainer.setBackgroundDrawable(
+                mListDrawableFactory.createContentBackgroundDrawable());
+        return viewHolder;
+    }
+
+    @Override
+    public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
+            int position) {
+        WidgetsListTableView table = holder.mTableContainer;
+        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);
+
+        List<ArrayList<WidgetItem>> widgetItemsTable =
+                WidgetsTableUtils.groupWidgetItemsIntoTable(
+                        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);
+            for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+                TableRow row = (TableRow) table.getChildAt(i);
+                row.setVisibility(View.VISIBLE);
+                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);
+            }
+        }
+    }
+
+    /**
+     * Adds and hides table rows and columns from {@code table} to ensure there is sufficient room
+     * to display {@code widgetItemsTable}.
+     *
+     * <p>Instead of recreating all UI elements in {@code table}, this function recycles all
+     * existing UI elements. Instead of deleting excessive elements, it hides them.
+     */
+    private void recycleTableBeforeBinding(TableLayout table,
+            List<ArrayList<WidgetItem>> widgetItemsTable) {
+        // Hide extra table rows.
+        for (int i = widgetItemsTable.size(); i < table.getChildCount(); i++) {
+            table.getChildAt(i).setVisibility(View.GONE);
+        }
+
+        for (int i = 0; i < widgetItemsTable.size(); i++) {
+            List<WidgetItem> widgetItems = widgetItemsTable.get(i);
+            TableRow tableRow;
+            if (i < table.getChildCount()) {
+                tableRow = (TableRow) table.getChildAt(i);
+            } else {
+                tableRow = new TableRow(table.getContext());
+                tableRow.setGravity(Gravity.TOP);
+                table.addView(tableRow);
+            }
+            if (tableRow.getChildCount() > widgetItems.size()) {
+                for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
+                    tableRow.getChildAt(j).setVisibility(View.GONE);
+                }
+            } else {
+                for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
+                    WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
+                            R.layout.widget_cell, tableRow, false);
+                    // set up touch.
+                    View preview = widget.findViewById(R.id.widget_preview_container);
+                    preview.setOnClickListener(mIconClickListener);
+                    preview.setOnLongClickListener(mIconLongClickListener);
+                    tableRow.addView(widget);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void unbindViewHolder(WidgetsRowViewHolder holder) {
+        int numOfRows = holder.mTableContainer.getChildCount();
+        for (int i = 0; i < numOfRows; i++) {
+            TableRow tableRow = (TableRow) holder.mTableContainer.getChildAt(i);
+            int numOfCols = tableRow.getChildCount();
+            for (int j = 0; j < numOfCols; j++) {
+                WidgetCell widget = (WidgetCell) tableRow.getChildAt(j);
+                widget.clear();
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
new file mode 100644
index 0000000..0b8ca34
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -0,0 +1,195 @@
+/*
+ * 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 com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Size;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+
+import androidx.annotation.Nullable;
+
+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;
+import com.android.launcher3.widget.util.WidgetSizes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** A {@link TableLayout} for showing recommended widgets. */
+public final class WidgetsRecommendationTableLayout extends TableLayout {
+    private static final String TAG = "WidgetsRecommendationTableLayout";
+    private static final float DOWN_SCALE_RATIO = 0.9f;
+    private static final float MAX_DOWN_SCALE_RATIO = 0.5f;
+    private final float mWidgetsRecommendationTableVerticalPadding;
+    private final float mWidgetCellVerticalPadding;
+    private final float mWidgetCellTextViewsHeight;
+
+    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);
+    }
+
+    public WidgetsRecommendationTableLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        // There are 1 row for title, 1 row for dimension and 2 rows for description.
+        mWidgetsRecommendationTableVerticalPadding = 2 * getResources()
+                .getDimensionPixelSize(R.dimen.recommended_widgets_table_vertical_padding);
+        mWidgetCellVerticalPadding = 2 * getResources()
+                .getDimensionPixelSize(R.dimen.widget_cell_vertical_padding);
+        mWidgetCellTextViewsHeight = 4 * getResources().getDimension(R.dimen.widget_cell_font_size);
+    }
+
+    /** Sets a {@link android.view.View.OnLongClickListener} for all widget cells in this table. */
+    public void setWidgetCellLongClickListener(OnLongClickListener onLongClickListener) {
+        mWidgetCellOnLongClickListener = onLongClickListener;
+    }
+
+    /** Sets a {@link android.view.View.OnClickListener} for all widget cells in this table. */
+    public void setWidgetCellOnClickListener(OnClickListener widgetCellOnClickListener) {
+        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}.
+     *
+     * <p>If the content can't fit {@code recommendationTableMaxHeight}, this view will remove a
+     * last row from the {@code recommendedWidgets} until it fits or only one row left. If the only
+     * row still doesn't fit, we scale down the preview image.
+     */
+    public void setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
+            float recommendationTableMaxHeight) {
+        mRecommendationTableMaxHeight = recommendationTableMaxHeight;
+        RecommendationTableData data = fitRecommendedWidgetsToTableSpace(/* previewScale= */ 1f,
+                recommendedWidgets);
+        bindData(data);
+    }
+
+    private void bindData(RecommendationTableData data) {
+        if (data.mRecommendationTable.size() == 0) {
+            setVisibility(GONE);
+            return;
+        }
+
+        removeAllViews();
+
+        for (int i = 0; i < data.mRecommendationTable.size(); i++) {
+            List<WidgetItem> widgetItems = data.mRecommendationTable.get(i);
+            TableRow tableRow = new TableRow(getContext());
+            tableRow.setGravity(Gravity.TOP);
+
+            for (WidgetItem widgetItem : widgetItems) {
+                WidgetCell widgetCell = addItemCell(tableRow);
+                widgetCell.setPreviewSize(widgetItem, data.mPreviewScale);
+                widgetCell.applyFromCellItem(widgetItem,
+                        LauncherAppState.getInstance(getContext()).getWidgetCache());
+                widgetCell.ensurePreview();
+            }
+            addView(tableRow);
+        }
+        setVisibility(VISIBLE);
+    }
+
+    private WidgetCell addItemCell(ViewGroup parent) {
+        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);
+        widget.setAnimatePreview(false);
+        widget.setSourceContainer(CONTAINER_WIDGETS_PREDICTION);
+
+        parent.addView(widget);
+        return widget;
+    }
+
+    private RecommendationTableData fitRecommendedWidgetsToTableSpace(
+            float previewScale,
+            List<ArrayList<WidgetItem>> recommendedWidgetsInTable) {
+        if (previewScale < MAX_DOWN_SCALE_RATIO) {
+            Log.w(TAG, "Hide recommended widgets. Can't down scale previews to " + previewScale);
+            return new RecommendationTableData(List.of(), previewScale);
+        }
+        // A naive estimation of the widgets recommendation table height without inflation.
+        float totalHeight = mWidgetsRecommendationTableVerticalPadding;
+        DeviceProfile deviceProfile = Launcher.getLauncher(getContext()).getDeviceProfile();
+        for (int i = 0; i < recommendedWidgetsInTable.size(); i++) {
+            List<WidgetItem> widgetItems = recommendedWidgetsInTable.get(i);
+            float rowHeight = 0;
+            for (int j = 0; j < widgetItems.size(); j++) {
+                WidgetItem widgetItem = widgetItems.get(j);
+                Size widgetSize = WidgetSizes.getWidgetItemSizePx(getContext(), deviceProfile,
+                        widgetItem);
+                float previewHeight = widgetSize.getHeight() * previewScale;
+                rowHeight = Math.max(rowHeight,
+                        previewHeight + mWidgetCellTextViewsHeight + mWidgetCellVerticalPadding);
+            }
+            totalHeight += rowHeight;
+        }
+
+        if (totalHeight < mRecommendationTableMaxHeight) {
+            return new RecommendationTableData(recommendedWidgetsInTable, previewScale);
+        }
+
+        if (recommendedWidgetsInTable.size() > 1) {
+            // We don't want to scale down widgets preview unless we really need to. Reduce the
+            // num of row by 1 to see if it fits.
+            return fitRecommendedWidgetsToTableSpace(
+                    previewScale,
+                    recommendedWidgetsInTable.subList(/* fromIndex= */0,
+                            /* toIndex= */recommendedWidgetsInTable.size() - 1));
+        }
+
+        float nextPreviewScale = previewScale * DOWN_SCALE_RATIO;
+        return fitRecommendedWidgetsToTableSpace(nextPreviewScale, recommendedWidgetsInTable);
+    }
+
+    /** Data class for the widgets recommendation table and widgets preview scaling. */
+    private class RecommendationTableData {
+        private final List<ArrayList<WidgetItem>> mRecommendationTable;
+        private final float mPreviewScale;
+
+        RecommendationTableData(List<ArrayList<WidgetItem>> recommendationTable,
+                float previewScale) {
+            mRecommendationTable = recommendationTable;
+            mPreviewScale = previewScale;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
new file mode 100644
index 0000000..7671841
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -0,0 +1,304 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher3.widget.picker;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+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;
+
+import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.views.ActivityContext;
+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;
+
+/**
+ * The widgets recycler view.
+ */
+public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouchListener {
+
+    private WidgetsListAdapter mAdapter;
+
+    private final int mScrollbarTop;
+
+    private final Point mFastScrollerOffset = new Point();
+    private boolean mTouchDownOnScroller;
+    private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
+    private int mLastVisibleWidgetContentTableHeight = 0;
+    private int mWidgetHeaderHeight = 0;
+    private final int mSpacingBetweenEntries;
+    @Nullable private OnContentChangeListener mOnContentChangeListener;
+
+    public WidgetsRecyclerView(Context context) {
+        this(context, null);
+    }
+
+    public WidgetsRecyclerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        // API 21 and below only support 3 parameter ctor.
+        super(context, attrs, defStyleAttr);
+        mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+        addOnItemTouchListener(this);
+
+        ActivityContext activity = ActivityContext.lookupContext(getContext());
+        DeviceProfile grid = activity.getDeviceProfile();
+
+        // The spacing used between entries.
+        mSpacingBetweenEntries =
+                getResources().getDimensionPixelSize(R.dimen.widget_list_entry_spacing);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        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);
+    }
+
+    @Override
+    public void setAdapter(Adapter adapter) {
+        super.setAdapter(adapter);
+        mAdapter = (WidgetsListAdapter) adapter;
+    }
+
+    /**
+     * Maps the touch (from 0..1) to the adapter position that should be visible.
+     */
+    @Override
+    public String scrollToPositionAtProgress(float touchFraction) {
+        // Skip early if widgets are not bound.
+        if (isModelNotReady()) {
+            return "";
+        }
+
+        // Stop the scroller if it is scrolling
+        stopScroll();
+
+        int rowCount = mAdapter.getItemCount();
+        float pos = rowCount * touchFraction;
+        int availableScrollHeight = getAvailableScrollHeight();
+        LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
+        layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
+
+        int posInt = (int) ((touchFraction == 1) ? pos - 1 : pos);
+        return mAdapter.getSectionName(posInt);
+    }
+
+    /**
+     * Updates the bounds for the scrollbar.
+     */
+    @Override
+    public void onUpdateScrollbar(int dy) {
+        // Skip early if widgets are not bound.
+        if (isModelNotReady()) {
+            mScrollbar.setThumbOffsetY(-1);
+            return;
+        }
+
+        // Skip early if, there no child laid out in the container.
+        int scrollY = getCurrentScrollY();
+        if (scrollY < 0) {
+            mScrollbar.setThumbOffsetY(-1);
+            return;
+        }
+
+        synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight());
+    }
+
+    @Override
+    public int getCurrentScrollY() {
+        // Skip early if widgets are not bound.
+        if (isModelNotReady() || getChildCount() == 0) {
+            return -1;
+        }
+
+        int rowIndex = -1;
+        View child = null;
+
+        LayoutManager layoutManager = getLayoutManager();
+        if (layoutManager instanceof LinearLayoutManager) {
+            // Use the LayoutManager as the source of truth for visible positions. During
+            // animations, the view group child may not correspond to the visible views that appear
+            // at the top.
+            rowIndex = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
+            child = layoutManager.findViewByPosition(rowIndex);
+        }
+
+        if (child == null) {
+            // If the layout manager returns null for any reason, which can happen before layout
+            // has occurred for the position, then look at the child of this view as a ViewGroup.
+            child = getChildAt(0);
+            rowIndex = getChildPosition(child);
+        }
+
+        for (int i = 0; i < getChildCount(); i++) {
+            View view = getChildAt(i);
+            if (view instanceof TableLayout) {
+                // This assumes there is ever only one content shown in this recycler view.
+                mLastVisibleWidgetContentTableHeight = view.getMeasuredHeight();
+            } else if (view instanceof WidgetsListHeader
+                    && mLastVisibleWidgetContentTableHeight == 0
+                    && view.getMeasuredHeight() > 0) {
+                // This assumes all header views are of the same height.
+                mWidgetHeaderHeight = view.getMeasuredHeight();
+            }
+        }
+
+        int scrollPosition = getItemsHeight(rowIndex);
+        int offset = getLayoutManager().getDecoratedTop(child);
+
+        return getPaddingTop() + scrollPosition - offset;
+    }
+
+    /**
+     * Returns the available scroll height, in pixel.
+     *
+     * <p>If the recycler view can't be scrolled, returns 0.
+     */
+    @Override
+    protected int getAvailableScrollHeight() {
+        // AvailableScrollHeight = Total height of the all items - first page height
+        int firstPageHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
+        int totalHeightOfAllItems = getItemsHeight(/* untilIndex= */ mAdapter.getItemCount());
+        int availableScrollHeight = totalHeightOfAllItems - firstPageHeight;
+        return Math.max(0, availableScrollHeight);
+    }
+
+    private boolean isModelNotReady() {
+        return mAdapter.getItemCount() == 0;
+    }
+
+    @Override
+    public int getScrollBarTop() {
+        return mHeaderViewDimensionsProvider == null
+                ? mScrollbarTop
+                : mHeaderViewDimensionsProvider.getHeaderViewHeight() + mScrollbarTop;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+        if (e.getAction() == MotionEvent.ACTION_DOWN) {
+            mTouchDownOnScroller =
+                    mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
+        }
+        if (mTouchDownOnScroller) {
+            final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+            return result;
+        }
+        return false;
+    }
+
+    @Override
+    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+        if (mTouchDownOnScroller) {
+            mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+        }
+    }
+
+    @Override
+    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+    }
+
+    public void setHeaderViewDimensionsProvider(
+            HeaderViewDimensionsProvider headerViewDimensionsProvider) {
+        mHeaderViewDimensionsProvider = headerViewDimensionsProvider;
+    }
+
+    @Override
+    public void scrollToTop() {
+        if (mScrollbar != null) {
+            mScrollbar.reattachThumbToScroll();
+        }
+
+        if (getLayoutManager() instanceof LinearLayoutManager) {
+            if (getCurrentScrollY() == 0) {
+                // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
+                return;
+            }
+        }
+        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}.
+     *
+     * <p>If the untilIndex is larger than the total number of items in this adapter, returns the
+     * sum of all items' height.
+     */
+    private int getItemsHeight(int untilIndex) {
+        if (untilIndex > mAdapter.getItems().size()) {
+            untilIndex = mAdapter.getItems().size();
+        }
+        int totalItemsHeight = 0;
+        for (int i = 0; i < untilIndex; i++) {
+            WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
+            if (entry instanceof WidgetsListHeaderEntry
+                    || entry instanceof WidgetsListSearchHeaderEntry) {
+                totalItemsHeight += mWidgetHeaderHeight;
+                if (i > 0) {
+                    // Each header contains the spacing between entries as top decoration, except
+                    // the first one.
+                    totalItemsHeight += mSpacingBetweenEntries;
+                }
+            } else if (entry instanceof WidgetsListContentEntry) {
+                totalItemsHeight += mLastVisibleWidgetContentTableHeight;
+            } else {
+                throw new UnsupportedOperationException("Can't estimate height for " + entry);
+            }
+        }
+        return totalItemsHeight;
+    }
+
+    /**
+     * Provides dimensions of the header view that is shown at the top of a
+     * {@link WidgetsRecyclerView}.
+     */
+    public interface HeaderViewDimensionsProvider {
+        /**
+         * Returns the height, in pixels, of the header view that is shown at the top of a
+         * {@link WidgetsRecyclerView}.
+         */
+        int getHeaderViewHeight();
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
similarity index 62%
rename from src/com/android/launcher3/widget/WidgetsRowViewHolder.java
rename to src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
index d26edb6..618e2cb 100644
--- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
@@ -13,25 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.widget;
+package com.android.launcher3.widget.picker;
 
-import android.view.ViewGroup;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.R;
+import android.view.View;
 
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
-public class WidgetsRowViewHolder extends ViewHolder {
+import com.android.launcher3.R;
 
-    public final ViewGroup cellContainer;
-    public final BubbleTextView title;
+/** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
+public final class WidgetsRowViewHolder extends ViewHolder {
 
-    public WidgetsRowViewHolder(ViewGroup v) {
+    public final WidgetsListTableView mTableContainer;
+
+    public WidgetsRowViewHolder(View v) {
         super(v);
 
-        cellContainer = v.findViewById(R.id.widgets_cell_list);
-        title = v.findViewById(R.id.section);
-        title.setAccessibilityDelegate(null);
+        mTableContainer = v.findViewById(R.id.widgets_table);
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
new file mode 100644
index 0000000..65937b6
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
@@ -0,0 +1,87 @@
+/*
+ * 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.search;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.R;
+import com.android.launcher3.popup.PopupDataProvider;
+
+/**
+ * View for a search bar with an edit text with a cancel button.
+ */
+public class LauncherWidgetsSearchBar extends LinearLayout implements WidgetsSearchBar {
+    private WidgetsSearchBarController mController;
+    private ExtendedEditText mEditText;
+    private ImageButton mCancelButton;
+
+    public LauncherWidgetsSearchBar(Context context) {
+        this(context, null, 0);
+    }
+
+    public LauncherWidgetsSearchBar(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LauncherWidgetsSearchBar(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public void initialize(PopupDataProvider dataProvider, SearchModeListener searchModeListener) {
+        mController = new WidgetsSearchBarController(
+                new SimpleWidgetsSearchAlgorithm(dataProvider),
+                mEditText, mCancelButton, searchModeListener);
+    }
+
+    @Override
+    public void reset() {
+        mController.clearSearchResult();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mEditText = findViewById(R.id.widgets_search_bar_edit_text);
+        mCancelButton = findViewById(R.id.widgets_search_cancel_button);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mController.onDestroy();
+    }
+
+    @Override
+    public boolean isSearchBarFocused() {
+        return mEditText.isFocused();
+    }
+
+    @Override
+    public void clearSearchBarFocus() {
+        mController.clearFocus();
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/search/SearchModeListener.java b/src/com/android/launcher3/widget/picker/search/SearchModeListener.java
new file mode 100644
index 0000000..cee7d67
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/SearchModeListener.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.launcher3.widget.picker.search;
+
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import java.util.List;
+
+/**
+ * A listener to help with widgets picker search.
+ */
+public interface SearchModeListener {
+    /**
+     * Notifies the subscriber when user enters widget picker search mode.
+     */
+    void enterSearchMode();
+
+    /**
+     * Notifies the subscriber when user exits widget picker search mode.
+     */
+    void exitSearchMode();
+
+    /**
+     * Notifies the subscriber with search results.
+     */
+    void onSearchResults(List<WidgetsListBaseEntry> entries);
+}
diff --git a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
new file mode 100644
index 0000000..9be3b5f
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget.picker.search;
+
+import static com.android.launcher3.search.StringMatcherUtility.matches;
+
+import android.os.Handler;
+
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.search.SearchAlgorithm;
+import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.search.StringMatcherUtility.StringMatcher;
+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 java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Implementation of {@link SearchAlgorithm} that posts a task to query on the main thread.
+ */
+public final class SimpleWidgetsSearchAlgorithm implements SearchAlgorithm<WidgetsListBaseEntry> {
+
+    private final Handler mResultHandler;
+    private final PopupDataProvider mDataProvider;
+
+    public SimpleWidgetsSearchAlgorithm(PopupDataProvider dataProvider) {
+        mResultHandler = new Handler();
+        mDataProvider = dataProvider;
+    }
+
+    @Override
+    public void doSearch(String query, SearchCallback<WidgetsListBaseEntry> callback) {
+        ArrayList<WidgetsListBaseEntry> result = getFilteredWidgets(mDataProvider, query);
+        mResultHandler.post(() -> callback.onSearchResult(query, result));
+    }
+
+    @Override
+    public void cancel(boolean interruptActiveRequests) {
+        if (interruptActiveRequests) {
+            mResultHandler.removeCallbacksAndMessages(/*token= */null);
+        }
+    }
+
+    /**
+     * Returns entries for all matched widgets
+     */
+    public static ArrayList<WidgetsListBaseEntry> getFilteredWidgets(
+            PopupDataProvider dataProvider, String input) {
+        ArrayList<WidgetsListBaseEntry> results = new ArrayList<>();
+        dataProvider.getAllWidgets().stream()
+                .filter(entry -> entry instanceof WidgetsListHeaderEntry)
+                .forEach(headerEntry -> {
+                    List<WidgetItem> matchedWidgetItems = filterWidgetItems(
+                            input, headerEntry.mPkgItem.title.toString(), headerEntry.mWidgets);
+                    if (matchedWidgetItems.size() > 0) {
+                        results.add(new WidgetsListSearchHeaderEntry(headerEntry.mPkgItem,
+                                headerEntry.mTitleSectionName, matchedWidgetItems));
+                        results.add(new WidgetsListContentEntry(headerEntry.mPkgItem,
+                                headerEntry.mTitleSectionName, matchedWidgetItems));
+                    }
+                });
+        return results;
+    }
+
+    private static List<WidgetItem> filterWidgetItems(String query, String packageTitle,
+            List<WidgetItem> items) {
+        StringMatcher matcher = StringMatcher.getInstance();
+        if (matches(query, packageTitle, matcher)) {
+            return items;
+        }
+        return items.stream()
+                .filter(item -> matches(query, item.label, matcher))
+                .collect(Collectors.toList());
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
new file mode 100644
index 0000000..44a5e80
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
@@ -0,0 +1,47 @@
+/*
+ * 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.search;
+
+import com.android.launcher3.popup.PopupDataProvider;
+
+/**
+ * Interface for a widgets picker search bar.
+ */
+public interface WidgetsSearchBar {
+    /**
+     * Attaches a controller to the search bar which interacts with {@code searchModeListener}.
+     */
+    void initialize(PopupDataProvider dataProvider, SearchModeListener searchModeListener);
+
+    /**
+     * Clears search bar.
+     */
+    void reset();
+
+    /** Returns {@code true} if the search bar is in focus. */
+    boolean isSearchBarFocused();
+
+    /**
+     * Clears focus from search bar.
+     */
+    void clearSearchBarFocus();
+
+    /**
+     * Sets the vertical location, in pixels, of this search bar relative to its top position.
+     */
+    void setTranslationY(float translationY);
+}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
new file mode 100644
index 0000000..2751a52
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
@@ -0,0 +1,137 @@
+/*
+ * 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.search;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.ImageButton;
+
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.search.SearchAlgorithm;
+import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import java.util.ArrayList;
+
+/**
+ * Controller for a search bar with an edit text and a cancel button.
+ */
+public class WidgetsSearchBarController implements TextWatcher,
+        SearchCallback<WidgetsListBaseEntry>,  ExtendedEditText.OnBackKeyListener,
+        View.OnKeyListener {
+    private static final String TAG = "WidgetsSearchBarController";
+    private static final boolean DEBUG = false;
+
+    protected SearchAlgorithm<WidgetsListBaseEntry> mSearchAlgorithm;
+    protected ExtendedEditText mInput;
+    protected ImageButton mCancelButton;
+    protected SearchModeListener mSearchModeListener;
+    protected String mQuery;
+
+    public WidgetsSearchBarController(
+            SearchAlgorithm<WidgetsListBaseEntry> algo, ExtendedEditText editText,
+            ImageButton cancelButton, SearchModeListener searchModeListener) {
+        mSearchAlgorithm = algo;
+        mInput = editText;
+        mInput.addTextChangedListener(this);
+        mInput.setOnBackKeyListener(this);
+        mInput.setOnKeyListener(this);
+        mCancelButton = cancelButton;
+        mCancelButton.setOnClickListener(v -> clearSearchResult());
+        mSearchModeListener = searchModeListener;
+    }
+
+    @Override
+    public void afterTextChanged(final Editable s) {
+        mQuery = s.toString();
+        if (mQuery.isEmpty()) {
+            mSearchAlgorithm.cancel(/* interruptActiveRequests= */ true);
+            mSearchModeListener.exitSearchMode();
+            mCancelButton.setVisibility(GONE);
+        } else {
+            mSearchAlgorithm.cancel(/* interruptActiveRequests= */ false);
+            mSearchModeListener.enterSearchMode();
+            mSearchAlgorithm.doSearch(mQuery, this);
+            mCancelButton.setVisibility(VISIBLE);
+        }
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+        // Do nothing.
+    }
+
+    @Override
+    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+        // Do nothing.
+    }
+
+    @Override
+    public void onSearchResult(String query, ArrayList<WidgetsListBaseEntry> items) {
+        if (DEBUG) {
+            Log.d(TAG, "onSearchResult query: " + query + " items: " + items);
+        }
+        mSearchModeListener.onSearchResults(items);
+    }
+
+    @Override
+    public void onAppendSearchResult(String query, ArrayList<WidgetsListBaseEntry> items) {
+        // Not needed.
+    }
+
+    @Override
+    public void clearSearchResult() {
+        // Any existing search session will be cancelled by setting text to empty.
+        mInput.setText("");
+    }
+
+    /**
+     * Cleans up after search is no longer needed.
+     */
+    public void onDestroy() {
+        mSearchAlgorithm.destroy();
+    }
+
+    @Override
+    public boolean onBackKey() {
+        clearFocus();
+        return true;
+    }
+
+    @Override
+    public boolean onKey(View view, int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
+            clearFocus();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Clears focus from edit text.
+     */
+    public void clearFocus() {
+        mInput.clearFocus();
+        mInput.hideKeyboard();
+    }
+}
diff --git a/src/com/android/launcher3/widget/util/WidgetSizes.java b/src/com/android/launcher3/widget/util/WidgetSizes.java
new file mode 100644
index 0000000..451ed6e
--- /dev/null
+++ b/src/com/android/launcher3/widget/util/WidgetSizes.java
@@ -0,0 +1,177 @@
+/*
+ * 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.util;
+
+import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
+
+
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Size;
+import android.util.SizeF;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.model.WidgetItem;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** A utility class for widget sizes related calculations. */
+public final class WidgetSizes {
+
+    /**
+     * Returns the list of all possible sizes, in dp, for a widget of given spans on this device.
+     *
+     * <p>The returned sizes already take into account the system padding, and whether it is applied
+     * or not in that specific configuration.
+     */
+    public static ArrayList<SizeF> getWidgetPaddedSizes(Context context, ComponentName provider,
+            int spanX, int spanY) {
+        Rect padding = getDefaultPaddingForWidget(context, provider, /* padding= */ null);
+
+        ArrayList<SizeF> sizes = new ArrayList<>(2);
+        final float density = context.getResources().getDisplayMetrics().density;
+        final Point cellSize = new Point();
+
+        for (DeviceProfile profile : LauncherAppState.getIDP(context).supportedProfiles) {
+            Size widgetSizePx = getWidgetSizePx(profile, spanX, spanY, cellSize);
+            if (!profile.shouldInsetWidgets()) {
+                widgetSizePx = new Size(widgetSizePx.getWidth() - padding.left - padding.right,
+                        widgetSizePx.getHeight() - padding.top - padding.bottom);
+            }
+            sizes.add(new SizeF(widgetSizePx.getWidth() / density,
+                    widgetSizePx.getHeight() / density));
+        }
+        return sizes;
+    }
+
+    /** Returns the size, in pixels, a widget of given spans & {@code profile}. */
+    public static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY) {
+        return getWidgetSizePx(profile, spanX, spanY, /* recycledCellSize= */ null);
+    }
+
+    /**
+     * Returns the size, in pixels and removing padding, a widget of given spans & {@code profile}.
+     */
+    public static Size getWidgetPaddedSizePx(Context context, ComponentName component,
+            DeviceProfile profile, int spanX, int spanY) {
+        Size size = getWidgetSizePx(profile, spanX, spanY);
+        if (profile.shouldInsetWidgets()) {
+            return size;
+        }
+        Rect padding = getDefaultPaddingForWidget(context, component, /* padding= */ null);
+        return new Size(size.getWidth() - padding.left - padding.right,
+                size.getHeight() - padding.top - padding.bottom);
+    }
+
+    /**
+     * Returns the size of a WidgetItem.
+     */
+    public static Size getWidgetItemSizePx(Context context, DeviceProfile profile,
+            WidgetItem widgetItem) {
+        if (widgetItem.isShortcut()) {
+            int dimension = profile.allAppsIconSizePx + 2 * context.getResources()
+                    .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
+            return new Size(dimension, dimension);
+        }
+        return getWidgetPaddedSizePx(context, widgetItem.componentName, profile, widgetItem.spanX,
+                widgetItem.spanY);
+    }
+
+    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;
+        if (recycledCellSize == null) {
+            recycledCellSize = new Point();
+        }
+        profile.getCellSize(recycledCellSize);
+        return new Size(((spanX * recycledCellSize.x) + hBorderSpacing),
+                ((spanY * recycledCellSize.y) + vBorderSpacing));
+    }
+
+    /**
+     * Updates a given {@code widgetView} with size, {@code spanX}, {@code spanY}.
+     *
+     * <p>On Android S+, it also updates the given {@code widgetView} with a list of sizes derived
+     * from {@code spanX}, {@code spanY} in all supported device profiles.
+     */
+    public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Context context,
+            int spanX, int spanY) {
+        AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
+        int widgetId = widgetView.getAppWidgetId();
+        if (widgetId <= 0) {
+            return;
+        }
+        Bundle sizeOptions = getWidgetSizeOptions(context, widgetView.getAppWidgetInfo().provider,
+                spanX, spanY);
+        if (sizeOptions.<SizeF>getParcelableArrayList(
+                AppWidgetManager.OPTION_APPWIDGET_SIZES).equals(
+                widgetManager.getAppWidgetOptions(widgetId).<SizeF>getParcelableArrayList(
+                        AppWidgetManager.OPTION_APPWIDGET_SIZES))) {
+            return;
+        }
+        widgetManager.updateAppWidgetOptions(widgetId, sizeOptions);
+    }
+
+    /**
+     * Returns the bundle to be used as the default options for a widget with provided size.
+     */
+    public static Bundle getWidgetSizeOptions(Context context, ComponentName provider, int spanX,
+            int spanY) {
+        ArrayList<SizeF> paddedSizes = getWidgetPaddedSizes(context, provider, spanX, spanY);
+
+        Rect rect = getMinMaxSizes(paddedSizes);
+        Bundle options = new Bundle();
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom);
+        options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes);
+        return options;
+    }
+
+    /**
+     * Returns the min and max widths and heights given a list of sizes, in dp.
+     *
+     * @param sizes List of sizes to get the min/max from.
+     * @return A rectangle with the left (resp. top) is used for the min width (resp. height) and
+     * the right (resp. bottom) for the max. The returned rectangle is set with 0s if the list is
+     * empty.
+     */
+    private static Rect getMinMaxSizes(List<SizeF> sizes) {
+        if (sizes.isEmpty()) {
+            return new Rect();
+        } else {
+            SizeF first = sizes.get(0);
+            Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(),
+                    (int) first.getWidth(), (int) first.getHeight());
+            for (int i = 1; i < sizes.size(); i++) {
+                result.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight());
+            }
+            return result;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
new file mode 100644
index 0000000..54aaf93
--- /dev/null
+++ b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.util;
+
+import com.android.launcher3.model.WidgetItem;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** An utility class which groups {@link WidgetItem}s into a table. */
+public final class WidgetsTableUtils {
+
+    /**
+     * Groups widgets in the following order:
+     * 1. Widgets always go before shortcuts.
+     * 2. Widgets with smaller horizontal spans will be shown first.
+     * 3. If widgets have the same horizontal spans, then widgets with a smaller vertical spans will
+     *    go first.
+     * 4. If both widgets have the same horizontal and vertical spans, they will use the same order
+     *    from the given {@code widgetItems}.
+     */
+    private static final Comparator<WidgetItem> WIDGET_SHORTCUT_COMPARATOR = (item, otherItem) -> {
+        if (item.widgetInfo != null && otherItem.widgetInfo == null) return -1;
+
+        if (item.widgetInfo == null && otherItem.widgetInfo != null) return 1;
+        if (item.spanX == otherItem.spanX) {
+            if (item.spanY == otherItem.spanY) return 0;
+            return item.spanY > otherItem.spanY ? 1 : -1;
+        }
+        return item.spanX > otherItem.spanX ? 1 : -1;
+    };
+
+
+    /**
+     * Groups widgets items into a 2D array which matches their appearance in a UI table.
+     *
+     * <p>Grouping:
+     * 1. Widgets and shortcuts never group together in the same row.
+     * 2. The ordered widgets are grouped together in the same row until their total horizontal
+     *    spans exceed the {@code maxSpansPerRow} - 1.
+     * 3. The order shortcuts are grouped together in the same row until their total horizontal
+     *    spans exceed the {@code maxSpansPerRow} - 1.
+     * 4. If there is only one widget in a row, its width may exceed the {@code maxSpansPerRow}.
+     *
+     * <p>Let's say the {@code maxSpansPerRow} is set to 6. Widgets can be grouped in the same row
+     * if their total horizontal spans added don't exceed 5.
+     * Example 1: Row 1: 2x2, 2x3, 1x1. Total horizontal spans is 5. This is okay.
+     * Example 2: Row 1: 2x2, 4x3, 1x1. the total horizontal spans is 7. This is wrong. 4x3 and 1x1
+     * 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(
+            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) {
+            if (widgetItemsAtRow == null) {
+                widgetItemsAtRow = new ArrayList<>();
+                widgetItemsTable.add(widgetItemsAtRow);
+            }
+            int numOfWidgetItems = widgetItemsAtRow.size();
+            int totalHorizontalSpan = widgetItemsAtRow.stream().map(item -> item.spanX)
+                    .reduce(/* default= */ 0, Integer::sum);
+            int totalHorizontalSpanAfterAddingWidget = widgetItem.spanX + totalHorizontalSpan;
+            if (numOfWidgetItems == 0) {
+                widgetItemsAtRow.add(widgetItem);
+            } else if (
+                    // The max spans per row is reduced by 1 to ensure we don't pack too many
+                    // 1xn widgets on the same row, which may reduce the space for rendering a
+                    // widget's description.
+                    totalHorizontalSpanAfterAddingWidget <= maxSpansPerRow - 1
+                            && widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))) {
+                // Group items in the same row if
+                // 1. they are with the same type, i.e. a row can only have widgets or shortcuts but
+                //    never a mix of both.
+                // 2. the total number of horizontal spans are smaller than or equal to
+                //    MAX_SPAN_PER_ROW. If an item has a horizontal span > MAX_SPAN_PER_ROW, we just
+                //    place it in its own row regardless of the horizontal span limit.
+                widgetItemsAtRow.add(widgetItem);
+            } else {
+                widgetItemsAtRow = new ArrayList<>();
+                widgetItemsTable.add(widgetItemsAtRow);
+                widgetItemsAtRow.add(widgetItem);
+            }
+        }
+        return widgetItemsTable;
+    }
+}
diff --git a/src/com/android/launcher3/workprofile/PersonalWorkPagedView.java b/src/com/android/launcher3/workprofile/PersonalWorkPagedView.java
new file mode 100644
index 0000000..8b05a0d
--- /dev/null
+++ b/src/com/android/launcher3/workprofile/PersonalWorkPagedView.java
@@ -0,0 +1,92 @@
+/*
+ * 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.workprofile;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.launcher3.PagedView;
+
+/**
+ *  A {@link PagedView} for showing different views for the personal and work profile respectively.
+ */
+public class PersonalWorkPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
+
+    static final float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
+    static final float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
+    static final float TOUCH_SLOP_DAMPING_FACTOR = 4;
+
+    public PersonalWorkPagedView(Context context) {
+        this(context, null);
+    }
+
+    public PersonalWorkPagedView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PersonalWorkPagedView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected String getCurrentPageDescription() {
+        // Not necessary, tab-bar already has two tabs with their own descriptions.
+        return "";
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        mPageIndicator.setScroll(l, mMaxScroll);
+    }
+
+    @Override
+    protected void determineScrollingStart(MotionEvent ev) {
+        float absDeltaX = Math.abs(ev.getX() - getDownMotionX());
+        float absDeltaY = Math.abs(ev.getY() - getDownMotionY());
+
+        if (Float.compare(absDeltaX, 0f) == 0) return;
+
+        float slope = absDeltaY / absDeltaX;
+        float theta = (float) Math.atan(slope);
+
+        if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
+            cancelCurrentPageLongPress();
+        }
+
+        if (theta > MAX_SWIPE_ANGLE) {
+            return;
+        } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
+            theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
+            float extraRatio = (float)
+                    Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
+            super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
+        } else {
+            super.determineScrollingStart(ev);
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    @Override
+    protected boolean canScroll(float absVScroll, float absHScroll) {
+        return (absHScroll > absVScroll) && super.canScroll(absVScroll, absHScroll);
+    }
+}
diff --git a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
new file mode 100644
index 0000000..11185fb
--- /dev/null
+++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.workprofile;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.pageindicators.PageIndicator;
+
+/**
+ * Supports two indicator colors, dedicated for personal and work tabs.
+ */
+public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageIndicator {
+    private OnActivePageChangedListener mOnActivePageChangedListener;
+    private int mLastActivePage = 0;
+
+    public PersonalWorkSlidingTabStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * Highlights tab with index pos
+     */
+    public void updateTabTextColor(int pos) {
+        for (int i = 0; i < getChildCount(); i++) {
+            Button tab = (Button) getChildAt(i);
+            tab.setSelected(i == pos);
+        }
+    }
+
+    @Override
+    public void setScroll(int currentScroll, int totalScroll) {
+    }
+
+    @Override
+    public void setActiveMarker(int activePage) {
+        updateTabTextColor(activePage);
+        if (mOnActivePageChangedListener != null && mLastActivePage != activePage) {
+            mOnActivePageChangedListener.onActivePageChanged(activePage);
+        }
+        mLastActivePage = activePage;
+    }
+
+    public void setOnActivePageChangedListener(OnActivePageChangedListener listener) {
+        mOnActivePageChangedListener = listener;
+    }
+
+    @Override
+    public void setMarkersCount(int numMarkers) {
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when an active page has been changed.
+     */
+    public interface OnActivePageChangedListener {
+        /** Called when the active page has been changed. */
+        void onActivePageChanged(int currentActivePage);
+    }
+}
diff --git a/src_build_config/BuildConfig.java b/src_build_config/com/android/launcher3/BuildConfig.java
similarity index 100%
rename from src_build_config/BuildConfig.java
rename to src_build_config/com/android/launcher3/BuildConfig.java
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
deleted file mode 100644
index c57f07d..0000000
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.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.systemui.plugins;
-
-import android.app.Activity;
-import android.view.ViewGroup;
-import android.widget.EditText;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this plugin interface to replace the all apps recycler view of the all apps drawer.
- */
-@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
-public interface AllAppsSearchPlugin extends Plugin {
-    String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
-    int VERSION = 3;
-
-    /** Following are the order that these methods should be called. */
-    void setup(ViewGroup parent, Activity activity, float allAppsContainerHeight);
-
-    /**
-     * When drag starts, pass window inset related fields and the progress to indicate
-     * whether user is swiping down or swiping up
-     */
-    void onDragStart(float progress);
-
-    /** progress is between [0, 1] 1: down, 0: up */
-    void setProgress(float progress);
-
-    /** Called when container animation stops, so that plugin can perform cleanups */
-    void onAnimationEnd(float progress);
-
-    /** pass over the search box object */
-    void setEditText(EditText editText);
-}
diff --git a/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java b/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java
deleted file mode 100644
index 15a0ffa..0000000
--- a/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.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.systemui.plugins;
-
-import android.content.ComponentName;
-import android.os.UserHandle;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Plugin interface which sends app launch events.
- */
-@ProvidesInterface(action = AppLaunchEventsPlugin.ACTION, version = AppLaunchEventsPlugin.VERSION)
-public interface AppLaunchEventsPlugin extends Plugin {
-    String ACTION = "com.android.systemui.action.PLUGIN_APP_EVENTS";
-    int VERSION = 1;
-
-    /**
-     * Receives onStartShortcut event from
-     * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
-     */
-    void onStartShortcut(String packageName, String shortcutId, UserHandle user, String container);
-
-    /**
-     * Receives onStartApp event from
-     * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
-     */
-    void onStartApp(ComponentName componentName, UserHandle user, String container);
-
-    /**
-     * Receives onDismissApp event from
-     * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
-     */
-    void onDismissApp(ComponentName componentName, UserHandle user, String container);
-
-    /**
-     * Receives onReturnedToHome event from
-     * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
-     */
-    void onReturnedToHome();
-}
diff --git a/src_plugins/com/android/systemui/plugins/OWNERS b/src_plugins/com/android/systemui/plugins/OWNERS
deleted file mode 100644
index 0514999..0000000
--- a/src_plugins/com/android/systemui/plugins/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# When changing interface for this plugin OR when increasing version code, please add Alex
-# Only add other owners if Alex is not available
-per-file AllAppsSearchPlugin.java, globs = set noparent
-per-file AllAppsSearchPlugin.java = alexmang@google.com, hyunyoungs@google.com, sunnygoyal@google.com, twickham@google.com
diff --git a/src_plugins/com/android/systemui/plugins/UserEventPlugin.java b/src_plugins/com/android/systemui/plugins/UserEventPlugin.java
deleted file mode 100644
index 0e3664a..0000000
--- a/src_plugins/com/android/systemui/plugins/UserEventPlugin.java
+++ /dev/null
@@ -1,34 +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 com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this plugin interface to access user event log on the device for prototype purpose.
- * NOTE: plugin is for internal prototype only and is not visible in production environment.
- */
-@ProvidesInterface(action = UserEventPlugin.ACTION, version = UserEventPlugin.VERSION)
-public interface UserEventPlugin extends Plugin {
-    String ACTION = "com.android.launcher3.action.PLUGIN_USER_EVENT_LOG";
-    int VERSION = 1;
-
-    /**
-     * Callback to be triggered whenever an user event occurs.
-     */
-    void onUserEvent(Object event);
-}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
index dcb4636..abce2a2 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
@@ -21,11 +21,10 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
-import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 
 /**
  * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
@@ -34,12 +33,7 @@
 
     public LoaderResults(LauncherAppState app, BgDataModel dataModel,
             AllAppsList allAppsList, Callbacks[] callbacks) {
-        this(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
-    }
-
-    public LoaderResults(LauncherAppState app, BgDataModel dataModel,
-            AllAppsList allAppsList, Callbacks[] callbacks, LooperExecutor executor) {
-        super(app, dataModel, allAppsList, callbacks, executor);
+        super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
     }
 
     @Override
@@ -53,8 +47,8 @@
 
     @Override
     public void bindWidgets() {
-        final ArrayList<WidgetListRowEntry> widgets =
-                mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext());
+        final List<WidgetsListBaseEntry> widgets =
+                mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
         executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
     }
 }
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 9d87788..631067b 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -5,20 +5,21 @@
 
 import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
 
+import static java.util.stream.Collectors.toList;
+
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
 
 import androidx.annotation.Nullable;
+import androidx.collection.ArrayMap;
 
 import com.android.launcher3.AppFilter;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.config.FeatureFlags;
@@ -26,21 +27,25 @@
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.widget.WidgetItemComparator;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
+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.picker.WidgetsDiffReporter;
 
 import java.util.ArrayList;
-import java.util.Collections;
+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.
@@ -51,38 +56,56 @@
 
     // True is the widget support is disabled.
     public static final boolean GO_DISABLE_WIDGETS = false;
+    public static final boolean GO_DISABLE_NOTIFICATION_DOTS = false;
 
     private static final String TAG = "WidgetsModel";
     private static final boolean DEBUG = false;
 
-    /* Map of widgets and shortcuts that are tracked per package. */
-    private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList = new MultiHashMap<>();
+    private static final ComponentName CONVERSATION_WIDGET = ComponentName.createRelative(
+            "com.android.systemui", ".people.widget.PeopleSpaceWidgetProvider");
 
-    private AppFilter mAppFilter;
+    /* Map of widgets and shortcuts that are tracked per package. */
+    private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
 
     /**
-     * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
-     * are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
-     * is not sorted. This list is sorted at the UI when using
-     * {@link com.android.launcher3.widget.WidgetsDiffReporter}
+     * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row
+     * are sorted (based on label and user), but the overall list of
+     * {@link WidgetsListBaseEntry}s is not sorted. This list is sorted at the UI when using
+     * {@link WidgetsDiffReporter}
      *
-     * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+     * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
      */
-    public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
-        ArrayList<WidgetListRowEntry> result = new ArrayList<>();
+    public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
+        ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
         AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
 
-        WidgetItemComparator widgetComparator = new WidgetItemComparator();
-        for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
-            WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
-            row.titleSectionName = (row.pkgItem.title == null) ? "" :
-                    indexer.computeSectionName(row.pkgItem.title);
-            Collections.sort(row.widgets, widgetComparator);
-            result.add(row);
+        for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
+            PackageItemInfo pkgItem = entry.getKey();
+            List<WidgetItem> widgetItems = entry.getValue();
+            String sectionName = (pkgItem.title == null) ? "" :
+                    indexer.computeSectionName(pkgItem.title);
+            result.add(new WidgetsListHeaderEntry(pkgItem, sectionName, widgetItems));
+            result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems));
         }
         return result;
     }
 
+    /** Returns a mapping of packages to their widgets without static shortcuts. */
+    public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
+        Map<PackageUserKey, List<WidgetItem>> packagesToWidgets = new HashMap<>();
+        mWidgetsList.forEach((packageItemInfo, widgetsAndShortcuts) -> {
+            List<WidgetItem> widgets = widgetsAndShortcuts.stream()
+                        .filter(item -> item.widgetInfo != null)
+                        .collect(toList());
+            if (widgets.size() > 0) {
+                packagesToWidgets.put(
+                        new PackageUserKey(packageItemInfo.packageName, packageItemInfo.user),
+                        widgets);
+            }
+        });
+        return packagesToWidgets;
+    }
+
     /**
      * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
      *                    only widgets and shortcuts associated with the package/user are.
@@ -137,98 +160,38 @@
             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
         }
 
-        // Temporary list for {@link PackageItemInfos} to avoid having to go through
+        // Temporary cache for {@link PackageItemInfos} to avoid having to go through
         // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
-        HashMap<String, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
+        PackageItemInfoCache packageItemInfoCache = new PackageItemInfoCache();
 
-        // clear the lists.
         if (packageUser == null) {
+            // Clear the list if this is an update on all widgets and shortcuts.
             mWidgetsList.clear();
         } else {
-            // Only clear the widgets for the given package/user.
-            PackageItemInfo packageItem = null;
-            for (PackageItemInfo item : mWidgetsList.keySet()) {
-                if (item.packageName.equals(packageUser.mPackageName)) {
-                    packageItem = item;
-                    break;
-                }
-            }
-            if (packageItem != null) {
-                // We want to preserve the user that was on the packageItem previously,
-                // so add it to tmpPackageItemInfos here to avoid creating a new entry.
-                tmpPackageItemInfos.put(packageItem.packageName, packageItem);
-
-                Iterator<WidgetItem> widgetItemIterator = mWidgetsList.get(packageItem).iterator();
-                while (widgetItemIterator.hasNext()) {
-                    WidgetItem nextWidget = widgetItemIterator.next();
-                    if (nextWidget.componentName.getPackageName().equals(packageUser.mPackageName)
-                            && nextWidget.user.equals(packageUser.mUser)) {
-                        widgetItemIterator.remove();
-                    }
-                }
-            }
+            // Otherwise, only clear the widgets and shortcuts for the changed package.
+            mWidgetsList.remove(
+                    packageItemInfoCache.getOrCreate(new WidgetPackageOrCategoryKey(packageUser)));
         }
 
-        InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
-        UserHandle myUser = Process.myUserHandle();
-
         // add and update.
-        for (WidgetItem item : rawWidgetsShortcuts) {
-            if (item.widgetInfo != null) {
-                if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) {
-                    // Widget is hidden from picker
-                    continue;
-                }
-
-                // Ensure that all widgets we show can be added on a workspace of this size
-                int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX);
-                int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY);
-                if (minSpanX > idp.numColumns || minSpanY > idp.numRows) {
-                    if (DEBUG) {
-                        Log.d(TAG, String.format(
-                                "Widget %s : (%d X %d) can't fit on this device",
-                                item.componentName, minSpanX, minSpanY));
-                    }
-                    continue;
-                }
-            }
-
-            if (mAppFilter == null) {
-                mAppFilter = AppFilter.newInstance(app.getContext());
-            }
-            if (!mAppFilter.shouldShowApp(item.componentName)) {
-                if (DEBUG) {
-                    Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
-                            item.componentName));
-                }
-                continue;
-            }
-
-            String packageName = item.componentName.getPackageName();
-            PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName);
-            if (pInfo == null) {
-                pInfo = new PackageItemInfo(packageName);
-                pInfo.user = item.user;
-                tmpPackageItemInfos.put(packageName,  pInfo);
-            } else if (!myUser.equals(pInfo.user)) {
-                // Keep updating the user, until we get the primary user.
-                pInfo.user = item.user;
-            }
-            mWidgetsList.addToList(pInfo, item);
-        }
+        mWidgetsList.putAll(rawWidgetsShortcuts.stream()
+                .filter(new WidgetValidityCheck(app))
+                .collect(Collectors.groupingBy(item ->
+                        packageItemInfoCache.getOrCreate(getWidgetPackageOrCategoryKey(item))
+                )));
 
         // Update each package entry
         IconCache iconCache = app.getIconCache();
-        for (PackageItemInfo p : tmpPackageItemInfos.values()) {
+        for (PackageItemInfo p : packageItemInfoCache.values()) {
             iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
         }
     }
 
     public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
             LauncherAppState app) {
-        for (Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
+        for (Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
             if (packageNames.contains(entry.getKey().packageName)) {
-                ArrayList<WidgetItem> items = entry.getValue();
+                List<WidgetItem> items = entry.getValue();
                 int count = items.size();
                 for (int i = 0; i < count; i++) {
                     WidgetItem item = items.get(i);
@@ -248,7 +211,7 @@
 
     public WidgetItem getWidgetProviderInfoByProviderName(
             ComponentName providerName) {
-        ArrayList<WidgetItem> widgetsList = mWidgetsList.get(
+        List<WidgetItem> widgetsList = mWidgetsList.get(
                 new PackageItemInfo(providerName.getPackageName()));
         if (widgetsList == null) {
             return null;
@@ -261,4 +224,117 @@
         }
         return null;
     }
+
+    /** 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);
+        }
+        return new PackageItemInfo(provider.getPackageName());
+    }
+
+    private WidgetPackageOrCategoryKey getWidgetPackageOrCategoryKey(WidgetItem item) {
+        if (CONVERSATION_WIDGET.equals(item.componentName)) {
+            return new WidgetPackageOrCategoryKey(PackageItemInfo.CONVERSATIONS, item.user);
+        }
+        return new WidgetPackageOrCategoryKey(item.componentName.getPackageName(), item.user);
+    }
+
+    private static class WidgetValidityCheck implements Predicate<WidgetItem> {
+
+        private final InvariantDeviceProfile mIdp;
+        private final AppFilter mAppFilter;
+
+        WidgetValidityCheck(LauncherAppState app) {
+            mIdp = app.getInvariantDeviceProfile();
+            mAppFilter = new AppFilter(app.getContext());
+        }
+
+        @Override
+        public boolean test(WidgetItem item) {
+            if (item.widgetInfo != null) {
+                if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) {
+                    // Widget is hidden from picker
+                    return false;
+                }
+
+                // Ensure that all widgets we show can be added on a workspace of this size
+                if (!item.widgetInfo.isMinSizeFulfilled()) {
+                    if (DEBUG) {
+                        Log.d(TAG, String.format(
+                                "Widget %s : can't fit on this device with a grid size: %dx%d",
+                                item.componentName, mIdp.numColumns, mIdp.numRows));
+                    }
+                    return false;
+                }
+            }
+            if (!mAppFilter.shouldShowApp(item.componentName)) {
+                if (DEBUG) {
+                    Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
+                            item.componentName));
+                }
+                return false;
+            }
+
+            return true;
+        }
+    }
+
+    /** 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<>();
+
+        PackageItemInfo getOrCreate(WidgetPackageOrCategoryKey key) {
+            PackageItemInfo pInfo = mMap.get(key);
+            if (pInfo == null) {
+                pInfo = new PackageItemInfo(key.mPackage, key.mCategory);
+                pInfo.user = key.mUser;
+                mMap.put(key,  pInfo);
+            }
+            return pInfo;
+        }
+
+        Collection<PackageItemInfo> values() {
+            return mMap.values();
+        }
+    }
 }
\ No newline at end of file
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index 6fd147a..4407fe1 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -18,12 +18,22 @@
 
 import android.app.Person;
 import android.content.pm.ShortcutInfo;
+import android.view.Display;
 
 import com.android.launcher3.Utilities;
 
 public class ApiWrapper {
 
+    public static final boolean TASKBAR_DRAWN_IN_PROCESS = false;
+
     public static Person[] getPersons(ShortcutInfo si) {
         return Utilities.EMPTY_PERSON_ARRAY;
     }
+
+    /**
+     * Returns true if the display is an internal displays
+     */
+    public static boolean isInternalDisplay(Display display) {
+        return display.getDisplayId() == Display.DEFAULT_DISPLAY;
+    }
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java b/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java
deleted file mode 100644
index b3aa365..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java
+++ /dev/null
@@ -1,128 +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;
-
-import static android.app.WallpaperManager.FLAG_SYSTEM;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.util.Pair;
-
-import com.android.launcher3.uioverrides.dynamicui.ColorExtractionAlgorithm;
-import com.android.launcher3.uioverrides.dynamicui.WallpaperColorsCompat;
-import com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompat;
-import com.android.launcher3.util.MainThreadInitializedObject;
-
-import java.util.ArrayList;
-
-public class WallpaperColorInfo implements WallpaperManagerCompat.OnColorsChangedListenerCompat {
-
-    private static final int MAIN_COLOR_LIGHT = 0xffdadce0;
-    private static final int MAIN_COLOR_DARK = 0xff202124;
-    private static final int MAIN_COLOR_REGULAR = 0xff000000;
-
-    private static final int FALLBACK_COLOR = Color.WHITE;
-
-    public static final MainThreadInitializedObject<WallpaperColorInfo> INSTANCE =
-            new MainThreadInitializedObject<>(WallpaperColorInfo::new);
-
-    private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
-    private final WallpaperManagerCompat mWallpaperManager;
-    private final ColorExtractionAlgorithm mExtractionType;
-    private int mMainColor;
-    private int mSecondaryColor;
-    private boolean mIsDark;
-    private boolean mSupportsDarkText;
-
-    private OnChangeListener[] mTempListeners;
-
-    private WallpaperColorInfo(Context context) {
-        mWallpaperManager = WallpaperManagerCompat.getInstance(context);
-        mWallpaperManager.addOnColorsChangedListener(this);
-        mExtractionType = new ColorExtractionAlgorithm();
-        update(mWallpaperManager.getWallpaperColors(FLAG_SYSTEM));
-    }
-
-    public int getMainColor() {
-        return mMainColor;
-    }
-
-    public int getSecondaryColor() {
-        return mSecondaryColor;
-    }
-
-    public boolean isDark() {
-        return mIsDark;
-    }
-
-    public boolean supportsDarkText() {
-        return mSupportsDarkText;
-    }
-
-    public boolean isMainColorDark() {
-        return mMainColor == MAIN_COLOR_DARK;
-    }
-
-    @Override
-    public void onColorsChanged(WallpaperColorsCompat colors, int which) {
-        if ((which & FLAG_SYSTEM) != 0) {
-            update(colors);
-            notifyChange();
-        }
-    }
-
-    private void update(WallpaperColorsCompat wallpaperColors) {
-        Pair<Integer, Integer> colors = mExtractionType.extractInto(wallpaperColors);
-        if (colors != null) {
-            mMainColor = colors.first;
-            mSecondaryColor = colors.second;
-        } else {
-            mMainColor = FALLBACK_COLOR;
-            mSecondaryColor = FALLBACK_COLOR;
-        }
-        mSupportsDarkText = wallpaperColors != null
-                ? (wallpaperColors.getColorHints()
-                & WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT) > 0 : false;
-        mIsDark = wallpaperColors != null
-                ? (wallpaperColors.getColorHints()
-                & WallpaperColorsCompat.HINT_SUPPORTS_DARK_THEME) > 0 : false;
-    }
-
-    public void addOnChangeListener(OnChangeListener listener) {
-        mListeners.add(listener);
-    }
-
-    public void removeOnChangeListener(OnChangeListener listener) {
-        mListeners.remove(listener);
-    }
-
-    private void notifyChange() {
-        OnChangeListener[] copy =
-                mTempListeners != null && mTempListeners.length == mListeners.size() ?
-                        mTempListeners : new OnChangeListener[mListeners.size()];
-
-        // Create a new array to avoid concurrent modification when the activity destroys itself.
-        mTempListeners = mListeners.toArray(copy);
-        for (OnChangeListener listener : mTempListeners) {
-            listener.onExtractedColorsChanged(this);
-        }
-    }
-
-    public interface OnChangeListener {
-        void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo);
-    }
-}
\ No newline at end of file
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java
deleted file mode 100644
index 5a1f9ca..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java
+++ /dev/null
@@ -1,795 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides.dynamicui;
-
-import android.graphics.Color;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Range;
-
-import com.android.launcher3.Utilities;
-
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.graphics.ColorUtils;
-
-/**
- * Implementation of tonal color extraction
- **/
-public class ColorExtractionAlgorithm {
-
-    private static final String TAG = "Tonal";
-
-    // Used for tonal palette fitting
-    private static final float FIT_WEIGHT_H = 1.0f;
-    private static final float FIT_WEIGHT_S = 1.0f;
-    private static final float FIT_WEIGHT_L = 10.0f;
-
-    public static final int MAIN_COLOR_LIGHT = 0xffb0b0b0;
-    public static final int SECONDARY_COLOR_LIGHT = 0xff9e9e9e;
-    public static final int MAIN_COLOR_DARK = 0xff212121;
-    public static final int SECONDARY_COLOR_DARK = 0xff000000;
-
-    // Temporary variable to avoid allocations
-    private float[] mTmpHSL = new float[3];
-
-    public Pair<Integer, Integer> extractInto(WallpaperColorsCompat inWallpaperColors) {
-        if (inWallpaperColors == null) {
-            return applyFallback(inWallpaperColors);
-        }
-
-        final List<Integer> mainColors = getMainColors(inWallpaperColors);
-        final int mainColorsSize = mainColors.size();
-        final boolean supportsDarkText = (inWallpaperColors.getColorHints() &
-                WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT) != 0;
-
-        if (mainColorsSize == 0) {
-            return applyFallback(inWallpaperColors);
-        }
-        // Tonal is not really a sort, it takes a color from the extracted
-        // palette and finds a best fit amongst a collection of pre-defined
-        // palettes. The best fit is tweaked to be closer to the source color
-        // and replaces the original palette
-
-        // Get the most preeminent, non-blacklisted color.
-        Integer bestColor = 0;
-        final float[] hsl = new float[3];
-        for (int i = 0; i < mainColorsSize; i++) {
-            final int colorValue = mainColors.get(i);
-            ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue),
-                    Color.blue(colorValue), hsl);
-
-            // Stop when we find a color that meets our criteria
-            if (!isBlacklisted(hsl)) {
-                bestColor = colorValue;
-                break;
-            }
-        }
-
-        // Fail if not found
-        if (bestColor == null) {
-            return applyFallback(inWallpaperColors);
-        }
-
-        int colorValue = bestColor;
-        ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue),
-                hsl);
-
-        // The Android HSL definition requires the hue to go from 0 to 360 but
-        // the Material Tonal Palette defines hues from 0 to 1.
-        hsl[0] /= 360f;
-
-        // Find the palette that contains the closest color
-        TonalPalette palette = findTonalPalette(hsl[0], hsl[1]);
-        if (palette == null) {
-            Log.w(TAG, "Could not find a tonal palette!");
-            return applyFallback(inWallpaperColors);
-        }
-
-        // Figure out what's the main color index in the optimal palette
-        int fitIndex = bestFit(palette, hsl[0], hsl[1], hsl[2]);
-        if (fitIndex == -1) {
-            Log.w(TAG, "Could not find best fit!");
-            return applyFallback(inWallpaperColors);
-        }
-
-        // Generate the 10 colors palette by offsetting each one of them
-        float[] h = fit(palette.h, hsl[0], fitIndex,
-                Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
-        float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
-        float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
-
-        int primaryIndex = fitIndex;
-        int mainColor = getColorInt(primaryIndex, h, s, l);
-
-        // We might want use the fallback in case the extracted color is brighter than our
-        // light fallback or darker than our dark fallback.
-        ColorUtils.colorToHSL(mainColor, mTmpHSL);
-        final float mainLuminosity = mTmpHSL[2];
-        ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL);
-        final float lightLuminosity = mTmpHSL[2];
-        if (mainLuminosity > lightLuminosity) {
-            return applyFallback(inWallpaperColors);
-        }
-        ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL);
-        final float darkLuminosity = mTmpHSL[2];
-        if (mainLuminosity < darkLuminosity) {
-            return applyFallback(inWallpaperColors);
-        }
-
-        // Dark colors:
-        // Stops at 4th color, only lighter if dark text is supported
-        if (supportsDarkText) {
-            primaryIndex = h.length - 1;
-        } else if (fitIndex < 2) {
-            primaryIndex = 0;
-        } else {
-            primaryIndex = Math.min(fitIndex, 3);
-        }
-        int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
-        int secondaryColor = getColorInt(secondaryIndex, h, s, l);
-
-        return new Pair<>(mainColor, secondaryColor);
-    }
-
-    public static Pair<Integer, Integer> applyFallback(WallpaperColorsCompat inWallpaperColors) {
-        boolean light = inWallpaperColors != null
-                && (inWallpaperColors.getColorHints()
-                    & WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT)!= 0;
-        int innerColor = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK;
-        int outerColor = light ? SECONDARY_COLOR_LIGHT : SECONDARY_COLOR_DARK;
-        return new Pair<>(innerColor, outerColor);
-    }
-
-    private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) {
-        mTmpHSL[0] = fract(h[fitIndex]) * 360.0f;
-        mTmpHSL[1] = s[fitIndex];
-        mTmpHSL[2] = l[fitIndex];
-        return ColorUtils.HSLToColor(mTmpHSL);
-    }
-
-    /**
-     * Checks if a given color exists in the blacklist
-     * @param hsl float array with 3 components (H 0..360, S 0..1 and L 0..1)
-     * @return true if color should be avoided
-     */
-    private boolean isBlacklisted(float[] hsl) {
-        for (ColorRange badRange: BLACKLISTED_COLORS) {
-            if (badRange.containsColor(hsl[0], hsl[1], hsl[2])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Offsets all colors by a delta, clamping values that go beyond what's
-     * supported on the color space.
-     * @param data what you want to fit
-     * @param v how big should be the offset
-     * @param index which index to calculate the delta against
-     * @param min minimum accepted value (clamp)
-     * @param max maximum accepted value (clamp)
-     * @return new shifted palette
-     */
-    private static float[] fit(float[] data, float v, int index, float min, float max) {
-        float[] fitData = new float[data.length];
-        float delta = v - data[index];
-
-        for (int i = 0; i < data.length; i++) {
-            fitData[i] = Utilities.boundToRange(data[i] + delta, min, max);
-        }
-
-        return fitData;
-    }
-
-    /**
-     * Finds the closest color in a palette, given another HSL color
-     *
-     * @param palette where to search
-     * @param h hue
-     * @param s saturation
-     * @param l lightness
-     * @return closest index or -1 if palette is empty.
-     */
-    private static int bestFit(@NonNull TonalPalette palette, float h, float s, float l) {
-        int minErrorIndex = -1;
-        float minError = Float.POSITIVE_INFINITY;
-
-        for (int i = 0; i < palette.h.length; i++) {
-            float error =
-                    FIT_WEIGHT_H * Math.abs(h - palette.h[i])
-                            + FIT_WEIGHT_S * Math.abs(s - palette.s[i])
-                            + FIT_WEIGHT_L * Math.abs(l - palette.l[i]);
-            if (error < minError) {
-                minError = error;
-                minErrorIndex = i;
-            }
-        }
-
-        return minErrorIndex;
-    }
-
-    @Nullable
-    private static TonalPalette findTonalPalette(float h, float s) {
-        // Fallback to a grey palette if the color is too desaturated.
-        // This avoids hue shifts.
-        if (s < 0.05f) {
-            return GREY_PALETTE;
-        }
-
-        TonalPalette best = null;
-        float error = Float.POSITIVE_INFINITY;
-
-        for (int i = 0; i < TONAL_PALETTES.length; i++) {
-            final TonalPalette candidate = TONAL_PALETTES[i];
-
-            if (h >= candidate.minHue && h <= candidate.maxHue) {
-                best = candidate;
-                break;
-            }
-
-            if (candidate.maxHue > 1.0f && h >= 0.0f && h <= fract(candidate.maxHue)) {
-                best = candidate;
-                break;
-            }
-
-            if (candidate.minHue < 0.0f && h >= fract(candidate.minHue) && h <= 1.0f) {
-                best = candidate;
-                break;
-            }
-
-            if (h <= candidate.minHue && candidate.minHue - h < error) {
-                best = candidate;
-                error = candidate.minHue - h;
-            } else if (h >= candidate.maxHue && h - candidate.maxHue < error) {
-                best = candidate;
-                error = h - candidate.maxHue;
-            } else if (candidate.maxHue > 1.0f && h >= fract(candidate.maxHue)
-                    && h - fract(candidate.maxHue) < error) {
-                best = candidate;
-                error = h - fract(candidate.maxHue);
-            } else if (candidate.minHue < 0.0f && h <= fract(candidate.minHue)
-                    && fract(candidate.minHue) - h < error) {
-                best = candidate;
-                error = fract(candidate.minHue) - h;
-            }
-        }
-
-        return best;
-    }
-
-    private static float fract(float v) {
-        return v - (float) Math.floor(v);
-    }
-
-    static class TonalPalette {
-        final float[] h;
-        final float[] s;
-        final float[] l;
-        final float minHue;
-        final float maxHue;
-
-        TonalPalette(float[] h, float[] s, float[] l) {
-            if (h.length != s.length || s.length != l.length) {
-                throw new IllegalArgumentException("All arrays should have the same size. h: "
-                        + Arrays.toString(h) + " s: " + Arrays.toString(s) + " l: "
-                        + Arrays.toString(l));
-            }
-
-            this.h = h;
-            this.s = s;
-            this.l = l;
-
-            float minHue = Float.POSITIVE_INFINITY;
-            float maxHue = Float.NEGATIVE_INFINITY;
-
-            for (float v : h) {
-                minHue = Math.min(v, minHue);
-                maxHue = Math.max(v, maxHue);
-            }
-
-            this.minHue = minHue;
-            this.maxHue = maxHue;
-        }
-    }
-
-    // Data definition of Material Design tonal palettes
-    // When the sort type is set to TONAL, these palettes are used to find
-    // a best fit. Each palette is defined as 22 HSL colors
-    private static final TonalPalette[] TONAL_PALETTES = {
-            new TonalPalette(
-                    new float[] {1f, 1f, 0.991f, 0.991f, 0.9833333333333333f, 0f, 0f, 0f,
-                            0.01134380453752181f, 0.015625000000000003f, 0.024193548387096798f,
-                            0.027397260273972573f, 0.017543859649122865f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8434782608695652f, 1f, 1f, 1f, 1f,
-                            1f},
-                    new float[] {0.04f, 0.09f, 0.14f, 0.2f, 0.27450980392156865f,
-                            0.34901960784313724f, 0.4235294117647059f, 0.5490196078431373f,
-                            0.6254901960784314f, 0.6862745098039216f, 0.7568627450980392f,
-                            0.8568627450980393f, 0.9254901960784314f}
-            ),
-            new TonalPalette(
-                    new float[] {0.638f, 0.638f, 0.6385767790262171f, 0.6301169590643275f,
-                            0.6223958333333334f, 0.6151079136690647f, 0.6065400843881856f,
-                            0.5986964618249534f, 0.5910746812386157f, 0.5833333333333334f,
-                            0.5748031496062993f, 0.5582010582010583f},
-                    new float[] {1f, 1f, 1f, 1f, 0.9014084507042253f, 0.8128654970760234f,
-                            0.7979797979797981f, 0.7816593886462883f, 0.778723404255319f, 1f, 1f,
-                            1f},
-                    new float[] {0.05f, 0.12f, 0.17450980392156862f, 0.2235294117647059f,
-                            0.2784313725490196f, 0.3352941176470588f, 0.388235294117647f,
-                            0.44901960784313727f, 0.5392156862745098f, 0.6509803921568628f,
-                            0.7509803921568627f, 0.8764705882352941f}
-            ),
-            new TonalPalette(
-                    new float[] {0.563f, 0.569f, 0.5666f, 0.5669934640522876f, 0.5748031496062993f,
-                            0.5595238095238095f, 0.5473118279569893f, 0.5393258426966292f,
-                            0.5315955766192734f, 0.524031007751938f, 0.5154711673699016f,
-                            0.508080808080808f, 0.5f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8847736625514403f, 1f, 1f,
-                            1f},
-                    new float[] {0.07f, 0.12f, 0.16f, 0.2f, 0.24901960784313726f,
-                            0.27450980392156865f, 0.30392156862745096f, 0.34901960784313724f,
-                            0.4137254901960784f, 0.47647058823529415f, 0.5352941176470588f,
-                            0.6764705882352942f, 0.8f}
-            ),
-            new TonalPalette(
-                    new float[] {0.508f, 0.511f, 0.508f, 0.508f, 0.5082304526748972f,
-                            0.5069444444444444f, 0.5f, 0.5f, 0.5f, 0.48724954462659376f,
-                            0.4800347222222222f, 0.4755134281200632f, 0.4724409448818897f,
-                            0.4671052631578947f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8888888888888887f, 0.9242424242424242f, 1f,
-                            1f, 0.8133333333333332f, 0.7868852459016393f, 1f, 1f, 1f},
-                    new float[] {0.04f, 0.06f, 0.08f, 0.12f, 0.1588235294117647f,
-                            0.21176470588235297f, 0.25882352941176473f, 0.3f, 0.34901960784313724f,
-                            0.44117647058823534f, 0.5215686274509804f, 0.5862745098039216f,
-                            0.7509803921568627f, 0.8509803921568627f}
-            ),
-            new TonalPalette(
-                    new float[] {0.333f, 0.333f, 0.333f, 0.3333333333333333f, 0.3333333333333333f,
-                            0.34006734006734f, 0.34006734006734f, 0.34006734006734f,
-                            0.34259259259259256f, 0.3475783475783476f, 0.34767025089605735f,
-                            0.3467741935483871f, 0.3703703703703704f},
-                    new float[] {0.70f, 0.72f, 0.69f, 0.6703296703296703f, 0.728813559322034f,
-                            0.5657142857142856f, 0.5076923076923077f, 0.3944223107569721f,
-                            0.6206896551724138f, 0.8931297709923666f, 1f, 1f, 1f},
-                    new float[] {0.05f, 0.08f, 0.14f, 0.1784313725490196f, 0.23137254901960785f,
-                            0.3431372549019608f, 0.38235294117647056f, 0.49215686274509807f,
-                            0.6588235294117647f, 0.7431372549019608f, 0.8176470588235294f,
-                            0.8784313725490196f, 0.9294117647058824f}
-            ),
-            new TonalPalette(
-                    new float[] {0.161f, 0.163f, 0.163f, 0.162280701754386f, 0.15032679738562088f,
-                            0.15879265091863518f, 0.16236559139784948f, 0.17443868739205526f,
-                            0.17824074074074076f, 0.18674698795180725f, 0.18692449355432778f,
-                            0.1946778711484594f, 0.18604651162790695f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.05f, 0.08f, 0.11f, 0.14901960784313725f, 0.2f,
-                            0.24901960784313726f, 0.30392156862745096f, 0.3784313725490196f,
-                            0.4235294117647059f, 0.48823529411764705f, 0.6450980392156863f,
-                            0.7666666666666666f, 0.8313725490196078f}
-            ),
-            new TonalPalette(
-                    new float[] {0.108f, 0.105f, 0.105f, 0.105f, 0.10619469026548674f,
-                            0.11924686192468618f, 0.13046448087431692f, 0.14248366013071895f,
-                            0.1506024096385542f, 0.16220238095238093f, 0.16666666666666666f,
-                            0.16666666666666666f, 0.162280701754386f, 0.15686274509803924f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.17f, 0.22f, 0.28f, 0.35f, 0.44313725490196076f,
-                            0.46862745098039216f, 0.47843137254901963f, 0.5f, 0.5117647058823529f,
-                            0.5607843137254902f, 0.6509803921568628f, 0.7509803921568627f,
-                            0.8509803921568627f, 0.9f}
-            ),
-            new TonalPalette(
-                    new float[] {0.036f, 0.036f, 0.036f, 0.036f, 0.03561253561253561f,
-                            0.05098039215686275f, 0.07516339869281045f, 0.09477124183006536f,
-                            0.1150326797385621f, 0.134640522875817f, 0.14640522875816991f,
-                            0.1582397003745319f, 0.15773809523809523f, 0.15359477124183002f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.19f, 0.26f, 0.34f, 0.39f, 0.4588235294117647f, 0.5f, 0.5f, 0.5f,
-                            0.5f, 0.5f, 0.5f, 0.6509803921568628f, 0.7803921568627451f, 0.9f}
-            ),
-            new TonalPalette(
-                    new float[] {0.955f, 0.961f, 0.958f, 0.9596491228070175f, 0.9593837535014005f,
-                            0.9514767932489452f, 0.943859649122807f, 0.9396825396825397f,
-                            0.9395424836601307f, 0.9393939393939394f, 0.9362745098039216f,
-                            0.9754098360655739f, 0.9824561403508771f},
-                    new float[] {0.87f, 0.85f, 0.85f, 0.84070796460177f, 0.8206896551724138f,
-                            0.7979797979797981f, 0.7661290322580644f, 0.9051724137931036f,
-                            1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.06f, 0.11f, 0.16f, 0.22156862745098038f, 0.2843137254901961f,
-                            0.388235294117647f, 0.48627450980392156f, 0.5450980392156863f,
-                            0.6f, 0.6764705882352942f, 0.8f, 0.8803921568627451f,
-                            0.9254901960784314f}
-            ),
-            new TonalPalette(
-                    new float[] {0.866f, 0.855f, 0.841025641025641f, 0.8333333333333334f,
-                            0.8285256410256411f, 0.821522309711286f, 0.8083333333333333f,
-                            0.8046594982078853f, 0.8005822416302766f, 0.7842377260981912f,
-                            0.7771084337349398f, 0.7747747747747749f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f,
-                            0.737142857142857f, 0.6434108527131781f, 0.46835443037974644f},
-                    new float[] {0.05f, 0.08f, 0.12745098039215685f, 0.15490196078431373f,
-                            0.20392156862745098f, 0.24901960784313726f, 0.3137254901960784f,
-                            0.36470588235294116f, 0.44901960784313727f, 0.6568627450980392f,
-                            0.7470588235294118f, 0.8450980392156863f}
-            ),
-            new TonalPalette(
-                    new float[] {0.925f, 0.93f, 0.938f, 0.947f, 0.955952380952381f,
-                            0.9681069958847737f, 0.9760479041916167f, 0.9873563218390804f, 0f, 0f,
-                            0.009057971014492771f, 0.026748971193415648f,
-                            0.041666666666666616f, 0.05303030303030304f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8350515463917526f, 0.6929460580912863f,
-                            0.6387665198237885f, 0.6914893617021276f, 0.7583892617449666f,
-                            0.8070175438596495f, 0.9310344827586209f, 1f, 1f},
-                    new float[] {0.10f, 0.13f, 0.17f, 0.2f, 0.27450980392156865f,
-                            0.3803921568627451f, 0.4725490196078432f, 0.5549019607843138f,
-                            0.6313725490196078f, 0.707843137254902f, 0.7764705882352941f,
-                            0.8294117647058823f, 0.9058823529411765f, 0.9568627450980391f}
-            ),
-            new TonalPalette(
-                    new float[] {0.733f, 0.736f, 0.744f, 0.7514619883040936f, 0.7679738562091503f,
-                            0.7802083333333333f, 0.7844311377245509f, 0.796875f,
-                            0.8165618448637316f, 0.8487179487179487f, 0.8582375478927203f,
-                            0.8562091503267975f, 0.8666666666666667f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8163265306122449f, 0.6653386454183268f,
-                            0.7547169811320753f, 0.929824561403509f, 0.9558823529411766f,
-                            0.9560439560439562f, 1f, 1f},
-                    new float[] {0.07f, 0.12f, 0.17f, 0.2235294117647059f, 0.3f,
-                            0.38431372549019605f, 0.492156862745098f, 0.5843137254901961f,
-                            0.6647058823529411f, 0.7333333333333334f, 0.8215686274509804f, 0.9f,
-                            0.9411764705882353f}
-            ),
-            new TonalPalette(
-                    new float[] {0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
-                            0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
-                            0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
-                            0.6666666666666666f, 0.6666666666666666f},
-                    new float[] {0.25f, 0.24590163934426232f, 0.17880794701986752f,
-                            0.14606741573033713f, 0.13761467889908252f, 0.14893617021276592f,
-                            0.16756756756756758f, 0.20312500000000017f, 0.26086956521739135f,
-                            0.29999999999999966f, 0.5000000000000004f},
-                    new float[] {0.18f, 0.2392156862745098f, 0.296078431372549f,
-                            0.34901960784313724f, 0.4274509803921569f, 0.5392156862745098f,
-                            0.6372549019607843f, 0.7490196078431373f, 0.8196078431372549f,
-                            0.8823529411764706f, 0.9372549019607843f}
-            ),
-            new TonalPalette(
-                    new float[] {0.938f, 0.944f, 0.952f, 0.961f, 0.9678571428571429f,
-                            0.9944812362030905f, 0f, 0f,
-                            0.0047348484848484815f, 0.00316455696202532f, 0f,
-                            0.9980392156862745f, 0.9814814814814816f, 0.9722222222222221f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 0.7023255813953488f, 0.6638655462184874f,
-                            0.6521739130434782f, 0.7719298245614035f, 0.8315789473684211f,
-                            0.6867469879518071f, 0.7264957264957265f, 0.8181818181818182f,
-                            0.8181818181818189f},
-                    new float[] {0.08f, 0.13f, 0.18f, 0.23f, 0.27450980392156865f,
-                            0.4215686274509804f,
-                            0.4666666666666667f, 0.503921568627451f, 0.5529411764705883f,
-                            0.6274509803921569f, 0.6745098039215687f, 0.7705882352941176f,
-                            0.892156862745098f, 0.9568627450980391f}
-            ),
-            new TonalPalette(
-                    new float[] {0.88f, 0.888f, 0.897f, 0.9052287581699346f, 0.9112021857923498f,
-                            0.9270152505446624f, 0.9343137254901961f, 0.9391534391534391f,
-                            0.9437984496124031f, 0.943661971830986f, 0.9438943894389439f,
-                            0.9426229508196722f, 0.9444444444444444f},
-                    new float[] {1f, 1f, 1f, 1f, 0.8133333333333332f, 0.7927461139896375f,
-                            0.7798165137614679f, 0.7777777777777779f, 0.8190476190476191f,
-                            0.8255813953488372f, 0.8211382113821142f, 0.8133333333333336f,
-                            0.8000000000000006f},
-                    new float[] {0.08f, 0.12f, 0.16f, 0.2f, 0.29411764705882354f,
-                            0.3784313725490196f, 0.42745098039215684f, 0.4764705882352941f,
-                            0.5882352941176471f, 0.6627450980392157f, 0.7588235294117647f,
-                            0.8529411764705882f, 0.9411764705882353f}
-            ),
-            new TonalPalette(
-                    new float[] {0.669f, 0.680f, 0.6884057971014492f, 0.6974789915966387f,
-                            0.7079889807162534f, 0.7154471544715447f, 0.7217741935483872f,
-                            0.7274143302180687f, 0.7272727272727273f, 0.7258064516129031f,
-                            0.7252252252252251f, 0.7333333333333333f},
-                    new float[] {0.81f, 0.81f, 0.8214285714285715f, 0.6878612716763006f,
-                            0.6080402010050251f, 0.5774647887323943f, 0.5391304347826086f,
-                            0.46724890829694316f, 0.4680851063829788f, 0.462686567164179f,
-                            0.45679012345678977f, 0.4545454545454551f},
-                    new float[] {0.12f, 0.16f, 0.2196078431372549f, 0.33921568627450976f,
-                            0.39019607843137255f, 0.4176470588235294f, 0.45098039215686275f,
-                            0.5509803921568628f, 0.6313725490196078f, 0.7372549019607844f,
-                            0.8411764705882353f, 0.9352941176470588f}
-            ),
-            new TonalPalette(
-                    new float[] {0.6470588235294118f, 0.6516666666666667f, 0.6464174454828661f,
-                            0.6441441441441442f, 0.6432748538011696f, 0.6416666666666667f,
-                            0.6402439024390243f, 0.6412429378531074f, 0.6435185185185186f,
-                            0.6428571428571429f},
-                    new float[] {0.8095238095238095f, 0.6578947368421053f, 0.5721925133689839f,
-                            0.5362318840579711f, 0.5f, 0.4424778761061947f, 0.44086021505376327f,
-                            0.44360902255639095f, 0.4499999999999997f, 0.4375000000000006f},
-                    new float[] {0.16470588235294117f, 0.2980392156862745f, 0.36666666666666664f,
-                            0.40588235294117647f, 0.44705882352941173f,
-                            0.5568627450980392f, 0.6352941176470588f, 0.7392156862745098f,
-                            0.8431372549019608f, 0.9372549019607843f}
-            ),
-            new TonalPalette(
-                    new float[] {0.469f, 0.46732026143790845f, 0.4718614718614719f,
-                            0.4793650793650794f, 0.48071625344352614f, 0.4829683698296837f,
-                            0.484375f, 0.4841269841269842f, 0.48444444444444457f,
-                            0.48518518518518516f, 0.4907407407407408f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 0.6274509803921569f, 0.41832669322709176f,
-                            0.41899441340782106f, 0.4128440366972478f, 0.4090909090909088f},
-                    new float[] {0.07f, 0.1f, 0.15098039215686274f, 0.20588235294117646f,
-                            0.2372549019607843f, 0.26862745098039215f, 0.4f, 0.5078431372549019f,
-                            0.6490196078431372f, 0.7862745098039216f, 0.9137254901960784f}
-            ),
-            new TonalPalette(
-                    new float[] {0.542f, 0.5444444444444444f, 0.5555555555555556f,
-                            0.5555555555555556f, 0.553763440860215f, 0.5526315789473684f,
-                            0.5555555555555556f, 0.5555555555555555f, 0.5555555555555556f,
-                            0.5512820512820514f, 0.5666666666666667f},
-                    new float[] {0.25f, 0.24590163934426232f, 0.19148936170212766f,
-                            0.1791044776119403f, 0.18343195266272191f, 0.18446601941747576f,
-                            0.1538461538461539f, 0.15625000000000003f, 0.15328467153284678f,
-                            0.15662650602409653f, 0.151515151515151f},
-                    new float[] {0.05f, 0.1196078431372549f, 0.1843137254901961f,
-                            0.2627450980392157f,
-                            0.33137254901960783f, 0.403921568627451f, 0.5411764705882354f,
-                            0.6235294117647059f, 0.7313725490196079f, 0.8372549019607843f,
-                            0.9352941176470588f}
-            ),
-            new TonalPalette(
-                    new float[] {0.022222222222222223f, 0.02469135802469136f, 0.031249999999999997f,
-                            0.03947368421052631f, 0.04166666666666668f,
-                            0.043650793650793655f, 0.04411764705882352f, 0.04166666666666652f,
-                            0.04444444444444459f, 0.05555555555555529f},
-                    new float[] {0.33333333333333337f, 0.2783505154639175f, 0.2580645161290323f,
-                            0.25675675675675674f, 0.2528735632183908f, 0.17500000000000002f,
-                            0.15315315315315312f, 0.15189873417721522f,
-                            0.15789473684210534f, 0.15789473684210542f},
-                    new float[] {0.08823529411764705f, 0.19019607843137254f, 0.2431372549019608f,
-                            0.2901960784313725f, 0.3411764705882353f, 0.47058823529411764f,
-                            0.5647058823529412f, 0.6901960784313725f, 0.8137254901960784f,
-                            0.9254901960784314f}
-            ),
-            new TonalPalette(
-                    new float[] {0.027f, 0.03f, 0.038f, 0.044f, 0.050884955752212385f,
-                            0.07254901960784313f, 0.0934640522875817f,
-                            0.10457516339869281f, 0.11699346405228758f,
-                            0.1255813953488372f, 0.1268939393939394f, 0.12533333333333332f,
-                            0.12500000000000003f, 0.12777777777777777f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.25f, 0.3f, 0.35f, 0.4f, 0.44313725490196076f, 0.5f, 0.5f, 0.5f,
-                            0.5f, 0.5784313725490196f,
-                            0.6549019607843137f, 0.7549019607843137f, 0.8509803921568627f,
-                            0.9411764705882353f}
-            )
-    };
-
-    private static final TonalPalette GREY_PALETTE = new TonalPalette(
-            new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
-            new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
-            new float[]{0.08f, 0.11f, 0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
-                    0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
-                    0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f}
-    );
-
-    @SuppressWarnings("WeakerAccess")
-    static final ColorRange[] BLACKLISTED_COLORS = new ColorRange[] {
-
-            // Red
-            new ColorRange(
-                    new Range<>(0f, 20f) /* H */,
-                    new Range<>(0.7f, 1f) /* S */,
-                    new Range<>(0.21f, 0.79f)) /* L */,
-            new ColorRange(
-                    new Range<>(0f, 20f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.355f, 0.653f)),
-
-            // Red Orange
-            new ColorRange(
-                    new Range<>(20f, 40f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.28f, 0.643f)),
-            new ColorRange(
-                    new Range<>(20f, 40f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.414f, 0.561f)),
-            new ColorRange(
-                    new Range<>(20f, 40f),
-                    new Range<>(0f, 3f),
-                    new Range<>(0.343f, 0.584f)),
-
-            // Orange
-            new ColorRange(
-                    new Range<>(40f, 60f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.173f, 0.349f)),
-            new ColorRange(
-                    new Range<>(40f, 60f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.233f, 0.427f)),
-            new ColorRange(
-                    new Range<>(40f, 60f),
-                    new Range<>(0f, 0.3f),
-                    new Range<>(0.231f, 0.484f)),
-
-            // Yellow 60
-            new ColorRange(
-                    new Range<>(60f, 80f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.488f, 0.737f)),
-            new ColorRange(
-                    new Range<>(60f, 80f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.673f, 0.837f)),
-
-            // Yellow Green 80
-            new ColorRange(
-                    new Range<>(80f, 100f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.469f, 0.61f)),
-
-            // Yellow green 100
-            new ColorRange(
-                    new Range<>(100f, 120f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.388f, 0.612f)),
-            new ColorRange(
-                    new Range<>(100f, 120f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.424f, 0.541f)),
-
-            // Green
-            new ColorRange(
-                    new Range<>(120f, 140f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.375f, 0.52f)),
-            new ColorRange(
-                    new Range<>(120f, 140f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.435f, 0.524f)),
-
-            // Green Blue 140
-            new ColorRange(
-                    new Range<>(140f, 160f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.496f, 0.641f)),
-
-            // Seafoam
-            new ColorRange(
-                    new Range<>(160f, 180f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.496f, 0.567f)),
-
-            // Cyan
-            new ColorRange(
-                    new Range<>(180f, 200f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.52f, 0.729f)),
-
-            // Blue
-            new ColorRange(
-                    new Range<>(220f, 240f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.396f, 0.571f)),
-            new ColorRange(
-                    new Range<>(220f, 240f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.425f, 0.551f)),
-
-            // Blue Purple 240
-            new ColorRange(
-                    new Range<>(240f, 260f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.418f, 0.639f)),
-            new ColorRange(
-                    new Range<>(220f, 240f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.441f, 0.576f)),
-
-            // Blue Purple 260
-            new ColorRange(
-                    new Range<>(260f, 280f),
-                    new Range<>(0.3f, 1f), // Bigger range
-                    new Range<>(0.461f, 0.553f)),
-
-            // Fuchsia
-            new ColorRange(
-                    new Range<>(300f, 320f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.484f, 0.588f)),
-            new ColorRange(
-                    new Range<>(300f, 320f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.48f, 0.592f)),
-
-            // Pink
-            new ColorRange(
-                    new Range<>(320f, 340f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.466f, 0.629f)),
-
-            // Soft red
-            new ColorRange(
-                    new Range<>(340f, 360f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.437f, 0.596f))
-    };
-
-    /**
-     * Representation of an HSL color range.
-     * <ul>
-     * <li>hsl[0] is Hue [0 .. 360)</li>
-     * <li>hsl[1] is Saturation [0...1]</li>
-     * <li>hsl[2] is Lightness [0...1]</li>
-     * </ul>
-     */
-    static class ColorRange {
-        private Range<Float> mHue;
-        private Range<Float> mSaturation;
-        private Range<Float> mLightness;
-
-        ColorRange(Range<Float> hue, Range<Float> saturation, Range<Float> lightness) {
-            mHue = hue;
-            mSaturation = saturation;
-            mLightness = lightness;
-        }
-
-        boolean containsColor(float h, float s, float l) {
-            if (!mHue.contains(h)) {
-                return false;
-            } else if (!mSaturation.contains(s)) {
-                return false;
-            } else if (!mLightness.contains(l)) {
-                return false;
-            }
-            return true;
-        }
-
-        float[] getCenter() {
-            return new float[] {
-                    mHue.getLower() + (mHue.getUpper() - mHue.getLower()) / 2f,
-                    mSaturation.getLower() + (mSaturation.getUpper() - mSaturation.getLower()) / 2f,
-                    mLightness.getLower() + (mLightness.getUpper() - mLightness.getLower()) / 2f
-            };
-        }
-
-        @Override
-        public String toString() {
-            return String.format("H: %s, S: %s, L %s", mHue, mSaturation, mLightness);
-        }
-    }
-
-    private static List<Integer> getMainColors(WallpaperColorsCompat wallpaperColors) {
-        LinkedList<Integer> colors = new LinkedList<>();
-        if (wallpaperColors.getPrimaryColor() != 0) {
-            colors.add(wallpaperColors.getPrimaryColor());
-        }
-        if (wallpaperColors.getSecondaryColor() != 0) {
-            colors.add(wallpaperColors.getSecondaryColor());
-        }
-        if (wallpaperColors.getTertiaryColor() != 0) {
-            colors.add(wallpaperColors.getTertiaryColor());
-        }
-        return colors;
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperColorsCompat.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperColorsCompat.java
deleted file mode 100644
index d984a84..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperColorsCompat.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides.dynamicui;
-
-/**
- * A compatibility layer around platform implementation of WallpaperColors
- */
-public class WallpaperColorsCompat {
-
-    public static final int HINT_SUPPORTS_DARK_TEXT = 0x1;
-    public static final int HINT_SUPPORTS_DARK_THEME = 0x2;
-
-    private final int mPrimaryColor;
-    private final int mSecondaryColor;
-    private final int mTertiaryColor;
-    private final int mColorHints;
-
-    public WallpaperColorsCompat(int primaryColor, int secondaryColor, int tertiaryColor,
-            int colorHints) {
-        mPrimaryColor = primaryColor;
-        mSecondaryColor = secondaryColor;
-        mTertiaryColor = tertiaryColor;
-        mColorHints = colorHints;
-    }
-
-    public int getPrimaryColor() {
-        return mPrimaryColor;
-    }
-
-    public int getSecondaryColor() {
-        return mSecondaryColor;
-    }
-
-    public int getTertiaryColor() {
-        return mTertiaryColor;
-    }
-
-    public int getColorHints() {
-        return mColorHints;
-    }
-
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java
deleted file mode 100644
index 0fd0a35..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides.dynamicui;
-
-import android.content.Context;
-
-import com.android.launcher3.Utilities;
-
-import androidx.annotation.Nullable;
-
-public abstract class WallpaperManagerCompat {
-
-    private static final Object sInstanceLock = new Object();
-    private static WallpaperManagerCompat sInstance;
-
-    public static WallpaperManagerCompat getInstance(Context context) {
-        synchronized (sInstanceLock) {
-            if (sInstance == null) {
-                context = context.getApplicationContext();
-
-                if (Utilities.ATLEAST_OREO_MR1) {
-                    try {
-                        sInstance = new WallpaperManagerCompatVOMR1(context);
-                    } catch (Throwable e) {
-                        // The wallpaper APIs do not yet exist
-                    }
-                }
-                if (sInstance == null) {
-                    sInstance = new WallpaperManagerCompatVL(context);
-                }
-            }
-            return sInstance;
-        }
-    }
-
-
-    public abstract @Nullable WallpaperColorsCompat getWallpaperColors(int which);
-
-    public abstract void addOnColorsChangedListener(OnColorsChangedListenerCompat listener);
-
-    /**
-     * Interface definition for a callback to be invoked when colors change on a wallpaper.
-     */
-    public interface OnColorsChangedListenerCompat {
-
-        void onColorsChanged(WallpaperColorsCompat colors, int which);
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
deleted file mode 100644
index 500fdc3..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides.dynamicui;
-
-import static android.app.WallpaperManager.FLAG_SYSTEM;
-
-import static com.android.launcher3.Utilities.getDevicePrefs;
-
-import android.app.WallpaperInfo;
-import android.app.WallpaperManager;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.launcher3.icons.ColorExtractor;
-
-import java.io.IOException;
-import java.util.ArrayList;
-
-import androidx.annotation.Nullable;
-
-public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
-
-    private static final String TAG = "WMCompatVL";
-
-    private static final String VERSION_PREFIX = "1,";
-    private static final String KEY_COLORS = "wallpaper_parsed_colors";
-    private static final String ACTION_EXTRACTION_COMPLETE =
-            "com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL.EXTRACTION_COMPLETE";
-
-    public static final int WALLPAPER_COMPAT_JOB_ID = 1;
-
-    private final ArrayList<OnColorsChangedListenerCompat> mListeners = new ArrayList<>();
-
-    private final Context mContext;
-    private WallpaperColorsCompat mColorsCompat;
-
-    WallpaperManagerCompatVL(Context context) {
-        mContext = context;
-
-        String colors = getDevicePrefs(mContext).getString(KEY_COLORS, "");
-        int wallpaperId = -1;
-        if (colors.startsWith(VERSION_PREFIX)) {
-            Pair<Integer, WallpaperColorsCompat> storedValue = parseValue(colors);
-            wallpaperId = storedValue.first;
-            mColorsCompat = storedValue.second;
-        }
-
-        if (wallpaperId == -1 || wallpaperId != getWallpaperId(context)) {
-            reloadColors();
-        }
-        context.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                reloadColors();
-            }
-        }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
-
-        // Register a receiver for results
-        String permission = null;
-        // Find a permission which only we can use.
-        try {
-            for (PermissionInfo info : context.getPackageManager().getPackageInfo(
-                    context.getPackageName(),
-                    PackageManager.GET_PERMISSIONS).permissions) {
-                if ((info.protectionLevel & PermissionInfo.PROTECTION_SIGNATURE) != 0) {
-                    permission = info.name;
-                }
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // Something went wrong. ignore
-            Log.d(TAG, "Unable to get permission info", e);
-        }
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                handleResult(intent.getStringExtra(KEY_COLORS));
-            }
-        }, new IntentFilter(ACTION_EXTRACTION_COMPLETE), permission, new Handler());
-    }
-
-    @Nullable
-    @Override
-    public WallpaperColorsCompat getWallpaperColors(int which) {
-        return which == FLAG_SYSTEM ? mColorsCompat : null;
-    }
-
-    @Override
-    public void addOnColorsChangedListener(OnColorsChangedListenerCompat listener) {
-        mListeners.add(listener);
-    }
-
-    private void reloadColors() {
-        JobInfo job = new JobInfo.Builder(WALLPAPER_COMPAT_JOB_ID,
-                new ComponentName(mContext, ColorExtractionService.class))
-                .setMinimumLatency(0).build();
-        ((JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(job);
-    }
-
-    private void handleResult(String result) {
-        getDevicePrefs(mContext).edit().putString(KEY_COLORS, result).apply();
-        mColorsCompat = parseValue(result).second;
-        for (OnColorsChangedListenerCompat listener : mListeners) {
-            listener.onColorsChanged(mColorsCompat, FLAG_SYSTEM);
-        }
-    }
-
-    private static final int getWallpaperId(Context context) {
-        return context.getSystemService(WallpaperManager.class).getWallpaperId(FLAG_SYSTEM);
-    }
-
-    /**
-     * Parses the stored value and returns the wallpaper id and wallpaper colors.
-     */
-    private static Pair<Integer, WallpaperColorsCompat> parseValue(String value) {
-        String[] parts = value.split(",");
-        Integer wallpaperId = Integer.parseInt(parts[1]);
-        if (parts.length == 2) {
-            // There is no wallpaper color info present, eg when live wallpaper has no preview.
-            return Pair.create(wallpaperId, null);
-        }
-
-        int primary = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
-        int secondary = parts.length > 3 ? Integer.parseInt(parts[3]) : 0;
-        int tertiary = parts.length > 4 ? Integer.parseInt(parts[4]) : 0;
-
-        return Pair.create(wallpaperId, new WallpaperColorsCompat(primary, secondary, tertiary,
-                0 /* hints */));
-    }
-
-    /**
-     * Intent service to handle color extraction
-     */
-    public static class ColorExtractionService extends JobService implements Runnable {
-        private static final int MAX_WALLPAPER_EXTRACTION_AREA = 112 * 112;
-
-        private HandlerThread mWorkerThread;
-        private Handler mWorkerHandler;
-        private ColorExtractor mColorExtractor;
-
-        @Override
-        public void onCreate() {
-            super.onCreate();
-            mWorkerThread = new HandlerThread("ColorExtractionService");
-            mWorkerThread.start();
-            mWorkerHandler = new Handler(mWorkerThread.getLooper());
-            mColorExtractor = new ColorExtractor();
-        }
-
-        @Override
-        public void onDestroy() {
-            super.onDestroy();
-            mWorkerThread.quit();
-        }
-
-        @Override
-        public boolean onStartJob(final JobParameters jobParameters) {
-            mWorkerHandler.post(this);
-            return true;
-        }
-
-        @Override
-        public boolean onStopJob(JobParameters jobParameters) {
-            mWorkerHandler.removeCallbacksAndMessages(null);
-            return true;
-        }
-
-        /**
-         * Extracts the wallpaper colors and sends the result back through the receiver.
-         */
-        @Override
-        public void run() {
-            int wallpaperId = getWallpaperId(this);
-
-            Bitmap bitmap = null;
-            Drawable drawable = null;
-
-            WallpaperManager wm = WallpaperManager.getInstance(this);
-            WallpaperInfo info = wm.getWallpaperInfo();
-            if (info != null) {
-                // For live wallpaper, extract colors from thumbnail
-                drawable = info.loadThumbnail(getPackageManager());
-            } else {
-                try (ParcelFileDescriptor fd = wm.getWallpaperFile(FLAG_SYSTEM)) {
-                    BitmapRegionDecoder decoder = BitmapRegionDecoder
-                            .newInstance(fd.getFileDescriptor(), false);
-
-                    int requestedArea = decoder.getWidth() * decoder.getHeight();
-                    BitmapFactory.Options options = new BitmapFactory.Options();
-
-                    if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
-                        double areaRatio =
-                                (double) requestedArea / MAX_WALLPAPER_EXTRACTION_AREA;
-                        double nearestPowOf2 =
-                                Math.floor(Math.log(areaRatio) / (2 * Math.log(2)));
-                        options.inSampleSize = (int) Math.pow(2, nearestPowOf2);
-                    }
-                    Rect region = new Rect(0, 0, decoder.getWidth(), decoder.getHeight());
-                    bitmap = decoder.decodeRegion(region, options);
-                    decoder.recycle();
-                } catch (IOException | NullPointerException e) {
-                    Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
-                }
-                if (bitmap == null) {
-                    drawable = wm.getDrawable();
-                }
-            }
-
-            if (drawable != null) {
-                // Calculate how big the bitmap needs to be.
-                // This avoids unnecessary processing and allocation inside Palette.
-                final int requestedArea = drawable.getIntrinsicWidth() *
-                        drawable.getIntrinsicHeight();
-                double scale = 1;
-                if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
-                    scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea);
-                }
-                bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * scale),
-                        (int) (drawable.getIntrinsicHeight() * scale), Bitmap.Config.ARGB_8888);
-                final Canvas bmpCanvas = new Canvas(bitmap);
-                drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
-                drawable.draw(bmpCanvas);
-            }
-
-            String value = VERSION_PREFIX + wallpaperId;
-
-            if (bitmap != null) {
-                int color = mColorExtractor.findDominantColorByHue(bitmap,
-                        MAX_WALLPAPER_EXTRACTION_AREA);
-                value += "," + color;
-            }
-
-            // Send the result
-            sendBroadcast(new Intent(ACTION_EXTRACTION_COMPLETE)
-                    .setPackage(getPackageName())
-                    .putExtra(KEY_COLORS, value));
-        }
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVOMR1.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVOMR1.java
deleted file mode 100644
index f34497d..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVOMR1.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides.dynamicui;
-
-import android.annotation.TargetApi;
-import android.app.WallpaperColors;
-import android.app.WallpaperManager;
-import android.app.WallpaperManager.OnColorsChangedListener;
-import android.content.Context;
-import android.graphics.Color;
-import android.util.Log;
-
-import java.lang.reflect.Method;
-
-import androidx.annotation.Nullable;
-
-@TargetApi(27)
-public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
-
-    private static final String TAG = "WMCompatVOMR1";
-
-    private final WallpaperManager mWm;
-    private Method mWCColorHintsMethod;
-
-    WallpaperManagerCompatVOMR1(Context context) throws Throwable {
-        mWm = context.getSystemService(WallpaperManager.class);
-        String className = WallpaperColors.class.getName();
-        try {
-            mWCColorHintsMethod = WallpaperColors.class.getDeclaredMethod("getColorHints");
-        } catch (Exception exc) {
-            Log.e(TAG, "getColorHints not available", exc);
-        }
-    }
-
-    @Nullable
-    @Override
-    public WallpaperColorsCompat getWallpaperColors(int which) {
-        return convertColorsObject(mWm.getWallpaperColors(which));
-    }
-
-    @Override
-    public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) {
-        OnColorsChangedListener onChangeListener = new OnColorsChangedListener() {
-            @Override
-            public void onColorsChanged(WallpaperColors colors, int which) {
-                listener.onColorsChanged(convertColorsObject(colors), which);
-            }
-        };
-        mWm.addOnColorsChangedListener(onChangeListener, null);
-    }
-
-    private WallpaperColorsCompat convertColorsObject(WallpaperColors colors) {
-        if (colors == null) {
-            return null;
-        }
-        Color primary = colors.getPrimaryColor();
-        Color secondary = colors.getSecondaryColor();
-        Color tertiary = colors.getTertiaryColor();
-        int primaryVal = primary != null ? primary.toArgb() : 0;
-        int secondaryVal = secondary != null ? secondary.toArgb() : 0;
-        int tertiaryVal = tertiary != null ? tertiary.toArgb() : 0;
-        int colorHints = 0;
-        try {
-            if (mWCColorHintsMethod != null) {
-                colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
-            }
-        } catch (Exception exc) {
-            Log.e(TAG, "error calling color hints", exc);
-        }
-        return new WallpaperColorsCompat(primaryVal, secondaryVal, tertiaryVal, colorHints);
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index ec3f93f..978c321 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -16,13 +16,14 @@
 package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
 
 import android.content.Context;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.Themes;
 
 /**
  * Definition for AllApps state
@@ -41,7 +42,7 @@
     };
 
     public AllAppsState(int id) {
-        super(id, ContainerType.ALLAPPS, STATE_FLAGS);
+        super(id, LAUNCHER_STATE_ALLAPPS, STATE_FLAGS);
     }
 
     @Override
@@ -56,7 +57,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return ALL_APPS_HEADER | ALL_APPS_CONTENT;
+        return ALL_APPS_CONTENT;
     }
 
     @Override
@@ -74,4 +75,9 @@
     public float getVerticalProgress(Launcher launcher) {
         return 0f;
     }
+
+    @Override
+    public int getWorkspaceScrimColor(Launcher launcher) {
+        return Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
+    }
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
index 7a6332c..d154317 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -15,10 +15,14 @@
  */
 package com.android.launcher3.uioverrides.states;
 
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
+
 import android.content.Context;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 
 /**
  * Definition for overview state
@@ -26,7 +30,7 @@
 public class OverviewState extends LauncherState {
 
     public OverviewState(int id) {
-        super(id, ContainerType.WORKSPACE, FLAG_DISABLE_RESTORE);
+        super(id, LAUNCHER_STATE_OVERVIEW, FLAG_DISABLE_RESTORE);
     }
 
     @Override
@@ -38,10 +42,6 @@
         return new OverviewState(id);
     }
 
-    public static OverviewState newPeekState(int id) {
-        return new OverviewState(id);
-    }
-
     public static OverviewState newSwitchState(int id) {
         return new OverviewState(id);
     }
@@ -52,4 +52,16 @@
     public static OverviewState newModalTaskState(int id) {
         return new OverviewState(id);
     }
+
+    /**
+     *  New Overview substate that represents the overview in modal mode (one task shown on its own)
+     */
+    public static OverviewState newSplitSelectState(int id) {
+        return new OverviewState(id);
+    }
+
+    @Override
+    public int getWorkspaceScrimColor(Launcher launcher) {
+        return Themes.getAttrColor(launcher, R.attr.overviewScrimColor);
+    }
 }
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..da55c28
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,26 @@
+// 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 {
+    // 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-test-src-common",
+    srcs: ["src_common/**/*.java"],
+}
diff --git a/tests/Android.mk b/tests/Android.mk
index 41130be..6adc685 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -16,34 +16,6 @@
 LOCAL_PATH := $(call my-dir)
 
 #
-# Build rule for Tapl library.
-#
-include $(CLEAR_VARS)
-LOCAL_STATIC_JAVA_LIBRARIES := \
-	androidx.annotation_annotation \
-	androidx.test.runner \
-	androidx.test.rules \
-	androidx.test.uiautomator_uiautomator
-
-ifneq (,$(wildcard frameworks/base))
-else
-    LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib
-
-    LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
-        ../src/com/android/launcher3/ResourceUtils.java \
-        ../src/com/android/launcher3/util/SecureSettingsObserver.java \
-        ../src/com/android/launcher3/testing/TestProtocol.java
-endif
-
-LOCAL_MODULE := ub-launcher-aosp-tapl
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-LOCAL_SDK_VERSION := system_current
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#
 # Build rule for Launcher3Tests
 #
 include $(CLEAR_VARS)
@@ -56,14 +28,8 @@
     mockito-target-minus-junit4 \
     launcher_log_protos_lite
 
-ifneq (,$(wildcard frameworks/base))
-    LOCAL_PRIVATE_PLATFORM_APIS := true
-    LOCAL_STATIC_JAVA_LIBRARIES += launcher-aosp-tapl
-else
-    LOCAL_SDK_VERSION := system_28
-    LOCAL_MIN_SDK_VERSION := 21
-    LOCAL_STATIC_JAVA_LIBRARIES += ub-launcher-aosp-tapl
-endif
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_STATIC_JAVA_LIBRARIES += launcher-aosp-tapl
 
 LOCAL_SRC_FILES := \
 	$(call all-java-files-under, src) \
@@ -76,6 +42,10 @@
 
 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
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 1c8f095..4d980c4 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -16,6 +16,7 @@
 
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.android.launcher3.tests">
 
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
@@ -29,6 +30,7 @@
 
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig"
+            android:exported="true"
             android:label="No Config">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -39,6 +41,7 @@
 
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWdigetHidden"
+            android:exported="true"
             android:label="Hidden widget">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -49,6 +52,7 @@
 
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig"
+            android:exported="true"
             android:label="With Config">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -57,13 +61,26 @@
                        android:resource="@xml/appwidget_with_config"/>
         </receiver>
 
+        <receiver
+            android:name="com.android.launcher3.testcomponent.AppWidgetDynamicColors"
+            android:exported="true"
+            android:label="Dynamic Colors">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                android:resource="@xml/appwidget_dynamic_colors"/>
+        </receiver>
+
         <activity
-            android:name="com.android.launcher3.testcomponent.WidgetConfigActivity">
+            android:name="com.android.launcher3.testcomponent.WidgetConfigActivity"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
             </intent-filter>
         </activity>
-        <activity android:name="com.android.launcher3.testcomponent.CustomShortcutConfigActivity">
+        <activity android:name="com.android.launcher3.testcomponent.CustomShortcutConfigActivity"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.CREATE_SHORTCUT" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -72,6 +89,7 @@
         <activity
             android:name="com.android.launcher3.testcomponent.RequestPinItemActivity"
             android:icon="@drawable/test_drawable_pin_item"
+            android:exported="true"
             android:label="Test Pin Item">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -92,7 +110,7 @@
         <activity
             android:name="com.android.launcher3.testcomponent.TestLauncherActivity"
             android:clearTaskOnLaunch="true"
-            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
             android:enabled="false"
             android:label="Test launcher"
             android:launchMode="singleTask"
@@ -102,6 +120,7 @@
             android:stateNotNeeded="true"
             android:taskAffinity=""
             android:theme="@android:style/Theme.DeviceDefault.Light"
+            android:exported="true"
             android:windowSoftInputMode="adjustPan">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -114,6 +133,7 @@
         <activity
             android:name="com.android.launcher3.testcomponent.BaseTestingActivity"
             android:label="LauncherTestApp"
+            android:exported="true"
             android:taskAffinity="com.android.launcher3.testcomponent.Affinity1">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -128,6 +148,7 @@
         </activity>
         <activity-alias android:name="Activity2"
                         android:label="TestActivity2"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -136,6 +157,7 @@
         </activity-alias>
         <activity-alias android:name="Activity3"
                         android:label="TestActivity3"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -144,6 +166,7 @@
         </activity-alias>
         <activity-alias android:name="Activity4"
                         android:label="TestActivity4"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -152,6 +175,7 @@
         </activity-alias>
         <activity-alias android:name="Activity5"
                         android:label="TestActivity5"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -160,6 +184,7 @@
         </activity-alias>
         <activity-alias android:name="Activity6"
                         android:label="TestActivity6"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -168,6 +193,7 @@
         </activity-alias>
         <activity-alias android:name="Activity7"
                         android:label="TestActivity7"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -176,6 +202,7 @@
         </activity-alias>
         <activity-alias android:name="Activity8"
                         android:label="TestActivity8"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -184,6 +211,7 @@
         </activity-alias>
         <activity-alias android:name="Activity9"
                         android:label="TestActivity9"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -192,6 +220,7 @@
         </activity-alias>
         <activity-alias android:name="Activity10"
                         android:label="TestActivity10"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
@@ -200,11 +229,18 @@
         </activity-alias>
         <activity-alias android:name="Activity11"
                         android:label="TestActivity11"
+                        android:exported="true"
                         android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity-alias>
+
+        <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
+        <provider
+            android:name="androidx.startup.InitializationProvider"
+            android:authorities="${applicationId}.androidx-startup"
+            tools:node="remove" />
     </application>
 </manifest>
diff --git a/tests/Launcher3Tests.xml b/tests/Launcher3Tests.xml
new file mode 100644
index 0000000..3fff622
--- /dev/null
+++ b/tests/Launcher3Tests.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+<!-- This test config file is auto-generated. -->
+<configuration description="Runs Launcher3 tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="set-test-harness" value="true" />
+        <option name="run-command" value="am force-stop com.android.launcher3" />
+        <option name="run-command" value="pm uninstall com.google.android.apps.nexuslauncher" />
+        <option name="run-command" value="pm uninstall com.google.android.apps.nexuslauncher.out_of_proc_tests" />
+        <option name="run-command" value="pm uninstall com.google.android.apps.nexuslauncher.tests" />
+        <option name="run-command" value="pm disable com.google.android.googlequicksearchbox" />
+
+        <option name="run-command" value="input keyevent 82" />
+        <option name="run-command" value="settings delete secure assistant" />
+        <option name="run-command" value="settings put global airplane_mode_on 1" />
+        <option name="run-command" value="am broadcast -a android.intent.action.AIRPLANE_MODE" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="Launcher3Tests.apk" />
+        <option name="test-file-name" value="Launcher3.apk" />
+    </target_preparer>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/data/user/0/com.android.launcher3/files" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.launcher3.tests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/tests/dummy_app/AndroidManifest.xml b/tests/dummy_app/AndroidManifest.xml
index f00138c..d5e2320 100644
--- a/tests/dummy_app/AndroidManifest.xml
+++ b/tests/dummy_app/AndroidManifest.xml
@@ -26,6 +26,7 @@
         <activity
             android:name="Activity1"
             android:icon="@mipmap/ic_launcher1"
+            android:exported="true"
             android:label="Aardwolf">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/tests/res/layout/test_layout_appwidget_dynamic_colors.xml b/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
new file mode 100644
index 0000000..56c343e
--- /dev/null
+++ b/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:background="?android:attr/colorBackground"
+    android:padding="8dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout
+        android:orientation = "horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="neut1"/>
+        <ImageView
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:background="@android:color/system_neutral1_500"/>
+    </LinearLayout>
+    <LinearLayout
+        android:orientation = "horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="accent1"/>
+        <ImageView
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:background="@android:color/system_accent1_500"/>
+    </LinearLayout>
+    <LinearLayout
+        android:orientation = "horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="accent2"/>
+        <ImageView
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:background="@android:color/system_accent2_500"/>
+    </LinearLayout>
+
+    </LinearLayout>
\ No newline at end of file
diff --git a/tests/res/xml/appwidget_dynamic_colors.xml b/tests/res/xml/appwidget_dynamic_colors.xml
new file mode 100644
index 0000000..f6b9a04
--- /dev/null
+++ b/tests/res/xml/appwidget_dynamic_colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<appwidget-provider
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="1dp"
+    android:minHeight="1dp"
+    android:updatePeriodMillis="0"
+    android:initialLayout="@layout/test_layout_appwidget_dynamic_colors"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/res/xml/appwidget_hidden.xml b/tests/res/xml/appwidget_hidden.xml
index 6f0e006..f6cffb5 100644
--- a/tests/res/xml/appwidget_hidden.xml
+++ b/tests/res/xml/appwidget_hidden.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <appwidget-provider
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:minWidth="180dp"
-    android:minHeight="110dp"
+    android:minWidth="1dp"
+    android:minHeight="1dp"
     android:updatePeriodMillis="86400000"
     android:initialLayout="@layout/test_layout_appwidget_blue"
     android:resizeMode="horizontal|vertical"
diff --git a/tests/res/xml/appwidget_no_config.xml b/tests/res/xml/appwidget_no_config.xml
index d24dfe3..0d932dc 100644
--- a/tests/res/xml/appwidget_no_config.xml
+++ b/tests/res/xml/appwidget_no_config.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <appwidget-provider
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:minWidth="180dp"
-    android:minHeight="110dp"
+    android:minWidth="1dp"
+    android:minHeight="1dp"
     android:updatePeriodMillis="86400000"
     android:initialLayout="@layout/test_layout_appwidget_red"
     android:resizeMode="horizontal|vertical"
diff --git a/tests/res/xml/appwidget_with_config.xml b/tests/res/xml/appwidget_with_config.xml
index 8403689..813e6df 100644
--- a/tests/res/xml/appwidget_with_config.xml
+++ b/tests/res/xml/appwidget_with_config.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <appwidget-provider
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:minWidth="180dp"
-    android:minHeight="110dp"
+    android:minWidth="1dp"
+    android:minHeight="1dp"
     android:updatePeriodMillis="86400000"
     android:initialLayout="@layout/test_layout_appwidget_blue"
     android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity"
diff --git a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
deleted file mode 100644
index 39709a9..0000000
--- a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.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.allapps.search;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.content.ComponentName;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.model.data.AppInfo;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Unit tests for {@link DefaultAppSearchAlgorithm}
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class DefaultAppSearchAlgorithmTest {
-    private static final DefaultAppSearchAlgorithm.StringMatcher MATCHER =
-            DefaultAppSearchAlgorithm.StringMatcher.getInstance();
-
-    @Test
-    public void testMatches() {
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("white cow"), "cow", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whiteCow"), "cow", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whiteCOW"), "cow", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecowCOW"), "cow", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("white2cow"), "cow", MATCHER));
-
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitecow"), "cow", MATCHER));
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitEcow"), "cow", MATCHER));
-
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecowCow"), "cow", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("whitecow cow"), "cow", MATCHER));
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whitecowcow"), "cow", MATCHER));
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("whit ecowcow"), "cow", MATCHER));
-
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&dogs"), "dog", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&Dogs"), "dog", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("cats&Dogs"), "&", MATCHER));
-
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("2+43"), "43", MATCHER));
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("2+43"), "3", MATCHER));
-
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("Q"), "q", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("  Q"), "q", MATCHER));
-
-        // match lower case words
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("elephant"), "e", MATCHER));
-
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "电", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "电子", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "子", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("电子邮件"), "邮件", MATCHER));
-
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("Bot"), "ba", MATCHER));
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("bot"), "ba", MATCHER));
-    }
-
-    @Test
-    public void testMatchesVN() {
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드"), "다", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("드라이브"), "드", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷ", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("운로 드라이브"), "ㄷ", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("abc"), "åbç", MATCHER));
-        assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("Alpha"), "ål", MATCHER));
-
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷㄷ", MATCHER));
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("로드라이브"), "ㄷ", MATCHER));
-        assertFalse(DefaultAppSearchAlgorithm.matches(getInfo("abc"), "åç", MATCHER));
-    }
-
-    private AppInfo getInfo(String title) {
-        AppInfo info = new AppInfo();
-        info.title = title;
-        info.componentName = new ComponentName("Test", title);
-        return info;
-    }
-}
diff --git a/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java b/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java
new file mode 100644
index 0000000..413f404
--- /dev/null
+++ b/tests/src/com/android/launcher3/search/StringMatcherUtilityTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.search;
+
+import static com.android.launcher3.search.StringMatcherUtility.matches;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.search.StringMatcherUtility.StringMatcher;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link StringMatcherUtility}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class StringMatcherUtilityTest {
+    private static final StringMatcher MATCHER =
+            StringMatcher.getInstance();
+
+    @Test
+    public void testMatches() {
+        assertTrue(matches("white ", "white cow", MATCHER));
+        assertTrue(matches("white c", "white cow", MATCHER));
+        assertTrue(matches("cow", "white cow", MATCHER));
+        assertTrue(matches("cow", "whiteCow", MATCHER));
+        assertTrue(matches("cow", "whiteCOW", MATCHER));
+        assertTrue(matches("cow", "whitecowCOW", MATCHER));
+        assertTrue(matches("cow", "white2cow", MATCHER));
+
+        assertFalse(matches("cow", "whitecow", MATCHER));
+        assertFalse(matches("cow", "whitEcow", MATCHER));
+
+        assertTrue(matches("cow", "whitecowCow", MATCHER));
+        assertTrue(matches("cow", "whitecow cow", MATCHER));
+        assertFalse(matches("cow", "whitecowcow", MATCHER));
+        assertFalse(matches("cow", "whit ecowcow", MATCHER));
+
+        assertTrue(matches("dog", "cats&dogs", MATCHER));
+        assertTrue(matches("dog", "cats&Dogs", MATCHER));
+        assertTrue(matches("&", "cats&Dogs", MATCHER));
+
+        assertTrue(matches("43", "2+43", MATCHER));
+        assertFalse(matches("3", "2+43", MATCHER));
+
+        assertTrue(matches("q", "Q", MATCHER));
+        assertTrue(matches("q", "  Q", MATCHER));
+
+        // match lower case words
+        assertTrue(matches("e", "elephant", MATCHER));
+        assertTrue(matches("eL", "Elephant", MATCHER));
+
+        assertTrue(matches("电", "电子邮件", MATCHER));
+        assertTrue(matches("电子", "电子邮件", MATCHER));
+        assertTrue(matches("子", "电子邮件", MATCHER));
+        assertTrue(matches("邮件", "电子邮件", MATCHER));
+
+        assertFalse(matches("ba", "Bot", MATCHER));
+        assertFalse(matches("ba", "bot", MATCHER));
+        assertFalse(matches("phant", "elephant", MATCHER));
+        assertFalse(matches("elephants", "elephant", MATCHER));
+    }
+
+    @Test
+    public void testMatchesVN() {
+        assertTrue(matches("다", "다운로드", MATCHER));
+        assertTrue(matches("드", "드라이브", MATCHER));
+        assertTrue(matches("ㄷ", "다운로드 드라이브", MATCHER));
+        assertTrue(matches("ㄷ", "운로 드라이브", MATCHER));
+        assertTrue(matches("åbç", "abc", MATCHER));
+        assertTrue(matches("ål", "Alpha", MATCHER));
+
+        assertFalse(matches("ㄷㄷ", "다운로드 드라이브", MATCHER));
+        assertFalse(matches("ㄷ", "로드라이브", MATCHER));
+        assertFalse(matches("åç", "abc", MATCHER));
+    }
+}
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
similarity index 66%
copy from src/com/android/launcher3/util/SafeCloseable.java
copy to tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
index ba8ee04..5fb3454 100644
--- a/src/com/android/launcher3/util/SafeCloseable.java
+++ b/tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.util;
+package com.android.launcher3.testcomponent;
+
+import android.appwidget.AppWidgetProvider;
 
 /**
- * Extension of closeable which does not throw an exception
+ * A simple app widget showing a primary, secondary and neutral color.
  */
-public interface SafeCloseable extends AutoCloseable {
-
-    @Override
-    void close();
+public class AppWidgetDynamicColors extends AppWidgetProvider {
 }
diff --git a/tests/src/com/android/launcher3/testcomponent/ListViewService.java b/tests/src/com/android/launcher3/testcomponent/ListViewService.java
index 3da20e0..9e3a492 100644
--- a/tests/src/com/android/launcher3/testcomponent/ListViewService.java
+++ b/tests/src/com/android/launcher3/testcomponent/ListViewService.java
@@ -89,7 +89,7 @@
                 public RemoteViewsFactory onGetViewFactory(Intent intent) {
                     return SimpleViewsFactory.this;
                 }
-            }.onBind(new Intent("dummy_intent"));
+            }.onBind(new Intent("stub_intent"));
         }
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 858e183..dcb6dc1 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -53,7 +53,6 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.common.WidgetUtils;
-import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -64,9 +63,9 @@
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.FailureRewriterRule;
 import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.launcher3.util.rule.LauncherActivityRule;
+import com.android.launcher3.util.rule.ScreenRecordRule;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.util.rule.TestStabilityRule;
 
@@ -101,6 +100,7 @@
     private static final String TAG = "AbstractLauncherUiTest";
 
     private static String sStrictmodeDetectedActivityLeak;
+    private static boolean sDumpWasGenerated = false;
     private static boolean sActivityLeakReported;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
     protected static final ActivityLeakTracker ACTIVITY_LEAK_TRACKER = new ActivityLeakTracker();
@@ -116,8 +116,6 @@
         if (TestHelpers.isInLauncherProcess()) {
             StrictMode.VmPolicy.Builder builder =
                     new StrictMode.VmPolicy.Builder()
-// b/154772063
-//                            .detectActivityLeaks()
                             .penaltyLog()
                             .penaltyListener(Runnable::run, violation -> {
                                 if (sStrictmodeDetectedActivityLeak == null) {
@@ -141,7 +139,7 @@
         // Check whether activity leak detector has found leaked activities.
         Wait.atMost(AbstractLauncherUiTest::getActivityLeakErrorMessage,
                 () -> {
-                    launcher.getTotalPssKb();  // Triggers GC
+                    launcher.forceGc();
                     return MAIN_EXECUTOR.submit(
                             () -> ACTIVITY_LEAK_TRACKER.noLeakedActivities()).get();
                 }, DEFAULT_UI_TIMEOUT, launcher);
@@ -152,16 +150,34 @@
         return "Activity leak detector has found leaked activities, " + dumpHprofData() + ".";
     }
 
-    private static String dumpHprofData() {
-        try {
-            final String fileName = getInstrumentation().getTargetContext().getFilesDir().getPath()
-                    + "/ActivityLeakHeapDump.hprof";
-            Debug.dumpHprofData(fileName);
-            return "memory dump filename: " + fileName;
-        } catch (Throwable e) {
-            Log.e(TAG, "dumpHprofData failed", e);
-            return "failed to save memory dump";
+    public static String dumpHprofData() {
+        String result;
+        if (sDumpWasGenerated) {
+            Log.d("b/195319692", "dump has already been generated by another test",
+                    new Exception());
+            result = "dump has already been generated by another test";
+        } else {
+            try {
+                final String fileName =
+                        getInstrumentation().getTargetContext().getFilesDir().getPath()
+                                + "/ActivityLeakHeapDump.hprof";
+                if (TestHelpers.isInLauncherProcess()) {
+                    Debug.dumpHprofData(fileName);
+                } else {
+                    final UiDevice device = UiDevice.getInstance(getInstrumentation());
+                    device.executeShellCommand(
+                            "am dumpheap " + device.getLauncherPackageName() + " " + fileName);
+                }
+                sDumpWasGenerated = true;
+                Log.d("b/195319692", "sDumpWasGenerated := true", new Exception());
+                result = "memory dump filename: " + fileName;
+            } 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();
     }
 
     protected AbstractLauncherUiTest() {
@@ -192,6 +208,9 @@
     public ShellCommandRule mDisableHeadsUpNotification =
             ShellCommandRule.disableHeadsUpNotification();
 
+    @Rule
+    public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
+
     protected void clearPackageData(String pkg) throws IOException, InterruptedException {
         final CountDownLatch count = new CountDownLatch(2);
         final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@@ -217,7 +236,7 @@
 
     protected TestRule getRulesInsideActivityMonitor() {
         final RuleChain inner = RuleChain.outerRule(new PortraitLandscapeRunner(this))
-                .around(new FailureWatcher(mDevice));
+                .around(new FailureWatcher(mDevice, mLauncher));
 
         return TestHelpers.isInLauncherProcess()
                 ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher())
@@ -226,9 +245,8 @@
     }
 
     @Rule
-    public TestRule mOrderSensitiveRules = RuleChain.
-            outerRule(new FailureRewriterRule())
-            .around(new TestStabilityRule())
+    public TestRule mOrderSensitiveRules = RuleChain
+            .outerRule(new TestStabilityRule())
             .around(mActivityMonitor)
             .around(getRulesInsideActivityMonitor());
 
@@ -242,6 +260,7 @@
 
     @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");
@@ -252,7 +271,7 @@
             mDevice.waitForIdle();
         }
         Assert.assertTrue("Keyguard still visible",
-                mDevice.wait(
+                TestHelpers.wait(
                         Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
         Log.d(TAG, "Keyguard is not visible");
 
@@ -272,8 +291,6 @@
         }
 
         mLauncherPid = 0;
-        // Disable app tracker
-        AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
 
         mTargetContext = InstrumentationRegistry.getTargetContext();
         mTargetPackage = mTargetContext.getPackageName();
@@ -283,6 +300,8 @@
         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());
                 }
             }
@@ -291,13 +310,16 @@
 
     @After
     public void verifyLauncherState() {
-        // Limits UI tests affecting tests running after them.
-        mLauncher.waitForLauncherInitialized();
-        if (mLauncherPid != 0) {
-            assertEquals("Launcher crashed, pid mismatch:",
-                    mLauncherPid, mLauncher.getPid().intValue());
+        try {
+            // Limits UI tests affecting tests running after them.
+            mLauncher.waitForLauncherInitialized();
+            if (mLauncherPid != 0) {
+                assertEquals("Launcher crashed, pid mismatch:",
+                        mLauncherPid, mLauncher.getPid().intValue());
+            }
+        } finally {
+            mLauncher.onTestFinish();
         }
-        checkDetectedLeaks(mLauncher);
     }
 
     protected void clearLauncherData() {
@@ -486,8 +508,7 @@
         }
         getInstrumentation().getTargetContext().startActivity(intent);
         assertTrue("App didn't start: " + selector,
-                UiDevice.getInstance(getInstrumentation())
-                        .wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+                TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
     }
 
     public static ActivityInfo resolveSystemAppInfo(String category) {
@@ -506,6 +527,7 @@
         // Destroy Launcher activity.
         executeOnLauncher(launcher -> {
             if (launcher != null) {
+                onLauncherActivityClose(launcher);
                 launcher.finish();
             }
         });
@@ -514,7 +536,7 @@
     }
 
     protected boolean isInBackground(Launcher launcher) {
-        return !launcher.hasBeenResumed();
+        return launcher == null || !launcher.hasBeenResumed();
     }
 
     protected boolean isInState(Supplier<LauncherState> state) {
@@ -527,7 +549,7 @@
         return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
     }
 
-    private static void checkLauncherIntegrity(
+    private void checkLauncherIntegrity(
             Launcher launcher, ContainerType expectedContainerType) {
         if (launcher != null) {
             final StateManager<LauncherState> stateManager = launcher.getStateManager();
@@ -538,10 +560,8 @@
                     stableState == stateManager.getState());
 
             final boolean isResumed = launcher.hasBeenResumed();
-            assertTrue("hasBeenResumed() != isStarted(), hasBeenResumed(): " + isResumed,
-                    isResumed == launcher.isStarted());
-            assertTrue("hasBeenResumed() != isUserActive(), hasBeenResumed(): " + isResumed,
-                    isResumed == launcher.isUserActive());
+            final boolean isStarted = launcher.isStarted();
+            checkLauncherState(launcher, expectedContainerType, isResumed, isStarted);
 
             final int ordinal = stableState.ordinal;
 
@@ -564,8 +584,7 @@
                     break;
                 }
                 case OVERVIEW: {
-                    assertTrue(
-                            "Launcher is not resumed in state: " + expectedContainerType,
+                    checkLauncherStateInOverview(launcher, expectedContainerType, isStarted,
                             isResumed);
                     assertTrue(TestProtocol.stateOrdinalToString(ordinal),
                             ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL);
@@ -590,4 +609,21 @@
                             expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
         }
     }
+
+    protected void checkLauncherState(Launcher launcher, ContainerType expectedContainerType,
+            boolean isResumed, boolean isStarted) {
+        assertTrue("hasBeenResumed() != isStarted(), hasBeenResumed(): " + isResumed,
+                isResumed == isStarted);
+        assertTrue("hasBeenResumed() != isUserActive(), hasBeenResumed(): " + isResumed,
+                isResumed == launcher.isUserActive());
+    }
+
+    protected void checkLauncherStateInOverview(Launcher launcher,
+            ContainerType expectedContainerType, boolean isStarted, boolean isResumed) {
+        assertTrue("Launcher is not resumed in state: " + expectedContainerType,
+                isResumed);
+    }
+
+    protected void onLauncherActivityClose(Launcher launcher) {
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
index 202dcb1..2db7472 100644
--- a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
+++ b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
@@ -25,6 +25,7 @@
 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<>();
@@ -73,20 +74,17 @@
     }
 
     public boolean noLeakedActivities() {
-        int liveActivities = 0;
-        int destroyedActivities = 0;
-
         for (Activity activity : mActivities.keySet()) {
             if (activity.isDestroyed()) {
-                ++destroyedActivities;
-            } else {
-                ++liveActivities;
+                return false;
             }
         }
 
-        if (liveActivities > 2) return false;
+        return mActivities.size() <= 2;
+    }
 
-        // It's OK to have 1 leaked activity if no active activities exist.
-        return liveActivities == 0 ? destroyedActivities <= 1 : destroyedActivities == 0;
+    public String getActivitiesList() {
+        return mActivities.keySet().stream().map(a -> a.getClass().getSimpleName())
+                .collect(Collectors.joining(","));
     }
 }
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 266f0ae..8f2d528 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -4,6 +4,7 @@
 import android.view.Surface;
 
 import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.testing.TestProtocol;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 34e425d..d8576df 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -36,11 +36,13 @@
 import com.android.launcher3.tapl.AppIconMenuItem;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.views.OptionsPopupView;
-import com.android.launcher3.widget.WidgetsFullSheet;
-import com.android.launcher3.widget.WidgetsRecyclerView;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.WidgetsRecyclerView;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -91,6 +93,7 @@
     }
 
     @Test
+    @ScreenRecord //b/187080582
     public void testDevicePressMenu() throws Exception {
         mDevice.pressMenu();
         mDevice.waitForIdle();
@@ -101,7 +104,7 @@
         mLauncher.pressHome();
     }
 
-    @Test
+    @Ignore
     public void testOpenHomeSettingsFromWorkspace() {
         mDevice.pressMenu();
         mDevice.waitForIdle();
@@ -244,6 +247,7 @@
     }
 
     @Test
+    @Ignore("b/197802324")
     @PortraitLandscape
     public void testWidgets() throws Exception {
         // Test opening widgets.
@@ -292,13 +296,15 @@
         try {
             final AppIconMenu menu = allApps.
                     getAppIcon(APP_NAME).
-                    openMenu();
+                    openDeepShortcutMenu();
 
             executeOnLauncher(
                     launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
                             isOptionsPopupVisible(launcher)));
 
-            menu.getMenuItem(1).launch(getAppPackageName());
+            final AppIconMenuItem menuItem = menu.getMenuItem(1);
+            assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText());
+            menuItem.launch(getAppPackageName());
         } finally {
             allApps.unfreeze();
         }
@@ -331,19 +337,31 @@
         // 1. Open all apps and wait for load complete.
         // 2. Find the app and long press it to show shortcuts.
         // 3. Press icon center until shortcuts appear
-        final AllApps allApps = mLauncher.
-                getWorkspace().
-                switchToAllApps();
+        final AllApps allApps = mLauncher
+                .getWorkspace()
+                .switchToAllApps();
         allApps.freeze();
         try {
-            final AppIconMenuItem menuItem = allApps.
-                    getAppIcon(APP_NAME).
-                    openMenu().
-                    getMenuItem(0);
-            final String shortcutName = menuItem.getText();
+            final AppIconMenu menu = allApps
+                    .getAppIcon(APP_NAME)
+                    .openDeepShortcutMenu();
+            final AppIconMenuItem menuItem0 = menu.getMenuItem(0);
+            final AppIconMenuItem menuItem2 = menu.getMenuItem(2);
+
+            final AppIconMenuItem menuItem;
+
+            final String expectedShortcutName = "Shortcut 3";
+            if (menuItem0.getText().equals(expectedShortcutName)) {
+                menuItem = menuItem0;
+            } else {
+                final String shortcutName2 = menuItem2.getText();
+                assertEquals("Wrong menu item", expectedShortcutName, shortcutName2);
+                menuItem = menuItem2;
+            }
 
             menuItem.dragToWorkspace(false, false);
-            mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName());
+            mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName)
+                    .launch(getAppPackageName());
         } finally {
             allApps.unfreeze();
         }
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
index eceff34..083f580 100644
--- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -24,9 +24,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.testcomponent.AppWidgetNoConfig;
 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
 import java.util.concurrent.Callable;
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 9d4ccff..4978c01 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -28,7 +28,6 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -39,6 +38,7 @@
 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;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -92,9 +92,8 @@
 
         // Drag widget to homescreen
         WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
-        widgets.
-                getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager())).
-                dragToWorkspace(true, false);
+        widgets.getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))
+                .dragToWorkspace(true, false);
         // Widget id for which the config activity was opened
         mWidgetId = monitor.getWidgetId();
 
@@ -103,6 +102,7 @@
 
         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));
         } else {
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index f146db5..dad4f2b 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -23,12 +23,12 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.tapl.Widget;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -58,6 +58,8 @@
                 openAllWidgets().
                 getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())).
                 dragToWorkspace(false, false);
+        // Dismiss widget resize frame.
+        mDevice.pressHome();
 
         assertTrue(mActivityMonitor.itemExists(
                 (info, view) -> info instanceof LauncherAppWidgetInfo &&
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index a4aa9f2..9c6c317 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -37,7 +37,6 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -47,6 +46,7 @@
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
 import org.junit.After;
@@ -192,7 +192,7 @@
                         WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
         executeOnLauncher(l -> l.getAppWidgetHost().startListening());
         verifyWidgetPresent(info);
-        assertNull(mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT));
+        assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100));
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0e43d81..745dc22 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.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -43,6 +42,7 @@
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 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,6 +78,7 @@
     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 &&
@@ -86,6 +87,7 @@
     }
 
     @Test
+    @ScreenRecord  //b/192005114
     public void testPinWidgetNoConfig_customPreview() throws Throwable {
         // Command to set custom preview
         Intent command = RequestPinItemActivity.getCommandIntent(
@@ -126,9 +128,6 @@
 
     private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
             Intent... commandIntents) throws Throwable {
-        if (!Utilities.ATLEAST_OREO) {
-            return;
-        }
         clearHomescreen();
         mDevice.pressHome();
 
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 55e5744..67f3902 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -22,6 +22,8 @@
 
 import androidx.test.uiautomator.UiDevice;
 
+import org.junit.Assert;
+
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -48,7 +50,10 @@
         in.close();
         out.close();
 
-        UiDevice.getInstance(getInstrumentation()).executeShellCommand("pm install " + apkFilename);
+        final String result = UiDevice.getInstance(getInstrumentation())
+                .executeShellCommand("pm install " + apkFilename);
+        Assert.assertTrue("Failed to install wellbeing test apk; make sure the device is rooted",
+                "Success".equals(result.replaceAll("\\s+", "")));
     }
 
     public static void uninstallDummyApp() throws IOException {
diff --git a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
deleted file mode 100644
index e4f520f..0000000
--- a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
+++ /dev/null
@@ -1,127 +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.rule;
-
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import android.os.SystemClock;
-
-import androidx.test.uiautomator.UiDevice;
-
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.regex.Pattern;
-
-class FailureInvestigator {
-    private static boolean matches(String regex, CharSequence string) {
-        return Pattern.compile(regex).matcher(string).find();
-    }
-
-    static class LogcatMatch {
-        String logcatPattern;
-        int bug;
-
-        LogcatMatch(String logcatPattern, int bug) {
-            this.logcatPattern = logcatPattern;
-            this.bug = bug;
-        }
-    }
-
-    static class ExceptionMatch {
-        String exceptionPattern;
-        LogcatMatch[] logcatMatches;
-
-        ExceptionMatch(String exceptionPattern, LogcatMatch[] logcatMatches) {
-            this.exceptionPattern = exceptionPattern;
-            this.logcatMatches = logcatMatches;
-        }
-    }
-
-    private static final ExceptionMatch[] EXCEPTION_MATCHES = {
-            new ExceptionMatch(
-                    "java.lang.AssertionError: http://go/tapl : Tests are broken by a "
-                            + "non-Launcher system error: (Phone is locked|Screen is empty)",
-                    new LogcatMatch[]{
-                            new LogcatMatch(
-                                    "BroadcastQueue: Can't deliver broadcast to com.android"
-                                            + ".systemui.*Crashing it",
-                                    147845913),
-                            new LogcatMatch(
-                                    "Attempt to invoke virtual method 'boolean android\\"
-                                            + ".graphics\\.Bitmap\\.isRecycled\\(\\)' on a null "
-                                            + "object reference",
-                                    148424291),
-                            new LogcatMatch(
-                                    "java\\.lang\\.IllegalArgumentException\\: Ranking map "
-                                            + "doesn't contain key",
-                                    148570537),
-                    }),
-            new ExceptionMatch("Launcher didn't initialize",
-                    new LogcatMatch[]{
-                            new LogcatMatch(
-                                    "ActivityManager: Reason: executing service com.google"
-                                            + ".android.apps.nexuslauncher/com.android.launcher3"
-                                            + ".notification.NotificationListener",
-                                    148238677),
-                    }),
-    };
-
-    static int getBugForFailure(CharSequence exception) {
-        if ("com.google.android.setupwizard".equals(
-                UiDevice.getInstance(getInstrumentation()).getLauncherPackageName())) {
-            return 145935261;
-        }
-
-        if (matches("java\\.lang\\.AssertionError\\: http\\:\\/\\/go\\/tapl \\: want to get "
-                + "workspace object; Presence of recents button doesn't match the interaction "
-                + "mode, mode\\=ZERO_BUTTON, hasRecents\\=true", exception)) {
-            return 148422894;
-        }
-
-        final String logSinceBoot;
-        try {
-            final String systemBootTime =
-                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(
-                            new Date(System.currentTimeMillis() - SystemClock.elapsedRealtime()));
-
-            logSinceBoot =
-                    UiDevice.getInstance(getInstrumentation())
-                            .executeShellCommand("logcat -d -t " + systemBootTime.replace(" ", ""));
-        } catch (IOException | OutOfMemoryError e) {
-            return 0;
-        }
-
-        if (matches("android\\:\\:uirenderer\\:\\:renderthread\\:\\:EglManager\\:\\:swapBuffers",
-                logSinceBoot)) {
-            return 148529608;
-        }
-
-        for (ExceptionMatch exceptionMatch : EXCEPTION_MATCHES) {
-            if (matches(exceptionMatch.exceptionPattern, exception)) {
-                for (LogcatMatch logcatMatch : exceptionMatch.logcatMatches) {
-                    if (matches(logcatMatch.logcatPattern, logSinceBoot)) {
-                        return logcatMatch.bug;
-                    }
-                }
-                break;
-            }
-        }
-
-        return 0;
-    }
-}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureRewriterRule.java b/tests/src/com/android/launcher3/util/rule/FailureRewriterRule.java
deleted file mode 100644
index 99ddee4..0000000
--- a/tests/src/com/android/launcher3/util/rule/FailureRewriterRule.java
+++ /dev/null
@@ -1,47 +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.rule;
-
-import android.util.Log;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-public class FailureRewriterRule implements TestRule {
-    private static final String TAG = "FailureRewriter";
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                try {
-                    base.evaluate();
-                } catch (Throwable e) {
-                    final int bug = FailureInvestigator.getBugForFailure(e.toString());
-                    if (bug == 0) throw e;
-
-                    Log.e(TAG, "Known bug found for the original failure "
-                            + android.util.Log.getStackTraceString(e));
-                    throw new AssertionError(
-                            "Detected a failure that matches a known bug b/" + bug);
-                }
-            }
-        };
-    }
-}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index cdda0f0..dc59bdd 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -2,37 +2,39 @@
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.util.Log;
 
 import androidx.test.uiautomator.UiDevice;
 
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 
-import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 public class FailureWatcher extends TestWatcher {
     private static final String TAG = "FailureWatcher";
     final private UiDevice mDevice;
+    private final LauncherInstrumentation mLauncher;
 
-    public FailureWatcher(UiDevice device) {
+    public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) {
         mDevice = device;
+        mLauncher = launcher;
     }
 
-    private static void dumpViewHierarchy(UiDevice device) {
-        final ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        try {
-            device.dumpWindowHierarchy(stream);
-            stream.flush();
-            stream.close();
-            for (String line : stream.toString().split("\\r?\\n")) {
-                Log.e(TAG, line.trim());
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "error dumping XML to logcat", e);
-        }
+    @Override
+    protected void succeeded(Description description) {
+        super.succeeded(description);
+        AbstractLauncherUiTest.checkDetectedLeaks(mLauncher);
     }
 
     @Override
@@ -42,22 +44,41 @@
 
     public static void onError(UiDevice device, Description description, Throwable e) {
         if (device == null) return;
-        final String pathname = getInstrumentation().getTargetContext().
-                getFilesDir().getPath() + "/TestScreenshot-" + description.getMethodName()
-                + ".png";
-        Log.e(TAG, "Failed test " + description.getMethodName() +
-                ", screenshot will be saved to " + pathname +
-                ", track trace is below, UI object dump is further below:\n" +
-                Log.getStackTraceString(e));
-        dumpViewHierarchy(device);
+        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");
 
-        try {
-            final String dumpsysResult = device.executeShellCommand(
-                    "dumpsys activity service TouchInteractionService");
-            Log.d(TAG, "TouchInteractionService: " + dumpsysResult);
-        } catch (IOException ex) {
+        // Dump window hierarchy
+        try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(hierarchy))) {
+            out.putNextEntry(new ZipEntry("bugreport.txt"));
+            dumpStringCommand("dumpsys window windows", out);
+            dumpStringCommand("dumpsys package", out);
+            dumpStringCommand("dumpsys activity service TouchInteractionService", out);
+            out.closeEntry();
+
+            out.putNextEntry(new ZipEntry("visible_windows.zip"));
+            dumpCommand("cmd window dump-visible-window-views", out);
+            out.closeEntry();
+        } 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);
+    }
+
+    private static void dumpStringCommand(String cmd, OutputStream out) throws IOException {
+        out.write(("\n\n" + cmd + "\n").getBytes());
+        dumpCommand(cmd, out);
+    }
+
+    private static void dumpCommand(String cmd, OutputStream out) throws IOException {
+        try (AutoCloseInputStream in = new AutoCloseInputStream(getInstrumentation()
+                .getUiAutomation().executeShellCommand(cmd))) {
+            FileUtils.copy(in, out);
         }
-
-        device.takeScreenshot(new File(pathname));
     }
 }
diff --git a/tests/src/com/android/launcher3/util/rule/ScreenRecordRule.java b/tests/src/com/android/launcher3/util/rule/ScreenRecordRule.java
new file mode 100644
index 0000000..7a5cf2c
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/ScreenRecordRule.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.util.rule;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Rule which captures a screen record for a test.
+ * After adding this rule to the test class, apply the annotation @ScreenRecord to individual tests
+ */
+public class ScreenRecordRule implements TestRule {
+
+    private static final String TAG = "ScreenRecordRule";
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        if (description.getAnnotation(ScreenRecord.class) == null) {
+            return base;
+        }
+
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                Instrumentation inst = getInstrumentation();
+                UiAutomation automation = inst.getUiAutomation();
+                UiDevice device = UiDevice.getInstance(inst);
+
+                File outputFile = new File(inst.getTargetContext().getFilesDir(),
+                        "screenrecord-" + description.getMethodName() + ".mp4");
+                device.executeShellCommand("killall screenrecord");
+                ParcelFileDescriptor output =
+                        automation.executeShellCommand("screenrecord " + outputFile);
+                String screenRecordPid = device.executeShellCommand("pidof screenrecord");
+                boolean success = false;
+                try {
+                    base.evaluate();
+                    success = true;
+                } finally {
+                    device.executeShellCommand("kill -INT " + screenRecordPid);
+                    Log.e(TAG, "Screenrecord captured at: " + outputFile);
+                    output.close();
+                    if (success) {
+                        automation.executeShellCommand("rm " + outputFile);
+                    }
+                }
+            }
+        };
+    }
+
+    /**
+     * Interface to indicate that the test should capture screenrecord
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    public @interface ScreenRecord {
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
index 0ec0f02..2b2fef4 100644
--- a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
+++ b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
@@ -15,21 +15,27 @@
  */
 package com.android.launcher3.util.rule;
 
-import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
-
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
+
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
+import android.util.Log;
 
 import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
 
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+import org.junit.Assert;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+
 /**
  * Test rule which executes a shell command at the start of the test.
  */
@@ -37,10 +43,19 @@
 
     private final String mCmd;
     private final String mRevertCommand;
+    private final boolean mCheckSuccess;
+    private final Runnable mAdditionalChecks;
 
-    public ShellCommandRule(String cmd, @Nullable String revertCommand) {
+    public ShellCommandRule(String cmd, @Nullable String revertCommand, boolean checkSuccess,
+            Runnable additionalChecks) {
         mCmd = cmd;
         mRevertCommand = revertCommand;
+        mCheckSuccess = checkSuccess;
+        mAdditionalChecks = additionalChecks;
+    }
+
+    public ShellCommandRule(String cmd, @Nullable String revertCommand) {
+        this(cmd, revertCommand, false, null);
     }
 
     @Override
@@ -48,12 +63,27 @@
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                UiDevice.getInstance(getInstrumentation()).executeShellCommand(mCmd);
+                final String result =
+                        UiDevice.getInstance(getInstrumentation()).executeShellCommand(mCmd);
+                if (mCheckSuccess) {
+                    Assert.assertTrue(
+                            "Failed command: " + mCmd + ", result: " + result,
+                            "Success".equals(result.replaceAll("\\s", "")));
+                }
+                if (mAdditionalChecks != null) mAdditionalChecks.run();
                 try {
                     base.evaluate();
                 } finally {
                     if (mRevertCommand != null) {
-                        UiDevice.getInstance(getInstrumentation()).executeShellCommand(mRevertCommand);
+                        final String revertResult = UiDevice.getInstance(
+                                getInstrumentation()).executeShellCommand(
+                                mRevertCommand);
+                        if (mCheckSuccess) {
+                            Assert.assertTrue(
+                                    "Failed command: " + mRevertCommand
+                                            + ", result: " + revertResult,
+                                    "Success".equals(result.replaceAll("\\s", "")));
+                        }
                     }
                 }
             }
@@ -72,7 +102,15 @@
      * Sets the target launcher as default launcher.
      */
     public static ShellCommandRule setDefaultLauncher() {
-        return new ShellCommandRule(getLauncherCommand(getLauncherInMyProcess()), null);
+        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)
+                                .flattenToString(),
+                        PackageManagerWrapper.getInstance().getHomeActivities(new ArrayList<>())
+                                .flattenToString()));
     }
 
     public static String getLauncherCommand(ActivityInfo launcher) {
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index 61f5e05..0e27b61 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -53,8 +53,6 @@
                     + ")$");
 
     public static final int LOCAL = 0x1;
-    public static final int UNBUNDLED_PRESUBMIT = 0x2;
-    public static final int UNBUNDLED_POSTSUBMIT = 0x4;
     public static final int PLATFORM_PRESUBMIT = 0x8;
     public static final int PLATFORM_POSTSUBMIT = 0x10;
 
@@ -136,14 +134,6 @@
                         platformBuildMatcher.group("postsubmit") != null)) {
             Log.d(TAG, "LOCAL RUN");
             sRunFlavor = LOCAL;
-        } else if (launcherBuildMatcher.group("presubmit") != null
-                && platformBuildMatcher.group("postsubmit") != null) {
-            Log.d(TAG, "UNBUNDLED PRESUBMIT");
-            sRunFlavor = UNBUNDLED_PRESUBMIT;
-        } else if (launcherBuildMatcher.group("postsubmit") != null
-                && platformBuildMatcher.group("postsubmit") != null) {
-            Log.d(TAG, "UNBUNDLED POSTSUBMIT");
-            sRunFlavor = UNBUNDLED_POSTSUBMIT;
         } else if (launcherBuildMatcher.group("platform") != null
                 && platformBuildMatcher.group("presubmit") != null) {
             Log.d(TAG, "PLATFORM PRESUBMIT");
diff --git a/tests/src_common/com/android/launcher3/common/WidgetUtils.java b/tests/src_common/com/android/launcher3/common/WidgetUtils.java
index c0913bf..97500e3 100644
--- a/tests/src_common/com/android/launcher3/common/WidgetUtils.java
+++ b/tests/src_common/com/android/launcher3/common/WidgetUtils.java
@@ -16,19 +16,18 @@
 package com.android.launcher3.common;
 
 import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
-import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
 
 import android.appwidget.AppWidgetHost;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Bundle;
 
-import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 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;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
@@ -57,12 +56,14 @@
         item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
 
         if (bindWidget) {
-            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(info);
+            PendingAddWidgetInfo pendingInfo =
+                    new PendingAddWidgetInfo(
+                            info, LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY);
             pendingInfo.spanX = item.spanX;
             pendingInfo.spanY = item.spanY;
             pendingInfo.minSpanX = item.minSpanX;
             pendingInfo.minSpanY = item.minSpanY;
-            Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo);
+            Bundle options = pendingInfo.getDefaultSizeOptions(targetContext);
 
             AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
             int widgetId = host.allocateAppWidgetId();
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src_disabled/WorkTabTest.java
similarity index 76%
rename from tests/src/com/android/launcher3/ui/WorkTabTest.java
rename to tests/src_disabled/WorkTabTest.java
index f5f93c4..bfacc74 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src_disabled/WorkTabTest.java
@@ -17,6 +17,7 @@
 
 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;
@@ -35,16 +36,19 @@
 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)
@@ -55,7 +59,9 @@
     private static final int WORK_PAGE = AllAppsContainerView.AdapterHolder.WORK;
 
     @Before
-    public void createWorkProfile() throws Exception {
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
         String output =
                 mDevice.executeShellCommand(
                         "pm create-user --profileOf 0 --managed TestProfile");
@@ -63,26 +69,42 @@
 
         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();
@@ -114,6 +136,7 @@
                 l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
     }
 
+    @Ignore("b/182844465")
     @Test
     public void testWorkEduFlow() {
         mDevice.pressHome();
@@ -122,9 +145,13 @@
                 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);
+        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();
@@ -135,17 +162,25 @@
             workEduView.findViewById(R.id.proceed).callOnClick();
         });
 
-        executeOnLauncher(launcher -> Log.d(TestProtocol.WORK_PROFILE_REMOVED,
-                "Work profile status: " + launcher.getAppsView().isPersonalTabVisible()));
-
+        AtomicInteger attempt = new AtomicInteger(0);
         // verify work edu is seen next
-        waitForLauncherCondition("Launcher did not show the next edu screen", l ->
-                ((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage() == WORK_PAGE
-                        && ((TextView) workEduView.findViewById(
-                        R.id.content_text)).getText().equals(
-                        l.getResources().getString(R.string.work_profile_edu_work_apps)));
+        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();
@@ -170,6 +205,10 @@
         // 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);
@@ -189,7 +228,7 @@
             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));
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index e1fde3b..0582bc9 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -28,7 +28,7 @@
 
 public class AddToHomeScreenPrompt {
     private static final Pattern ADD_AUTOMATICALLY =
-            Pattern.compile("^Add automatically$", CASE_INSENSITIVE);
+            Pattern.compile("^Add to Home screen$", CASE_INSENSITIVE);
     private final LauncherInstrumentation mLauncher;
     private final UiObject2 mWidgetCell;
 
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index b6c17df..1cb6b2d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -25,9 +25,9 @@
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.StaleObjectException;
 import androidx.test.uiautomator.UiObject2;
 
-import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.testing.TestProtocol;
 
 import java.util.stream.Collectors;
@@ -62,7 +62,13 @@
 
     private boolean hasClickableIcon(UiObject2 allAppsContainer, UiObject2 appListRecycler,
             BySelector appIconSelector, int displayBottom) {
-        final UiObject2 icon = appListRecycler.findObject(appIconSelector);
+        final UiObject2 icon;
+        try {
+            icon = appListRecycler.findObject(appIconSelector);
+        } catch (StaleObjectException e) {
+            mLauncher.fail("All apps recycler disappeared from screen");
+            return false;
+        }
         if (icon == null) {
             LauncherInstrumentation.log("hasClickableIcon: icon not visible");
             return false;
@@ -108,26 +114,24 @@
                     "apps_list_view");
             final UiObject2 searchBox = getSearchBox(allAppsContainer);
 
-            int bottomGestureMargin = ResourceUtils.getNavbarSize(
-                    ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
-            int deviceHeight = mLauncher.getDevice().getDisplayHeight();
-            int displayBottom = deviceHeight - bottomGestureMargin;
+            int deviceHeight = mLauncher.getRealDisplaySize().y;
+            int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen();
             final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
             if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
-                    displayBottom)) {
+                    bottomGestureStartOnScreen)) {
                 scrollBackToBeginning();
                 int attempts = 0;
                 int scroll = getAllAppsScroll();
                 try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled")) {
                     while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
-                            displayBottom)) {
+                            bottomGestureStartOnScreen)) {
                         mLauncher.scrollToLastVisibleRow(
                                 allAppsContainer,
                                 mLauncher.getObjectsInContainer(allAppsContainer, "icon")
                                         .stream()
                                         .filter(icon ->
-                                                        mLauncher.getVisibleBounds(icon).bottom
-                                                        <= displayBottom)
+                                                mLauncher.getVisibleBounds(icon).top
+                                                        < bottomGestureStartOnScreen)
                                         .collect(Collectors.toList()),
                                 mLauncher.getVisibleBounds(searchBox).bottom
                                         - mLauncher.getVisibleBounds(allAppsContainer).top);
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 5de5b4a..21099b4 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -47,6 +47,16 @@
     public AppIconMenu openMenu() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
+                    mObject, "popup_container", LONG_CLICK_EVENT));
+        }
+    }
+
+    /**
+     * Long-clicks the icon to open its menu, and looks at the deep shortcuts container only.
+     */
+    public AppIconMenu openDeepShortcutMenu() {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
                     mObject, "deep_shortcuts_container", LONG_CLICK_EVENT));
         }
     }
@@ -58,11 +68,16 @@
 
     @Override
     protected String getLongPressIndicator() {
-        return "deep_shortcuts_container";
+        return "popup_container";
     }
 
     @Override
     protected void expectActivityStartEvents() {
         mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START);
     }
+
+    @Override
+    protected String launchableType() {
+        return "app icon";
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index a40919b..ac0db08 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -20,8 +20,6 @@
 
 import com.android.launcher3.testing.TestProtocol;
 
-import java.util.regex.Pattern;
-
 /**
  * Menu item in an app icon menu.
  */
@@ -51,4 +49,9 @@
     protected void expectActivityStartEvents() {
         mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START);
     }
+
+    @Override
+    protected String launchableType() {
+        return "app icon menu item";
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 816cd7d..4b1610e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -16,8 +16,9 @@
 
 package com.android.launcher3.tapl;
 
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
 import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
-import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 
 import android.graphics.Point;
@@ -87,13 +88,9 @@
                                 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
                                 : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
 
-                // b/156044202
-                mLauncher.log("Hierarchy before swiping up to overview:");
-                mLauncher.dumpViewHierarchy();
-
                 mLauncher.sendPointer(
                         downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
-                mLauncher.executeAndWaitForEvent(
+                mLauncher.executeAndWaitForLauncherEvent(
                         () -> mLauncher.movePointer(
                                 downTime,
                                 downTime,
@@ -102,12 +99,12 @@
                                 end,
                                 gestureScope),
                         event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(event.getClassName()),
-                        () -> "Pause wasn't detected");
+                        () -> "Pause wasn't detected", "swiping and holding");
                 mLauncher.runToState(
                         () -> mLauncher.sendPointer(
                                 downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end,
                                 gestureScope),
-                        OVERVIEW_STATE_ORDINAL);
+                        OVERVIEW_STATE_ORDINAL, "sending UP event");
                 break;
             }
 
@@ -125,7 +122,9 @@
                     endY = startY - swipeLength;
                 } else {
                     startX = getSwipeStartX();
-                    endX = startX - swipeLength;
+                    // TODO(b/184059820) make horizontal swipe use swipe width not height, for the
+                    // moment just double the swipe length.
+                    endX = startX - swipeLength * 2;
                     startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
                 }
 
@@ -138,7 +137,7 @@
                 mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
                 mLauncher.runToState(
                         () -> mLauncher.waitForSystemUiObject("recent_apps").click(),
-                        OVERVIEW_STATE_ORDINAL);
+                        OVERVIEW_STATE_ORDINAL, "clicking Recents button");
                 break;
         }
         expectSwitchToOverviewEvents();
@@ -147,126 +146,100 @@
     private void expectSwitchToOverviewEvents() {
     }
 
-    /**
-     * Swipes right or double presses the square button to switch to the previous app.
-     */
+    @NonNull
     public Background quickSwitchToPreviousApp() {
+        boolean toRight = true;
+        quickSwitch(toRight);
+        return new Background(mLauncher);
+    }
+
+    @NonNull
+    public Background quickSwitchToPreviousAppSwipeLeft() {
+        boolean toRight = false;
+        quickSwitch(toRight);
+        return new Background(mLauncher);
+    }
+
+    @NonNull
+    private void quickSwitch(boolean toRight) {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "want to quick switch to the previous app")) {
             verifyActiveContainer();
-            quickSwitchToPreviousApp(getExpectedStateForQuickSwitch());
-            return new Background(mLauncher);
-        }
-    }
+            final boolean launcherWasVisible = mLauncher.isLauncherVisible();
+            boolean transposeInLandscape = false;
+            switch (mLauncher.getNavigationModel()) {
+                case TWO_BUTTON:
+                    transposeInLandscape = true;
+                    // Fall through, zero button and two button modes behave the same.
+                case ZERO_BUTTON: {
+                    final int startX;
+                    final int startY;
+                    final int endX;
+                    final int endY;
+                    final int cornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
+                    if (toRight) {
+                        if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
+                            // Swipe from the bottom left to the bottom right of the screen.
+                            startX = cornerRadius;
+                            startY = getSwipeStartY();
+                            endX = mLauncher.getDevice().getDisplayWidth() - cornerRadius;
+                            endY = startY;
+                        } else {
+                            // Swipe from the bottom right to the top right of the screen.
+                            startX = getSwipeStartX();
+                            startY = mLauncher.getRealDisplaySize().y - 1 - cornerRadius;
+                            endX = startX;
+                            endY = cornerRadius;
+                        }
+                    } else {
+                        if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
+                            // Swipe from the bottom right to the bottom left of the screen.
+                            startX = mLauncher.getDevice().getDisplayWidth() - cornerRadius;
+                            startY = getSwipeStartY();
+                            endX = cornerRadius;
+                            endY = startY;
+                        } else {
+                            // Swipe from the bottom left to the top left of the screen.
+                            startX = getSwipeStartX();
+                            startY = cornerRadius;
+                            endX = startX;
+                            endY = mLauncher.getRealDisplaySize().y - 1 - cornerRadius;
+                        }
+                    }
 
-    protected int getExpectedStateForQuickSwitch() {
-        return BACKGROUND_APP_STATE_ORDINAL;
-    }
-
-    protected void quickSwitchToPreviousApp(int expectedState) {
-        final boolean launcherWasVisible = mLauncher.isLauncherVisible();
-        boolean transposeInLandscape = false;
-        switch (mLauncher.getNavigationModel()) {
-            case TWO_BUTTON:
-                transposeInLandscape = true;
-                // Fall through, zero button and two button modes behave the same.
-            case ZERO_BUTTON: {
-                final int startX;
-                final int startY;
-                final int endX;
-                final int endY;
-                if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
-                    // Swipe from the bottom left to the bottom right of the screen.
-                    startX = 0;
-                    startY = getSwipeStartY();
-                    endX = mLauncher.getDevice().getDisplayWidth();
-                    endY = startY;
-                } else {
-                    // Swipe from the bottom right to the top right of the screen.
-                    startX = getSwipeStartX();
-                    startY = mLauncher.getRealDisplaySize().y - 1;
-                    endX = startX;
-                    endY = 0;
+                    final boolean isZeroButton = mLauncher.getNavigationModel()
+                            == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
+                    LauncherInstrumentation.GestureScope gestureScope =
+                            launcherWasVisible && isZeroButton
+                                    ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+                                    : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+                    mLauncher.executeAndWaitForEvent(
+                            () -> mLauncher.linearGesture(
+                                    startX, startY, endX, endY, 20, false, gestureScope),
+                            event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                            () -> "Quick switch gesture didn't change window state", "swiping");
+                    break;
                 }
-                final boolean isZeroButton = mLauncher.getNavigationModel()
-                        == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
-                mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
-                        launcherWasVisible && isZeroButton
-                                ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
-                                : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
-                break;
+
+                case THREE_BUTTON:
+                    // Double press the recents button.
+                    UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
+                    mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
+                    mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL,
+                            "clicking Recents button for the first time");
+                    mLauncher.getOverview();
+                    mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
+                    mLauncher.executeAndWaitForEvent(
+                            () -> recentsButton.click(),
+                            event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                            () -> "Pressing recents button didn't change window state",
+                            "clicking Recents button for the second time");
+                    break;
             }
-
-            case THREE_BUTTON:
-                // Double press the recents button.
-                UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
-                mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
-                mLauncher.getOverview();
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
-                recentsButton.click();
-                break;
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
+            return;
         }
-        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
-    }
-    /** Swipes left to switch to the previous app. */
-    public Background quickSwitchToPreviousAppSwipeLeft() {
-        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
-            LauncherInstrumentation.Closable c =
-                mLauncher.addContextLayer("want to quick switch to the previous app")) {
-            verifyActiveContainer();
-            quickSwitchToPreviousAppSwipeLeft(getExpectedStateForQuickSwitch());
-            return new Background(mLauncher);
-        }
-    }
-
-    protected void quickSwitchToPreviousAppSwipeLeft(int expectedState) {
-        final boolean launcherWasVisible = mLauncher.isLauncherVisible();
-        boolean transposeInLandscape = false;
-        switch (mLauncher.getNavigationModel()) {
-            case TWO_BUTTON:
-                transposeInLandscape = true;
-                // Fall through, zero button and two button modes behave the same.
-            case ZERO_BUTTON: {
-                final int startX;
-                final int startY;
-                final int endX;
-                final int endY;
-                if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
-                    // Swipe from the bottom right to the bottom left of the screen.
-                    startX = mLauncher.getDevice().getDisplayWidth();
-                    startY = getSwipeStartY();
-                    endX = 0;
-                    endY = startY;
-                } else {
-                    // Swipe from the bottom right to the top right of the screen.
-                    startX = getSwipeStartX();
-                    startY = mLauncher.getRealDisplaySize().y - 1;
-                    endX = startX;
-                    endY = 0;
-                }
-                final boolean isZeroButton =
-                        mLauncher.getNavigationModel()
-                        == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
-                mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
-                        launcherWasVisible && isZeroButton
-                                ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
-                                : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
-                break;
-            }
-
-            case THREE_BUTTON:
-                // Double press the recents button.
-                UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
-                mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
-                mLauncher.getOverview();
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
-                recentsButton.click();
-                break;
-        }
-        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
     }
 
     protected String getSwipeHeightRequestName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index c06e254..0060844 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.tapl;
 
-import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
-
 import androidx.annotation.NonNull;
 
 /**
@@ -65,8 +63,4 @@
         return true;
     }
 
-    @Override
-    protected int getExpectedStateForQuickSwitch() {
-        return QUICK_SWITCH_STATE_ORDINAL;
-    }
 }
\ 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 13ecfb8..a15131d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -53,19 +53,25 @@
 
     protected abstract void expectActivityStartEvents();
 
+    protected abstract String launchableType();
+
     private Background launch(BySelector selector) {
-        LauncherInstrumentation.log("Launchable.launch before click " +
-                mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+        LauncherInstrumentation.log("Launchable.launch before click "
+                + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+        final String label = mObject.getText();
 
         mLauncher.executeAndWaitForEvent(
-                () -> mLauncher.clickLauncherObject(mObject),
+                () -> {
+                    mLauncher.clickLauncherObject(mObject);
+                    expectActivityStartEvents();
+                },
                 event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
-                () -> "Launching an app didn't open a new window: " + mObject.getText());
-        expectActivityStartEvents();
+                () -> "Launching an app didn't open a new window: " + label,
+                "clicking " + launchableType());
 
         mLauncher.assertTrue(
-                "App didn't start: " + selector,
-                mLauncher.getDevice().wait(Until.hasObject(selector),
+                "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 be3cb4e..19094f8 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -39,6 +39,7 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -52,6 +53,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
@@ -64,6 +66,7 @@
 
 import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.testing.TestProtocol;
+import com.android.systemui.shared.system.ContextUtils;
 import com.android.systemui.shared.system.QuickStepContract;
 
 import org.junit.Assert;
@@ -93,7 +96,6 @@
     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 long START_TIME = System.currentTimeMillis();
 
     private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
     private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
@@ -103,6 +105,9 @@
 
     static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
     static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
+    private final String mLauncherPackage;
+    private Boolean mIsLauncher3;
+    private long mTestStartTime = -1;
 
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
@@ -150,9 +155,9 @@
     private static final String WORKSPACE_RES_ID = "workspace";
     private static final String APPS_RES_ID = "apps_view";
     private static final String OVERVIEW_RES_ID = "overview_panel";
-    private static final String WIDGETS_RES_ID = "widgets_list_view";
-    private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container";
-    public static final int WAIT_TIME_MS = 10000;
+    private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
+    private static final String CONTEXT_MENU_RES_ID = "popup_container";
+    public static final int WAIT_TIME_MS = 60000;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
     private static final String ANDROID_PACKAGE = "android";
 
@@ -173,8 +178,7 @@
     private Runnable mOnLauncherCrashed;
 
     private static Pattern getTouchEventPattern(String prefix, String action) {
-        // The pattern includes sanity checks that we don't get a multi-touch events or other
-        // surprises.
+        // The pattern includes checks that we don't get a multi-touch events or other surprises.
         return Pattern.compile(
                 prefix + ": MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
                         + ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1");
@@ -216,11 +220,11 @@
         // Launcher package. As during inproc tests the tested launcher may not be selected as the
         // current launcher, choosing target package for inproc. For out-of-proc, use the installed
         // launcher package.
-        final String authorityPackage = testPackage.equals(targetPackage) ?
-                getLauncherPackageName() :
-                targetPackage;
+        mLauncherPackage = testPackage.equals(targetPackage)
+                ? getLauncherPackageName()
+                : targetPackage;
 
-        String testProviderAuthority = authorityPackage + ".TestInfo";
+        String testProviderAuthority = mLauncherPackage + ".TestInfo";
         mTestProviderUri = new Uri.Builder()
                 .scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(testProviderAuthority)
@@ -237,11 +241,12 @@
 
         if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
             if (TestHelpers.isInLauncherProcess()) {
-                getContext().getPackageManager().setComponentEnabledSetting(
-                        cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+                pm.setComponentEnabledSetting(cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
             } else {
                 try {
-                    mDevice.executeShellCommand("pm enable " + cn.flattenToString());
+                    final int userId = ContextUtils.getUserId(getContext());
+                    mDevice.executeShellCommand(
+                            "pm enable --user " + userId + " " + cn.flattenToString());
                 } catch (IOException e) {
                     fail(e.toString());
                 }
@@ -265,6 +270,9 @@
         try (ContentProviderClient client = getContext().getContentResolver()
                 .acquireContentProviderClient(mTestProviderUri)) {
             return client.call(request, null, null);
+        } catch (DeadObjectException e) {
+            fail("Launcher crashed");
+            return null;
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -360,9 +368,11 @@
 
             if (hasSystemUiObject("keyguard_status_view")) return "Phone is locked";
 
-            if (!mDevice.hasObject(By.textStartsWith(""))) return "Screen is empty";
+            if (!mDevice.wait(Until.hasObject(getAnyObjectSelector()), WAIT_TIME_MS)) {
+                return "Screen is empty";
+            }
 
-            final String navigationModeError = getNavigationModeMismatchError();
+            final String navigationModeError = getNavigationModeMismatchError(true);
             if (navigationModeError != null) return navigationModeError;
         } catch (Throwable e) {
             Log.w(TAG, "getSystemAnomalyMessage failed", e);
@@ -381,7 +391,7 @@
     }
 
     private String getVisiblePackages() {
-        return mDevice.findObjects(By.textStartsWith(""))
+        return mDevice.findObjects(getAnyObjectSelector())
                 .stream()
                 .map(LauncherInstrumentation::getApplicationPackageSafe)
                 .distinct()
@@ -415,6 +425,14 @@
         mOnSettledStateAction = onSettledStateAction;
     }
 
+    public void onTestStart() {
+        mTestStartTime = System.currentTimeMillis();
+    }
+
+    public void onTestFinish() {
+        mTestStartTime = -1;
+    }
+
     private String formatSystemHealthMessage(String message) {
         final String testPackage = getContext().getPackageName();
 
@@ -423,16 +441,17 @@
         mInstrumentation.getUiAutomation().grantRuntimePermission(
                 testPackage, "android.permission.PACKAGE_USAGE_STATS");
 
-        final String systemHealth = mSystemHealthSupplier != null
-                ? mSystemHealthSupplier.apply(START_TIME)
-                : TestHelpers.getSystemHealthMessage(getContext(), START_TIME);
+        if (mTestStartTime > 0) {
+            final String systemHealth = mSystemHealthSupplier != null
+                    ? mSystemHealthSupplier.apply(mTestStartTime)
+                    : TestHelpers.getSystemHealthMessage(getContext(), mTestStartTime);
 
-        if (systemHealth != null) {
-            return message
-                    + ",\nperhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
-                    + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
+            if (systemHealth != null) {
+                return message
+                        + ";\nPerhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+                        + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
+            }
         }
-
         return message;
     }
 
@@ -443,14 +462,14 @@
             if (checkEvents) {
                 final String eventMismatch = eventChecker.verify(0, false);
                 if (eventMismatch != null) {
-                    message = message + ", having produced " + eventMismatch;
+                    message = message + ";\n" + eventMismatch;
                 }
             } else {
                 eventChecker.finishNoWait();
             }
         }
 
-        dumpDiagnostics();
+        dumpDiagnostics(message);
 
         log("Hierarchy dump for: " + message);
         dumpViewHierarchy();
@@ -458,10 +477,11 @@
         return message;
     }
 
-    private void dumpDiagnostics() {
-        Log.e("b/156287114", "Input:");
+    private void dumpDiagnostics(String message) {
+        log("Diagnostics for failure: " + message);
+        log("Input:");
         logShellCommand("dumpsys input");
-        Log.e("b/156287114", "TIS:");
+        log("TIS:");
         logShellCommand("dumpsys activity service TouchInteractionService");
     }
 
@@ -469,22 +489,24 @@
         try {
             for (String line : mDevice.executeShellCommand(command).split("\\n")) {
                 SystemClock.sleep(10);
-                Log.d("b/156287114", line);
+                log(line);
             }
         } catch (IOException e) {
-            Log.d("b/156287114", "Failed to execute " + command);
+            log("Failed to execute " + command);
         }
     }
 
-    private void fail(String message) {
+    void fail(String message) {
         checkForAnomaly();
         Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
-                "http://go/tapl : " + getContextDescription() + message
-                        + " (visible state: " + getVisibleStateMessage() + ")", true)));
+                "http://go/tapl test failure:\nContext: " + getContextDescription()
+                        + " - visible state is " + getVisibleStateMessage()
+                        + ";\nDetails: " + message, true)));
     }
 
     private String getContextDescription() {
-        return mDiagnosticContext.isEmpty() ? "" : String.join(", ", mDiagnosticContext) + "; ";
+        return mDiagnosticContext.isEmpty()
+                ? "(no context)" : String.join(", ", mDiagnosticContext);
     }
 
     void assertTrue(String message, boolean condition) {
@@ -529,17 +551,28 @@
         mExpectedRotation = expectedRotation;
     }
 
-    public String getNavigationModeMismatchError() {
+    public String getNavigationModeMismatchError(boolean waitForCorrectState) {
+        final int waitTime = waitForCorrectState ? WAIT_TIME_MS : 0;
         final NavigationModel navigationModel = getNavigationModel();
-        final boolean hasRecentsButton = hasSystemUiObject("recent_apps");
-        final boolean hasHomeButton = hasSystemUiObject("home");
-        if ((navigationModel == NavigationModel.THREE_BUTTON) != hasRecentsButton) {
-            return "Presence of recents button doesn't match the interaction mode, mode="
-                    + navigationModel.name() + ", hasRecents=" + hasRecentsButton;
+
+        if (navigationModel == NavigationModel.THREE_BUTTON) {
+            if (!mDevice.wait(Until.hasObject(By.res(SYSTEMUI_PACKAGE, "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)) {
+                return "Recents button is present in non-3-button mode";
+            }
         }
-        if ((navigationModel != NavigationModel.ZERO_BUTTON) != hasHomeButton) {
-            return "Presence of home button doesn't match the interaction mode, mode="
-                    + navigationModel.name() + ", hasHome=" + hasHomeButton;
+
+        if (navigationModel == NavigationModel.ZERO_BUTTON) {
+            if (!mDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "home")), waitTime)) {
+                return "Home button is present in gestural mode";
+            }
+        } else {
+            if (!mDevice.wait(Until.hasObject(By.res(SYSTEMUI_PACKAGE, "home")), waitTime)) {
+                return "Home button not present in non-gestural mode";
+            }
         }
         return null;
     }
@@ -550,13 +583,7 @@
         assertEquals("Unexpected display rotation",
                 mExpectedRotation, mDevice.getDisplayRotation());
 
-        // b/148422894
-        String error = null;
-        for (int i = 0; i != 600; ++i) {
-            error = getNavigationModeMismatchError();
-            if (error == null) break;
-            sleep(100);
-        }
+        final String error = getNavigationModeMismatchError(true);
         assertTrue(error, error == null);
 
         log("verifyContainerType: " + containerType);
@@ -573,11 +600,7 @@
                 "but the current state is not " + containerType.name())) {
             switch (containerType) {
                 case WORKSPACE: {
-                    if (mDevice.isNaturalOrientation()) {
-                        waitForLauncherObject(APPS_RES_ID);
-                    } else {
-                        waitUntilLauncherObjectGone(APPS_RES_ID);
-                    }
+                    waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
                     return waitForLauncherObject(WORKSPACE_RES_ID);
@@ -595,11 +618,7 @@
                     return waitForLauncherObject(APPS_RES_ID);
                 }
                 case OVERVIEW: {
-                    if (hasAllAppsInOverview()) {
-                        waitForLauncherObject(APPS_RES_ID);
-                    } else {
-                        waitUntilLauncherObjectGone(APPS_RES_ID);
-                    }
+                    waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
 
@@ -631,22 +650,35 @@
             }
             SystemClock.sleep(100);
         }
+        checkForAnomaly();
         fail("Launcher didn't initialize");
     }
 
+    Parcelable executeAndWaitForLauncherEvent(Runnable command,
+            UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message,
+            String actionName) {
+        return executeAndWaitForEvent(
+                command,
+                e -> mLauncherPackage.equals(e.getPackageName()) && eventFilter.accept(e),
+                message, actionName);
+    }
+
     Parcelable executeAndWaitForEvent(Runnable command,
-            UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
-        try {
-            final AccessibilityEvent event =
-                    mInstrumentation.getUiAutomation().executeAndWaitForEvent(
-                            command, eventFilter, WAIT_TIME_MS);
-            assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
-            final Parcelable parcelableData = event.getParcelableData();
-            event.recycle();
-            return parcelableData;
-        } catch (TimeoutException e) {
-            fail(message.get());
-            return null;
+            UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message,
+            String actionName) {
+        try (LauncherInstrumentation.Closable c = addContextLayer(actionName)) {
+            try {
+                final AccessibilityEvent event =
+                        mInstrumentation.getUiAutomation().executeAndWaitForEvent(
+                                command, eventFilter, WAIT_TIME_MS);
+                assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
+                final Parcelable parcelableData = event.getParcelableData();
+                event.recycle();
+                return parcelableData;
+            } catch (TimeoutException e) {
+                fail(message.get());
+                return null;
+            }
         }
     }
 
@@ -679,6 +711,10 @@
                     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());
                     }
                 }
                 if (hasLauncherObject(WORKSPACE_RES_ID)) {
@@ -688,33 +724,30 @@
                     dumpViewHierarchy();
                     action = "swiping up to home";
 
-                    try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
-                        swipeToState(
-                                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
-                                        : GestureScope.OUTSIDE_WITH_PILFER);
-                    }
+                    swipeToState(
+                            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
+                                    : GestureScope.OUTSIDE_WITH_PILFER);
                 }
             } else {
                 log("Hierarchy before clicking home:");
                 dumpViewHierarchy();
                 action = "clicking home button";
-                try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
-                    if (!isLauncher3() && getNavigationModel() == NavigationModel.TWO_BUTTON) {
-                        expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
-                        expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
-                    }
-
-                    runToState(
-                            waitForSystemUiObject("home")::click,
-                            NORMAL_STATE_ORDINAL,
-                            !hasLauncherObject(WORKSPACE_RES_ID)
-                                    && (hasLauncherObject(APPS_RES_ID)
-                                    || hasLauncherObject(OVERVIEW_RES_ID)));
+                if (!isLauncher3() && getNavigationModel() == NavigationModel.TWO_BUTTON) {
+                    expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+                    expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
                 }
+
+                runToState(
+                        waitForSystemUiObject("home")::click,
+                        NORMAL_STATE_ORDINAL,
+                        !hasLauncherObject(WORKSPACE_RES_ID)
+                                && (hasLauncherObject(APPS_RES_ID)
+                                || hasLauncherObject(OVERVIEW_RES_ID)),
+                        action);
             }
             try (LauncherInstrumentation.Closable c = addContextLayer(
                     "performed action to switch to Home - " + action)) {
@@ -723,9 +756,13 @@
         }
     }
 
+    private static BySelector getAnyObjectSelector() {
+        return By.textStartsWith("");
+    }
+
     boolean isLauncherVisible() {
         mDevice.waitForIdle();
-        return hasLauncherObject(By.textStartsWith(""));
+        return hasLauncherObject(getAnyObjectSelector());
     }
 
     /**
@@ -855,6 +892,16 @@
         return object;
     }
 
+    @Nullable
+    UiObject2 findObjectInContainer(UiObject2 container, BySelector selector) {
+        try {
+            return container.findObject(selector);
+        } catch (StaleObjectException e) {
+            fail("The container disappeared from screen");
+            return null;
+        }
+    }
+
     @NonNull
     List<UiObject2> getObjectsInContainer(UiObject2 container, String resName) {
         try {
@@ -880,6 +927,16 @@
         }
     }
 
+    void waitForObjectEnabled(UiObject2 object, String waitReason) {
+        try {
+            assertTrue("Timed out waiting for object to be enabled for " + waitReason + " "
+                    + object.getResourceName(),
+                    object.wait(Until.enabled(true), WAIT_TIME_MS));
+        } catch (StaleObjectException e) {
+            fail("The object disappeared from screen");
+        }
+    }
+
     @NonNull
     UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) {
         try {
@@ -929,7 +986,7 @@
 
     @NonNull
     UiObject2 waitForAndroidObject(String resId) {
-        final UiObject2 object = mDevice.wait(
+        final UiObject2 object = TestHelpers.wait(
                 Until.findObject(By.res(ANDROID_PACKAGE, resId)), WAIT_TIME_MS);
         assertNotNull("Can't find a android object with id: " + resId, object);
         return object;
@@ -976,22 +1033,23 @@
                 + "]";
     }
 
-    void runToState(Runnable command, int expectedState, boolean requireEvent) {
+    void runToState(Runnable command, int expectedState, boolean requireEvent, String actionName) {
         if (requireEvent) {
-            runToState(command, expectedState);
+            runToState(command, expectedState, actionName);
         } else {
             command.run();
         }
     }
 
-    void runToState(Runnable command, int expectedState) {
+    void runToState(Runnable command, int expectedState, String actionName) {
         final List<Integer> actualEvents = new ArrayList<>();
-        executeAndWaitForEvent(
+        executeAndWaitForLauncherEvent(
                 command,
                 event -> isSwitchToStateEvent(event, expectedState, actualEvents),
                 () -> "Failed to receive an event for the state change: expected ["
                         + TestProtocol.stateOrdinalToString(expectedState)
-                        + "], actual: " + eventListToString(actualEvents));
+                        + "], actual: " + eventListToString(actualEvents),
+                        actionName);
     }
 
     private boolean isSwitchToStateEvent(
@@ -1008,20 +1066,26 @@
             GestureScope gestureScope) {
         runToState(
                 () -> linearGesture(startX, startY, endX, endY, steps, false, gestureScope),
-                expectedState);
+                expectedState,
+                "swiping");
     }
 
-    int getBottomGestureSize() {
+    private int getBottomGestureSize() {
         return ResourceUtils.getNavbarSize(
                 ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources()) + 1;
     }
 
     int getBottomGestureMarginInContainer(UiObject2 container) {
-        final int bottomGestureStartOnScreen = getRealDisplaySize().y - getBottomGestureSize();
+        final int bottomGestureStartOnScreen = getBottomGestureStartOnScreen();
         return getVisibleBounds(container).bottom - bottomGestureStartOnScreen;
     }
 
+    int getBottomGestureStartOnScreen() {
+        return getRealDisplaySize().y - getBottomGestureSize();
+    }
+
     void clickLauncherObject(UiObject2 object) {
+        waitForObjectEnabled(object, "clickLauncherObject");
         expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
         expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
         if (!isLauncher3() && getNavigationModel() != NavigationModel.THREE_BUTTON) {
@@ -1043,6 +1107,11 @@
         final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer;
         final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop();
 
+        scrollDownByDistance(container, distance);
+    }
+
+    void scrollDownByDistance(UiObject2 container, int distance) {
+        final Rect containerRect = getVisibleBounds(container);
         final int bottomGestureMarginInContainer = getBottomGestureMarginInContainer(container);
         scroll(
                 container,
@@ -1101,12 +1170,13 @@
                 return;
         }
 
-        executeAndWaitForEvent(
+        executeAndWaitForLauncherEvent(
                 () -> linearGesture(
                         startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE),
                 event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
                 () -> "Didn't receive a scroll end message: " + startX + ", " + startY
-                        + ", " + endX + ", " + endY);
+                        + ", " + endX + ", " + endY,
+                        "scrolling");
     }
 
     // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
@@ -1197,8 +1267,15 @@
         }
 
         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));
+                mInstrumentation.getUiAutomation().injectInputEvent(event, true, false));
         event.recycle();
     }
 
@@ -1289,30 +1366,13 @@
         getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
     }
 
-    public boolean hasAllAppsInOverview() {
-        // Vertical bar layouts don't contain all apps
-        if (!mDevice.isNaturalOrientation()) {
-            return false;
-        }
-        // Portrait two button (quickstep) always has all apps.
-        if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
-            return true;
-        }
-        // Overview actions hide all apps
-        if (overviewActionsEnabled()) {
-            return false;
-        }
-        // ...otherwise there should be all apps
-        return true;
-    }
-
-    private boolean overviewActionsEnabled() {
-        return getTestInfo(TestProtocol.REQUEST_OVERVIEW_ACTIONS_ENABLED).getBoolean(
+    boolean overviewShareEnabled() {
+        return getTestInfo(TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED).getBoolean(
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
-    boolean overviewShareEnabled() {
-        return getTestInfo(TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED).getBoolean(
+    boolean overviewContentPushEnabled() {
+        return getTestInfo(TestProtocol.REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED).getBoolean(
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
@@ -1324,9 +1384,10 @@
         getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
     }
 
-    public int getTotalPssKb() {
-        return getTestInfo(TestProtocol.REQUEST_TOTAL_PSS_KB).
-                getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    public void forceGc() {
+        // GC the system & sysui first before gc'ing launcher
+        logShellCommand("cmd statusbar run-gc");
+        getTestInfo(TestProtocol.REQUEST_FORCE_GC);
     }
 
     public Integer getPid() {
@@ -1334,14 +1395,6 @@
         return testInfo != null ? testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) : null;
     }
 
-    public void produceJavaLeak() {
-        getTestInfo(TestProtocol.REQUEST_JAVA_LEAK);
-    }
-
-    public void produceNativeLeak() {
-        getTestInfo(TestProtocol.REQUEST_NATIVE_LEAK);
-    }
-
     public void produceViewLeak() {
         getTestInfo(TestProtocol.REQUEST_VIEW_LEAK);
     }
@@ -1381,7 +1434,7 @@
                 if (mCheckEventsForSuccessfulGestures) {
                     final String message = eventChecker.verify(WAIT_TIME_MS, true);
                     if (message != null) {
-                        dumpDiagnostics();
+                        dumpDiagnostics(message);
                         checkForAnomaly();
                         Assert.fail(formatSystemHealthMessage(
                                 "http://go/tapl : successful gesture produced " + message));
@@ -1394,7 +1447,10 @@
     }
 
     boolean isLauncher3() {
-        return "com.android.launcher3".equals(getLauncherPackageName());
+        if (mIsLauncher3 == null) {
+            mIsLauncher3 = "com.android.launcher3".equals(getLauncherPackageName());
+        }
+        return mIsLauncher3;
     }
 
     void expectEvent(String sequence, Pattern expected) {
@@ -1408,9 +1464,41 @@
     Rect getVisibleBounds(UiObject2 object) {
         try {
             return object.getVisibleBounds();
+        } catch (StaleObjectException e) {
+            fail("Object " + object + " disappeared from screen");
+            return null;
         } catch (Throwable t) {
             fail(t.toString());
             return null;
         }
     }
-}
+
+    float getWindowCornerRadius() {
+        final Resources resources = getResources();
+        if (!supportsRoundedCornersOnWindows(resources)) {
+            return 0f;
+        }
+
+        // Radius that should be used in case top or bottom aren't defined.
+        float defaultRadius = ResourceUtils.getDimenByName("rounded_corner_radius", resources, 0);
+
+        float topRadius = ResourceUtils.getDimenByName("rounded_corner_radius_top", resources, 0);
+        if (topRadius == 0f) {
+            topRadius = defaultRadius;
+        }
+        float bottomRadius = ResourceUtils.getDimenByName(
+                "rounded_corner_radius_bottom", resources, 0);
+        if (bottomRadius == 0f) {
+            bottomRadius = defaultRadius;
+        }
+
+        // Always use the smallest radius to make sure the rounded corners will
+        // completely cover the display.
+        return Math.min(topRadius, bottomRadius);
+    }
+
+    private static boolean supportsRoundedCornersOnWindows(Resources resources) {
+        return ResourceUtils.getBoolByName(
+                "config_supportsRoundedCornersOnWindows", resources, false);
+    }
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 4440b82..710e3cd 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -15,6 +15,10 @@
  */
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.testing.TestProtocol.SEQUENCE_MAIN;
+import static com.android.launcher3.testing.TestProtocol.SEQUENCE_PILFER;
+import static com.android.launcher3.testing.TestProtocol.SEQUENCE_TIS;
+
 import android.os.SystemClock;
 
 import com.android.launcher3.testing.TestProtocol;
@@ -57,6 +61,8 @@
         while (true) {
             rawEvents = mLauncher.getTestInfo(TestProtocol.REQUEST_GET_TEST_EVENTS)
                     .getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+            if (rawEvents == null) return null;
+
             final int expectedCount = mExpectedEvents.entrySet()
                     .stream().mapToInt(e -> e.getValue().size()).sum();
             if (rawEvents.size() >= expectedCount
@@ -83,7 +89,26 @@
 
     String verify(long waitForExpectedCountMs, boolean successfulGesture) {
         final ListMap<String> actualEvents = finishSync(waitForExpectedCountMs);
+        if (actualEvents == null) return "null event sequences because launcher likely died";
 
+        final String lowLevelDiags = lowLevelMismatchDiagnostics(actualEvents);
+        // If we have a sequence mismatch for a successful gesture, we want to provide all low-level
+        // details.
+        if (successfulGesture) {
+            return lowLevelDiags;
+        }
+
+        final String sequenceMismatchInEnglish = highLevelMismatchDiagnostics(actualEvents);
+
+        if (sequenceMismatchInEnglish != null) {
+            LauncherInstrumentation.log(lowLevelDiags);
+            return "Hint: " + sequenceMismatchInEnglish;
+        } else {
+            return lowLevelDiags;
+        }
+    }
+
+    private String lowLevelMismatchDiagnostics(ListMap<String> actualEvents) {
         final StringBuilder sb = new StringBuilder();
         boolean hasMismatches = false;
         for (Map.Entry<String, List<Pattern>> expectedEvents : mExpectedEvents.entrySet()) {
@@ -91,8 +116,7 @@
 
             List<String> actual = new ArrayList<>(actualEvents.getNonNull(sequence));
             final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
-            hasMismatches = hasMismatches
-                    || mismatchPosition != -1 && !ignoreMistatch(successfulGesture, sequence);
+            hasMismatches = hasMismatches || mismatchPosition != -1;
             formatSequenceWithMismatch(
                     sb,
                     sequence,
@@ -103,8 +127,7 @@
         // Check for unexpected event sequences in the actual data.
         for (String actualNamedSequence : actualEvents.keySet()) {
             if (!mExpectedEvents.containsKey(actualNamedSequence)) {
-                hasMismatches = hasMismatches
-                        || !ignoreMistatch(successfulGesture, actualNamedSequence);
+                hasMismatches = true;
                 formatSequenceWithMismatch(
                         sb,
                         actualNamedSequence,
@@ -114,14 +137,43 @@
             }
         }
 
-        return hasMismatches ? "mismatching events: " + sb.toString() : null;
+        return hasMismatches ? "Mismatching events: " + sb.toString() : null;
     }
 
-    // Workaround for b/154157191
-    private static boolean ignoreMistatch(boolean successfulGesture, String sequence) {
-        // b/156287114
-        return false;
-//        return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
+    private String highLevelMismatchDiagnostics(ListMap<String> actualEvents) {
+        if (!mExpectedEvents.getNonNull(SEQUENCE_TIS).isEmpty()
+                && actualEvents.getNonNull(SEQUENCE_TIS).isEmpty()) {
+            return "TouchInteractionService didn't receive any of the touch events sent by the "
+                    + "test";
+        }
+        if (getMismatchPosition(mExpectedEvents.getNonNull(SEQUENCE_TIS),
+                actualEvents.getNonNull(SEQUENCE_TIS)) != -1) {
+            // If TIS has a mismatch that we can't convert to high-level diags, don't convert
+            // other sequences either.
+            return null;
+        }
+
+        if (mExpectedEvents.getNonNull(SEQUENCE_PILFER).size() == 1
+                && actualEvents.getNonNull(SEQUENCE_PILFER).isEmpty()) {
+            return "Launcher didn't detect the navigation gesture sent by the test";
+        }
+        if (mExpectedEvents.getNonNull(SEQUENCE_PILFER).isEmpty()
+                && actualEvents.getNonNull(SEQUENCE_PILFER).size() == 1) {
+            return "Launcher detected a navigation gesture, but the test didn't send one";
+        }
+        if (getMismatchPosition(mExpectedEvents.getNonNull(SEQUENCE_PILFER),
+                actualEvents.getNonNull(SEQUENCE_PILFER)) != -1) {
+            // If Pilfer has a mismatch that we can't convert to high-level diags, don't analyze
+            // other sequences.
+            return null;
+        }
+
+        if (!mExpectedEvents.getNonNull(SEQUENCE_MAIN).isEmpty()
+                && actualEvents.getNonNull(SEQUENCE_MAIN).isEmpty()) {
+            return "None of the touch or keyboard events sent by the test was received by "
+                    + "Launcher's main thread";
+        }
+        return null;
     }
 
     // If the list of actual events matches the list of expected events, returns -1, otherwise
diff --git a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java
index 282fca9..787dc70 100644
--- a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenu.java
@@ -26,7 +26,7 @@
 
     OptionsPopupMenu(LauncherInstrumentation launcher) {
         mLauncher = launcher;
-        mDeepShortcutsContainer = launcher.waitForLauncherObject("deep_shortcuts_container");
+        mDeepShortcutsContainer = launcher.waitForLauncherObject("popup_container");
     }
 
     /**
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index a30a404..950c052 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -34,6 +34,27 @@
     }
 
     /**
+     * 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
@@ -43,6 +64,7 @@
                      "want to click screenshot button and exit screenshot ui")) {
             UiObject2 screenshot = mLauncher.waitForObjectInContainer(mOverviewActions,
                     "action_screenshot");
+
             mLauncher.clickLauncherObject(screenshot);
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
                     "clicked screenshot button")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index b235919..657b74d 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -71,15 +71,13 @@
     public Background open() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             verifyActiveContainer();
-            try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
-                    "clicking an overview task")) {
-                mLauncher.executeAndWaitForEvent(
-                        () -> mLauncher.clickLauncherObject(mTask),
-                        event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
-                        () -> "Launching task didn't open a new window: "
-                                + mTask.getParent().getContentDescription());
-                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
-            }
+            mLauncher.executeAndWaitForEvent(
+                    () -> mLauncher.clickLauncherObject(mTask),
+                    event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+                    () -> "Launching task didn't open a new window: "
+                            + mTask.getParent().getContentDescription(),
+                    "clicking an overview task");
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
             return new Background(mLauncher);
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
index 3507418..e1b73a4 100644
--- a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
+++ b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
@@ -48,21 +48,4 @@
             }
         }
     }
-
-    /**
-     * Click feedback button.
-     */
-    @NonNull
-    public Background clickFeedback() {
-        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
-             LauncherInstrumentation.Closable c =
-                     mLauncher.addContextLayer("want to click feedback button")) {
-            UiObject2 feedback = mLauncher.waitForObjectInContainer(mSelectModeButtons, "feedback");
-            mLauncher.clickLauncherObject(feedback);
-            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                    "clicked feedback button")) {
-                return new Background(mLauncher);
-            }
-        }
-    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index b8791e8..7f6062f 100644
--- a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -27,6 +27,11 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.os.DropBoxManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.uiautomator.SearchCondition;
+import androidx.test.uiautomator.UiDevice;
 
 import org.junit.Assert;
 
@@ -35,6 +40,7 @@
 
 public class TestHelpers {
 
+    private static final String TAG = "Tapl";
     private static Boolean sIsInLauncherProcess;
 
     public static boolean isInLauncherProcess() {
@@ -154,4 +160,12 @@
             return null;
         }
     }
+
+    public static <R> R wait(SearchCondition<R> condition, long timeout) {
+        Log.d(TAG,
+                "TestHelpers.wait, condition=" + timeout + ", time=" + SystemClock.uptimeMillis());
+        final R result = UiDevice.getInstance(getInstrumentation()).wait(condition, timeout);
+        Log.d(TAG, "TestHelpers.wait, result=" + result + ", time=" + SystemClock.uptimeMillis());
+        return result;
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 53ef796..3520318 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -46,4 +46,9 @@
     protected void addExpectedEventsForLongClick() {
         mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
     }
+
+    @Override
+    protected String launchableType() {
+        return "widget";
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 39ac645..99d9889 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -17,8 +17,8 @@
 package com.android.launcher3.tapl;
 
 import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
+import static com.android.launcher3.tapl.LauncherInstrumentation.log;
 
-import android.graphics.Point;
 import android.graphics.Rect;
 
 import androidx.test.uiautomator.By;
@@ -27,7 +27,7 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
-import com.android.launcher3.tapl.LauncherInstrumentation.GestureScope;
+import com.android.launcher3.testing.TestProtocol;
 
 import java.util.Collection;
 
@@ -36,6 +36,7 @@
  */
 public final class Widgets extends LauncherInstrumentation.VisibleContainer {
     private static final int FLING_STEPS = 10;
+    private static final int SCROLL_ATTEMPTS = 60;
 
     Widgets(LauncherInstrumentation launcher) {
         super(launcher);
@@ -49,7 +50,7 @@
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "want to fling forward in widgets")) {
-            LauncherInstrumentation.log("Widgets.flingForward enter");
+            log("Widgets.flingForward enter");
             final UiObject2 widgetsContainer = verifyActiveContainer();
             mLauncher.scroll(
                     widgetsContainer,
@@ -60,7 +61,7 @@
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
                 verifyActiveContainer();
             }
-            LauncherInstrumentation.log("Widgets.flingForward exit");
+            log("Widgets.flingForward exit");
         }
     }
 
@@ -71,7 +72,7 @@
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "want to fling backwards in widgets")) {
-            LauncherInstrumentation.log("Widgets.flingBackward enter");
+            log("Widgets.flingBackward enter");
             final UiObject2 widgetsContainer = verifyActiveContainer();
             mLauncher.scroll(
                     widgetsContainer,
@@ -81,7 +82,7 @@
             try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
                 verifyActiveContainer();
             }
-            LauncherInstrumentation.log("Widgets.flingBackward exit");
+            log("Widgets.flingBackward exit");
         }
     }
 
@@ -90,54 +91,123 @@
         return LauncherInstrumentation.ContainerType.WIDGETS;
     }
 
+    private int getWidgetsScroll() {
+        return mLauncher.getTestInfo(
+                TestProtocol.REQUEST_WIDGETS_SCROLL_Y)
+                .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     public Widget getWidget(String labelText) {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "getting widget " + labelText + " in widgets list")) {
-            final UiObject2 widgetsContainer = verifyActiveContainer();
+            final UiObject2 searchBar = findSearchBar();
+            final int searchBarHeight = searchBar.getVisibleBounds().height();
+            final UiObject2 fullWidgetsPicker = verifyActiveContainer();
             mLauncher.assertTrue("Widgets container didn't become scrollable",
-                    widgetsContainer.wait(Until.scrollable(true), WAIT_TIME_MS));
-            final Point displaySize = mLauncher.getRealDisplaySize();
-            final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
+                    fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS));
 
+            final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer();
+            mLauncher.assertTrue("Can't locate widgets list for the test app: "
+                            + mLauncher.getLauncherPackageName(),
+                    widgetsContainer != null);
+            final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
+            final BySelector previewSelector = By.res(mLauncher.getLauncherPackageName(),
+                    "widget_preview");
             int i = 0;
             for (; ; ) {
-                final Collection<UiObject2> cells = mLauncher.getObjectsInContainer(
-                        widgetsContainer, "widgets_scroll_container");
-                mLauncher.assertTrue("Widgets doesn't have 2 rows", cells.size() >= 2);
-                for (UiObject2 cell : cells) {
-                    final UiObject2 label = cell.findObject(labelSelector);
-                    if (label == null) continue;
-
-                    final UiObject2 widget = label.getParent().getParent();
-                    mLauncher.assertEquals(
-                            "View is not WidgetCell",
-                            "com.android.launcher3.widget.WidgetCell",
-                            widget.getClassName());
-
-                    int maxWidth = 0;
-                    for (UiObject2 sibling : widget.getParent().getChildren()) {
-                        maxWidth = Math.max(mLauncher.getVisibleBounds(sibling).width(), maxWidth);
-                    }
-
-                    if (mLauncher.getVisibleBounds(widget).bottom
-                            <= displaySize.y - mLauncher.getBottomGestureSize()) {
-                        int visibleDelta = maxWidth - mLauncher.getVisibleBounds(widget).width();
-                        if (visibleDelta > 0) {
-                            Rect parentBounds = mLauncher.getVisibleBounds(cell);
-                            mLauncher.linearGesture(parentBounds.centerX() + visibleDelta
-                                            + mLauncher.getTouchSlop(),
-                                    parentBounds.centerY(), parentBounds.centerX(),
-                                    parentBounds.centerY(), 10, true, GestureScope.INSIDE);
+                final Collection<UiObject2> tableRows = widgetsContainer.getChildren();
+                for (UiObject2 row : tableRows) {
+                    final Collection<UiObject2> widgetCells = row.getChildren();
+                    for (UiObject2 widget : widgetCells) {
+                        final UiObject2 label = mLauncher.findObjectInContainer(widget,
+                                labelSelector);
+                        if (label == null) {
+                            continue;
                         }
-
-                        return new Widget(mLauncher, widget);
+                        mLauncher.assertEquals(
+                                "View is not WidgetCell",
+                                "com.android.launcher3.widget.WidgetCell",
+                                widget.getClassName());
+                        UiObject2 preview = mLauncher.waitForObjectInContainer(widget,
+                                previewSelector);
+                        return new Widget(mLauncher, preview);
                     }
                 }
 
-                mLauncher.assertTrue("Too many attempts", ++i <= 40);
-                mLauncher.scrollToLastVisibleRow(widgetsContainer, cells, 0);
+                mLauncher.assertTrue("Too many attempts", ++i <= SCROLL_ATTEMPTS);
+                final int scroll = getWidgetsScroll();
+                mLauncher.scrollDownByDistance(fullWidgetsPicker, searchBarHeight);
+                final int newScroll = getWidgetsScroll();
+                mLauncher.assertTrue(
+                        "Scrolled in a wrong direction in Widgets: from " + scroll + " to "
+                                + newScroll, newScroll >= scroll);
+                mLauncher.assertTrue("Unable to scroll to the widget", newScroll != scroll);
             }
         }
     }
+
+    private UiObject2 findSearchBar() {
+        final BySelector searchBarContainerSelector = By.res(mLauncher.getLauncherPackageName(),
+                "search_and_recommendations_container");
+        final BySelector searchBarSelector = By.res(mLauncher.getLauncherPackageName(),
+                "widgets_search_bar");
+        final UiObject2 searchBarContainer = mLauncher.waitForLauncherObject(
+                searchBarContainerSelector);
+        UiObject2 searchBar = mLauncher.waitForObjectInContainer(searchBarContainer,
+                searchBarSelector);
+        return searchBar;
+    }
+
+    /** Finds the widgets list of this test app from the collapsed full widgets picker. */
+    private UiObject2 findTestAppWidgetsTableContainer() {
+        final BySelector headerSelector = By.res(mLauncher.getLauncherPackageName(),
+                "widgets_list_header");
+        final BySelector targetAppSelector = By.clazz("android.widget.TextView").text(
+                mLauncher.getContext().getPackageName());
+        final BySelector widgetsContainerSelector = By.res(mLauncher.getLauncherPackageName(),
+                "widgets_table");
+
+        boolean hasHeaderExpanded = false;
+        int scrollDistance = 0;
+        for (int i = 0; i < SCROLL_ATTEMPTS; i++) {
+            UiObject2 fullWidgetsPicker = verifyActiveContainer();
+
+            UiObject2 header = mLauncher.waitForObjectInContainer(fullWidgetsPicker,
+                    headerSelector);
+            // If a header is barely visible in the bottom edge of the screen, its height could be
+            // too small for a scroll gesture. Since all header should have roughly the same height,
+            // let's pick the max height we have seen so far.
+            scrollDistance = Math.max(scrollDistance, header.getVisibleBounds().height());
+
+            // Look for a header that has the test app name.
+            UiObject2 headerTitle = mLauncher.findObjectInContainer(fullWidgetsPicker,
+                    targetAppSelector);
+            if (headerTitle != null) {
+                // If we find the header and it has not been expanded, let's click it to see the
+                // widgets list. Note that we wait until the header is out of the gesture region at
+                // the bottom of the screen, because tapping there in Launcher3 causes NexusLauncher
+                // to briefly appear to handle the gesture, which can break our test.
+                boolean isHeaderOutOfGestureRegion = headerTitle.getVisibleCenter().y
+                        < mLauncher.getBottomGestureStartOnScreen();
+                if (!hasHeaderExpanded && isHeaderOutOfGestureRegion) {
+                    log("Header has not been expanded. Click to expand.");
+                    hasHeaderExpanded = true;
+                    mLauncher.clickLauncherObject(headerTitle);
+                }
+
+                // Look for a widgets list.
+                UiObject2 widgetsContainer = mLauncher.findObjectInContainer(fullWidgetsPicker,
+                        widgetsContainerSelector);
+                if (widgetsContainer != null) {
+                    log("Widgets container found.");
+                    return widgetsContainer;
+                }
+            }
+            log("Finding test widget package - scroll with distance: " + scrollDistance);
+            mLauncher.scrollDownByDistance(fullWidgetsPicker, scrollDistance);
+        }
+
+        return null;
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index f0e686f..43134d9 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -22,7 +22,6 @@
 
 import static junit.framework.TestCase.assertTrue;
 
-import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.SystemClock;
@@ -61,34 +60,6 @@
         mHotseat = launcher.waitForLauncherObject("hotseat");
     }
 
-    private static boolean supportsRoundedCornersOnWindows(Resources resources) {
-        return ResourceUtils.getBoolByName(
-                "config_supportsRoundedCornersOnWindows", resources, false);
-    }
-
-    private static float getWindowCornerRadius(Resources resources) {
-        if (!supportsRoundedCornersOnWindows(resources)) {
-            return 0f;
-        }
-
-        // Radius that should be used in case top or bottom aren't defined.
-        float defaultRadius = ResourceUtils.getDimenByName("rounded_corner_radius", resources, 0);
-
-        float topRadius = ResourceUtils.getDimenByName("rounded_corner_radius_top", resources, 0);
-        if (topRadius == 0f) {
-            topRadius = defaultRadius;
-        }
-        float bottomRadius = ResourceUtils.getDimenByName(
-                "rounded_corner_radius_bottom", resources, 0);
-        if (bottomRadius == 0f) {
-            bottomRadius = defaultRadius;
-        }
-
-        // Always use the smallest radius to make sure the rounded corners will
-        // completely cover the display.
-        return Math.min(topRadius, bottomRadius);
-    }
-
     /**
      * Swipes up to All Apps.
      *
@@ -103,14 +74,14 @@
             final int deviceHeight = mLauncher.getDevice().getDisplayHeight();
             final int bottomGestureMargin = ResourceUtils.getNavbarSize(
                     ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources());
-            final int windowCornerRadius = (int) Math.ceil(getWindowCornerRadius(
-                    mLauncher.getResources()));
+            final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
             final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1;
             final int swipeHeight = mLauncher.getTestInfo(
                     TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
                     getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
             LauncherInstrumentation.log(
-                    "switchToAllApps: swipeHeight = " + swipeHeight + ", slop = "
+                    "switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY
+                            + ", swipeHeight = " + swipeHeight + ", slop = "
                             + mLauncher.getTouchSlop());
 
             mLauncher.swipeToState(
@@ -178,7 +149,7 @@
                             getHotseatAppIcon("Chrome"),
                             new Point(mLauncher.getDevice().getDisplayWidth(),
                                     mLauncher.getVisibleBounds(workspace).centerY()),
-                            "deep_shortcuts_container",
+                            "popup_container",
                             false,
                             false,
                             () -> mLauncher.expectEvent(
@@ -219,13 +190,15 @@
                     launcher.movePointer(launchableCenter, dest, 10, downTime, true,
                             LauncherInstrumentation.GestureScope.INSIDE);
                 },
-                SPRING_LOADED_STATE_ORDINAL);
+                SPRING_LOADED_STATE_ORDINAL,
+                "long-pressing and moving");
         LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
         launcher.runToState(
                 () -> launcher.sendPointer(
                         downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest,
                         LauncherInstrumentation.GestureScope.INSIDE),
-                NORMAL_STATE_ORDINAL);
+                NORMAL_STATE_ORDINAL,
+                "sending UP event");
         if (startsActivity || isWidgetShortcut) {
             launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START);
         }